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: null,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * Bootstrap Body class
852  *
853  * @constructor
854  * Create a new body
855  * @param {Object} config The config object
856  */
857
858 Roo.bootstrap.Body = function(config){
859
860     config = config || {};
861
862     Roo.bootstrap.Body.superclass.constructor.call(this, config);
863     this.el = Roo.get(config.el ? config.el : document.body );
864     if (this.cls && this.cls.length) {
865         Roo.get(document.body).addClass(this.cls);
866     }
867 };
868
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
870
871     is_body : true,// just to make sure it's constructed?
872
873         autoCreate : {
874         cls: 'container'
875     },
876     onRender : function(ct, position)
877     {
878        /* Roo.log("Roo.bootstrap.Body - onRender");
879         if (this.cls && this.cls.length) {
880             Roo.get(document.body).addClass(this.cls);
881         }
882         // style??? xttr???
883         */
884     }
885
886
887
888
889 });
890 /*
891  * - LGPL
892  *
893  * button group
894  * 
895  */
896
897
898 /**
899  * @class Roo.bootstrap.ButtonGroup
900  * @extends Roo.bootstrap.Component
901  * Bootstrap ButtonGroup class
902  * @cfg {String} size lg | sm | xs (default empty normal)
903  * @cfg {String} align vertical | justified  (default none)
904  * @cfg {String} direction up | down (default down)
905  * @cfg {Boolean} toolbar false | true
906  * @cfg {Boolean} btn true | false
907  * 
908  * 
909  * @constructor
910  * Create a new Input
911  * @param {Object} config The config object
912  */
913
914 Roo.bootstrap.ButtonGroup = function(config){
915     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
916 };
917
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
919     
920     size: '',
921     align: '',
922     direction: '',
923     toolbar: false,
924     btn: true,
925
926     getAutoCreate : function(){
927         var cfg = {
928             cls: 'btn-group',
929             html : null
930         };
931         
932         cfg.html = this.html || cfg.html;
933         
934         if (this.toolbar) {
935             cfg = {
936                 cls: 'btn-toolbar',
937                 html: null
938             };
939             
940             return cfg;
941         }
942         
943         if (['vertical','justified'].indexOf(this.align)!==-1) {
944             cfg.cls = 'btn-group-' + this.align;
945             
946             if (this.align == 'justified') {
947                 console.log(this.items);
948             }
949         }
950         
951         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952             cfg.cls += ' btn-group-' + this.size;
953         }
954         
955         if (this.direction == 'up') {
956             cfg.cls += ' dropup' ;
957         }
958         
959         return cfg;
960     },
961     /**
962      * Add a button to the group (similar to NavItem API.)
963      */
964     addItem : function(cfg)
965     {
966         var cn = new Roo.bootstrap.Button(cfg);
967         //this.register(cn);
968         cn.parentId = this.id;
969         cn.onRender(this.el, null);
970         return cn;
971     }
972    
973 });
974
975  /*
976  * - LGPL
977  *
978  * button
979  * 
980  */
981
982 /**
983  * @class Roo.bootstrap.Button
984  * @extends Roo.bootstrap.Component
985  * Bootstrap Button class
986  * @cfg {String} html The button content
987  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990  * @cfg {String} size (lg|sm|xs)
991  * @cfg {String} tag (a|input|submit)
992  * @cfg {String} href empty or href
993  * @cfg {Boolean} disabled default false;
994  * @cfg {Boolean} isClose default false;
995  * @cfg {String} glyphicon depricated - use fa
996  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997  * @cfg {String} badge text for badge
998  * @cfg {String} theme (default|glow)  
999  * @cfg {Boolean} inverse dark themed version
1000  * @cfg {Boolean} toggle is it a slidy toggle button
1001  * @cfg {Boolean} pressed   default null - if the button ahs active state
1002  * @cfg {String} ontext text for on slidy toggle state
1003  * @cfg {String} offtext text for off slidy toggle state
1004  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1005  * @cfg {Boolean} removeClass remove the standard class..
1006  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1007  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1008  * 
1009  * @constructor
1010  * Create a new button
1011  * @param {Object} config The config object
1012  */
1013
1014
1015 Roo.bootstrap.Button = function(config){
1016     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020         /**
1021          * @event click
1022          * When a button is pressed
1023          * @param {Roo.bootstrap.Button} btn
1024          * @param {Roo.EventObject} e
1025          */
1026         "click" : true,
1027         /**
1028          * @event dblclick
1029          * When a button is double clicked
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "dblclick" : true,
1034          /**
1035          * @event toggle
1036          * After the button has been toggles
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          * @param {boolean} pressed (also available as button.pressed)
1040          */
1041         "toggle" : true
1042     });
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1046     html: false,
1047     active: false,
1048     weight: '',
1049     badge_weight: '',
1050     outline : false,
1051     size: '',
1052     tag: 'button',
1053     href: '',
1054     disabled: false,
1055     isClose: false,
1056     glyphicon: '',
1057     fa: '',
1058     badge: '',
1059     theme: 'default',
1060     inverse: false,
1061     
1062     toggle: false,
1063     ontext: 'ON',
1064     offtext: 'OFF',
1065     defaulton: true,
1066     preventDefault: true,
1067     removeClass: false,
1068     name: false,
1069     target: false,
1070     group : false,
1071      
1072     pressed : null,
1073      
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : 'button',
1079             cls : 'roo-button',
1080             html: ''
1081         };
1082         
1083         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085             this.tag = 'button';
1086         } else {
1087             cfg.tag = this.tag;
1088         }
1089         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090         
1091         if (this.toggle == true) {
1092             cfg={
1093                 tag: 'div',
1094                 cls: 'slider-frame roo-button',
1095                 cn: [
1096                     {
1097                         tag: 'span',
1098                         'data-on-text':'ON',
1099                         'data-off-text':'OFF',
1100                         cls: 'slider-button',
1101                         html: this.offtext
1102                     }
1103                 ]
1104             };
1105             // why are we validating the weights?
1106             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107                 cfg.cls +=  ' ' + this.weight;
1108             }
1109             
1110             return cfg;
1111         }
1112         
1113         if (this.isClose) {
1114             cfg.cls += ' close';
1115             
1116             cfg["aria-hidden"] = true;
1117             
1118             cfg.html = "&times;";
1119             
1120             return cfg;
1121         }
1122              
1123         
1124         if (this.theme==='default') {
1125             cfg.cls = 'btn roo-button';
1126             
1127             //if (this.parentType != 'Navbar') {
1128             this.weight = this.weight.length ?  this.weight : 'default';
1129             //}
1130             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131                 
1132                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134                 cfg.cls += ' btn-' + outline + weight;
1135                 if (this.weight == 'default') {
1136                     // BC
1137                     cfg.cls += ' btn-' + this.weight;
1138                 }
1139             }
1140         } else if (this.theme==='glow') {
1141             
1142             cfg.tag = 'a';
1143             cfg.cls = 'btn-glow roo-button';
1144             
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 cfg.cls += ' ' + this.weight;
1148             }
1149         }
1150    
1151         
1152         if (this.inverse) {
1153             this.cls += ' inverse';
1154         }
1155         
1156         
1157         if (this.active || this.pressed === true) {
1158             cfg.cls += ' active';
1159         }
1160         
1161         if (this.disabled) {
1162             cfg.disabled = 'disabled';
1163         }
1164         
1165         if (this.items) {
1166             Roo.log('changing to ul' );
1167             cfg.tag = 'ul';
1168             this.glyphicon = 'caret';
1169             if (Roo.bootstrap.version == 4) {
1170                 this.fa = 'caret-down';
1171             }
1172             
1173         }
1174         
1175         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176          
1177         //gsRoo.log(this.parentType);
1178         if (this.parentType === 'Navbar' && !this.parent().bar) {
1179             Roo.log('changing to li?');
1180             
1181             cfg.tag = 'li';
1182             
1183             cfg.cls = '';
1184             cfg.cn =  [{
1185                 tag : 'a',
1186                 cls : 'roo-button',
1187                 html : this.html,
1188                 href : this.href || '#'
1189             }];
1190             if (this.menu) {
1191                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1192                 cfg.cls += ' dropdown';
1193             }   
1194             
1195             delete cfg.html;
1196             
1197         }
1198         
1199        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1200         
1201         if (this.glyphicon) {
1202             cfg.html = ' ' + cfg.html;
1203             
1204             cfg.cn = [
1205                 {
1206                     tag: 'span',
1207                     cls: 'glyphicon glyphicon-' + this.glyphicon
1208                 }
1209             ];
1210         }
1211         if (this.fa) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'i',
1217                     cls: 'fa fas fa-' + this.fa
1218                 }
1219             ];
1220         }
1221         
1222         if (this.badge) {
1223             cfg.html += ' ';
1224             
1225             cfg.tag = 'a';
1226             
1227 //            cfg.cls='btn roo-button';
1228             
1229             cfg.href=this.href;
1230             
1231             var value = cfg.html;
1232             
1233             if(this.glyphicon){
1234                 value = {
1235                     tag: 'span',
1236                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1237                     html: this.html
1238                 };
1239             }
1240             if(this.fa){
1241                 value = {
1242                     tag: 'i',
1243                     cls: 'fa fas fa-' + this.fa,
1244                     html: this.html
1245                 };
1246             }
1247             
1248             var bw = this.badge_weight.length ? this.badge_weight :
1249                 (this.weight.length ? this.weight : 'secondary');
1250             bw = bw == 'default' ? 'secondary' : bw;
1251             
1252             cfg.cn = [
1253                 value,
1254                 {
1255                     tag: 'span',
1256                     cls: 'badge badge-' + bw,
1257                     html: this.badge
1258                 }
1259             ];
1260             
1261             cfg.html='';
1262         }
1263         
1264         if (this.menu) {
1265             cfg.cls += ' dropdown';
1266             cfg.html = typeof(cfg.html) != 'undefined' ?
1267                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1268         }
1269         
1270         if (cfg.tag !== 'a' && this.href !== '') {
1271             throw "Tag must be a to set href.";
1272         } else if (this.href.length > 0) {
1273             cfg.href = this.href;
1274         }
1275         
1276         if(this.removeClass){
1277             cfg.cls = '';
1278         }
1279         
1280         if(this.target){
1281             cfg.target = this.target;
1282         }
1283         
1284         return cfg;
1285     },
1286     initEvents: function() {
1287        // Roo.log('init events?');
1288 //        Roo.log(this.el.dom);
1289         // add the menu...
1290         
1291         if (typeof (this.menu) != 'undefined') {
1292             this.menu.parentType = this.xtype;
1293             this.menu.triggerEl = this.el;
1294             this.addxtype(Roo.apply({}, this.menu));
1295         }
1296
1297
1298         if (this.el.hasClass('roo-button')) {
1299              this.el.on('click', this.onClick, this);
1300              this.el.on('dblclick', this.onDblClick, this);
1301         } else {
1302              this.el.select('.roo-button').on('click', this.onClick, this);
1303              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1304              
1305         }
1306         // why?
1307         if(this.removeClass){
1308             this.el.on('click', this.onClick, this);
1309         }
1310         
1311         if (this.group === true) {
1312              if (this.pressed === false || this.pressed === true) {
1313                 // nothing
1314             } else {
1315                 this.pressed = false;
1316                 this.setActive(this.pressed);
1317             }
1318             
1319         }
1320         
1321         this.el.enableDisplayMode();
1322         
1323     },
1324     onClick : function(e)
1325     {
1326         if (this.disabled) {
1327             return;
1328         }
1329         
1330         Roo.log('button on click ');
1331         if(this.preventDefault){
1332             e.preventDefault();
1333         }
1334         
1335         if (this.group) {
1336             if (this.pressed) {
1337                 // do nothing -
1338                 return;
1339             }
1340             this.setActive(true);
1341             var pi = this.parent().items;
1342             for (var i = 0;i < pi.length;i++) {
1343                 if (this == pi[i]) {
1344                     continue;
1345                 }
1346                 if (pi[i].el.hasClass('roo-button')) {
1347                     pi[i].setActive(false);
1348                 }
1349             }
1350             this.fireEvent('click', this, e);            
1351             return;
1352         }
1353         
1354         if (this.pressed === true || this.pressed === false) {
1355             this.toggleActive(e);
1356         }
1357         
1358         
1359         this.fireEvent('click', this, e);
1360     },
1361     onDblClick: function(e)
1362     {
1363         if (this.disabled) {
1364             return;
1365         }
1366         if(this.preventDefault){
1367             e.preventDefault();
1368         }
1369         this.fireEvent('dblclick', this, e);
1370     },
1371     /**
1372      * Enables this button
1373      */
1374     enable : function()
1375     {
1376         this.disabled = false;
1377         this.el.removeClass('disabled');
1378         this.el.dom.removeAttribute("disabled");
1379     },
1380     
1381     /**
1382      * Disable this button
1383      */
1384     disable : function()
1385     {
1386         this.disabled = true;
1387         this.el.addClass('disabled');
1388         this.el.attr("disabled", "disabled")
1389     },
1390      /**
1391      * sets the active state on/off, 
1392      * @param {Boolean} state (optional) Force a particular state
1393      */
1394     setActive : function(v) {
1395         
1396         this.el[v ? 'addClass' : 'removeClass']('active');
1397         this.pressed = v;
1398     },
1399      /**
1400      * toggles the current active state 
1401      */
1402     toggleActive : function(e)
1403     {
1404         this.setActive(!this.pressed); // this modifies pressed...
1405         this.fireEvent('toggle', this, e, this.pressed);
1406     },
1407      /**
1408      * get the current active state
1409      * @return {boolean} true if it's active
1410      */
1411     isActive : function()
1412     {
1413         return this.el.hasClass('active');
1414     },
1415     /**
1416      * set the text of the first selected button
1417      */
1418     setText : function(str)
1419     {
1420         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1421     },
1422     /**
1423      * get the text of the first selected button
1424      */
1425     getText : function()
1426     {
1427         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1428     },
1429     
1430     setWeight : function(str)
1431     {
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434         this.weight = str;
1435         var outline = this.outline ? 'outline-' : '';
1436         if (str == 'default') {
1437             this.el.addClass('btn-default btn-outline-secondary');        
1438             return;
1439         }
1440         this.el.addClass('btn-' + outline + str);        
1441     }
1442     
1443     
1444 });
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446
1447 Roo.bootstrap.Button.weights = [
1448     'default',
1449     'secondary' ,
1450     'primary',
1451     'success',
1452     'info',
1453     'warning',
1454     'danger',
1455     'link',
1456     'light',
1457     'dark'              
1458    
1459 ];/*
1460  * - LGPL
1461  *
1462  * column
1463  * 
1464  */
1465
1466 /**
1467  * @class Roo.bootstrap.Column
1468  * @extends Roo.bootstrap.Component
1469  * Bootstrap Column class
1470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1478  *
1479  * 
1480  * @cfg {Boolean} hidden (true|false) hide the element
1481  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482  * @cfg {String} fa (ban|check|...) font awesome icon
1483  * @cfg {Number} fasize (1|2|....) font awsome size
1484
1485  * @cfg {String} icon (info-sign|check|...) glyphicon name
1486
1487  * @cfg {String} html content of column.
1488  * 
1489  * @constructor
1490  * Create a new Column
1491  * @param {Object} config The config object
1492  */
1493
1494 Roo.bootstrap.Column = function(config){
1495     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1496 };
1497
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1499     
1500     xs: false,
1501     sm: false,
1502     md: false,
1503     lg: false,
1504     xsoff: false,
1505     smoff: false,
1506     mdoff: false,
1507     lgoff: false,
1508     html: '',
1509     offset: 0,
1510     alert: false,
1511     fa: false,
1512     icon : false,
1513     hidden : false,
1514     fasize : 1,
1515     
1516     getAutoCreate : function(){
1517         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1518         
1519         cfg = {
1520             tag: 'div',
1521             cls: 'column'
1522         };
1523         
1524         var settings=this;
1525         var sizes =   ['xs','sm','md','lg'];
1526         sizes.map(function(size ,ix){
1527             //Roo.log( size + ':' + settings[size]);
1528             
1529             if (settings[size+'off'] !== false) {
1530                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1531             }
1532             
1533             if (settings[size] === false) {
1534                 return;
1535             }
1536             
1537             if (!settings[size]) { // 0 = hidden
1538                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539                 // bootsrap4
1540                 for (var i = ix; i > -1; i--) {
1541                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1542                 }
1543                 
1544                 
1545                 return;
1546             }
1547             cfg.cls += ' col-' + size + '-' + settings[size] + (
1548                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1549             );
1550             
1551         });
1552         
1553         if (this.hidden) {
1554             cfg.cls += ' hidden';
1555         }
1556         
1557         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558             cfg.cls +=' alert alert-' + this.alert;
1559         }
1560         
1561         
1562         if (this.html.length) {
1563             cfg.html = this.html;
1564         }
1565         if (this.fa) {
1566             var fasize = '';
1567             if (this.fasize > 1) {
1568                 fasize = ' fa-' + this.fasize + 'x';
1569             }
1570             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1571             
1572             
1573         }
1574         if (this.icon) {
1575             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1576         }
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * page container.
1589  * 
1590  */
1591
1592
1593 /**
1594  * @class Roo.bootstrap.Container
1595  * @extends Roo.bootstrap.Component
1596  * Bootstrap Container class
1597  * @cfg {Boolean} jumbotron is it a jumbotron element
1598  * @cfg {String} html content of element
1599  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1601  * @cfg {String} header content of header (for panel)
1602  * @cfg {String} footer content of footer (for panel)
1603  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604  * @cfg {String} tag (header|aside|section) type of HTML tag.
1605  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606  * @cfg {String} fa font awesome icon
1607  * @cfg {String} icon (info-sign|check|...) glyphicon name
1608  * @cfg {Boolean} hidden (true|false) hide the element
1609  * @cfg {Boolean} expandable (true|false) default false
1610  * @cfg {Boolean} expanded (true|false) default true
1611  * @cfg {String} rheader contet on the right of header
1612  * @cfg {Boolean} clickable (true|false) default false
1613
1614  *     
1615  * @constructor
1616  * Create a new Container
1617  * @param {Object} config The config object
1618  */
1619
1620 Roo.bootstrap.Container = function(config){
1621     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1622     
1623     this.addEvents({
1624         // raw events
1625          /**
1626          * @event expand
1627          * After the panel has been expand
1628          * 
1629          * @param {Roo.bootstrap.Container} this
1630          */
1631         "expand" : true,
1632         /**
1633          * @event collapse
1634          * After the panel has been collapsed
1635          * 
1636          * @param {Roo.bootstrap.Container} this
1637          */
1638         "collapse" : true,
1639         /**
1640          * @event click
1641          * When a element is chick
1642          * @param {Roo.bootstrap.Container} this
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1650     
1651     jumbotron : false,
1652     well: '',
1653     panel : '',
1654     header: '',
1655     footer : '',
1656     sticky: '',
1657     tag : false,
1658     alert : false,
1659     fa: false,
1660     icon : false,
1661     expandable : false,
1662     rheader : '',
1663     expanded : true,
1664     clickable: false,
1665   
1666      
1667     getChildContainer : function() {
1668         
1669         if(!this.el){
1670             return false;
1671         }
1672         
1673         if (this.panel.length) {
1674             return this.el.select('.panel-body',true).first();
1675         }
1676         
1677         return this.el;
1678     },
1679     
1680     
1681     getAutoCreate : function(){
1682         
1683         var cfg = {
1684             tag : this.tag || 'div',
1685             html : '',
1686             cls : ''
1687         };
1688         if (this.jumbotron) {
1689             cfg.cls = 'jumbotron';
1690         }
1691         
1692         
1693         
1694         // - this is applied by the parent..
1695         //if (this.cls) {
1696         //    cfg.cls = this.cls + '';
1697         //}
1698         
1699         if (this.sticky.length) {
1700             
1701             var bd = Roo.get(document.body);
1702             if (!bd.hasClass('bootstrap-sticky')) {
1703                 bd.addClass('bootstrap-sticky');
1704                 Roo.select('html',true).setStyle('height', '100%');
1705             }
1706              
1707             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1708         }
1709         
1710         
1711         if (this.well.length) {
1712             switch (this.well) {
1713                 case 'lg':
1714                 case 'sm':
1715                     cfg.cls +=' well well-' +this.well;
1716                     break;
1717                 default:
1718                     cfg.cls +=' well';
1719                     break;
1720             }
1721         }
1722         
1723         if (this.hidden) {
1724             cfg.cls += ' hidden';
1725         }
1726         
1727         
1728         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729             cfg.cls +=' alert alert-' + this.alert;
1730         }
1731         
1732         var body = cfg;
1733         
1734         if (this.panel.length) {
1735             cfg.cls += ' panel panel-' + this.panel;
1736             cfg.cn = [];
1737             if (this.header.length) {
1738                 
1739                 var h = [];
1740                 
1741                 if(this.expandable){
1742                     
1743                     cfg.cls = cfg.cls + ' expandable';
1744                     
1745                     h.push({
1746                         tag: 'i',
1747                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1748                     });
1749                     
1750                 }
1751                 
1752                 h.push(
1753                     {
1754                         tag: 'span',
1755                         cls : 'panel-title',
1756                         html : (this.expandable ? '&nbsp;' : '') + this.header
1757                     },
1758                     {
1759                         tag: 'span',
1760                         cls: 'panel-header-right',
1761                         html: this.rheader
1762                     }
1763                 );
1764                 
1765                 cfg.cn.push({
1766                     cls : 'panel-heading',
1767                     style : this.expandable ? 'cursor: pointer' : '',
1768                     cn : h
1769                 });
1770                 
1771             }
1772             
1773             body = false;
1774             cfg.cn.push({
1775                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1776                 html : this.html
1777             });
1778             
1779             
1780             if (this.footer.length) {
1781                 cfg.cn.push({
1782                     cls : 'panel-footer',
1783                     html : this.footer
1784                     
1785                 });
1786             }
1787             
1788         }
1789         
1790         if (body) {
1791             body.html = this.html || cfg.html;
1792             // prefix with the icons..
1793             if (this.fa) {
1794                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1795             }
1796             if (this.icon) {
1797                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1798             }
1799             
1800             
1801         }
1802         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803             cfg.cls =  'container';
1804         }
1805         
1806         return cfg;
1807     },
1808     
1809     initEvents: function() 
1810     {
1811         if(this.expandable){
1812             var headerEl = this.headerEl();
1813         
1814             if(headerEl){
1815                 headerEl.on('click', this.onToggleClick, this);
1816             }
1817         }
1818         
1819         if(this.clickable){
1820             this.el.on('click', this.onClick, this);
1821         }
1822         
1823     },
1824     
1825     onToggleClick : function()
1826     {
1827         var headerEl = this.headerEl();
1828         
1829         if(!headerEl){
1830             return;
1831         }
1832         
1833         if(this.expanded){
1834             this.collapse();
1835             return;
1836         }
1837         
1838         this.expand();
1839     },
1840     
1841     expand : function()
1842     {
1843         if(this.fireEvent('expand', this)) {
1844             
1845             this.expanded = true;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848             
1849             this.el.select('.panel-body',true).first().removeClass('hide');
1850             
1851             var toggleEl = this.toggleEl();
1852
1853             if(!toggleEl){
1854                 return;
1855             }
1856
1857             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1858         }
1859         
1860     },
1861     
1862     collapse : function()
1863     {
1864         if(this.fireEvent('collapse', this)) {
1865             
1866             this.expanded = false;
1867             
1868             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869             this.el.select('.panel-body',true).first().addClass('hide');
1870         
1871             var toggleEl = this.toggleEl();
1872
1873             if(!toggleEl){
1874                 return;
1875             }
1876
1877             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1878         }
1879     },
1880     
1881     toggleEl : function()
1882     {
1883         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1884             return;
1885         }
1886         
1887         return this.el.select('.panel-heading .fa',true).first();
1888     },
1889     
1890     headerEl : function()
1891     {
1892         if(!this.el || !this.panel.length || !this.header.length){
1893             return;
1894         }
1895         
1896         return this.el.select('.panel-heading',true).first()
1897     },
1898     
1899     bodyEl : function()
1900     {
1901         if(!this.el || !this.panel.length){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-body',true).first()
1906     },
1907     
1908     titleEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-title',true).first();
1915     },
1916     
1917     setTitle : function(v)
1918     {
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return;
1923         }
1924         
1925         titleEl.dom.innerHTML = v;
1926     },
1927     
1928     getTitle : function()
1929     {
1930         
1931         var titleEl = this.titleEl();
1932         
1933         if(!titleEl){
1934             return '';
1935         }
1936         
1937         return titleEl.dom.innerHTML;
1938     },
1939     
1940     setRightTitle : function(v)
1941     {
1942         var t = this.el.select('.panel-header-right',true).first();
1943         
1944         if(!t){
1945             return;
1946         }
1947         
1948         t.dom.innerHTML = v;
1949     },
1950     
1951     onClick : function(e)
1952     {
1953         e.preventDefault();
1954         
1955         this.fireEvent('click', this, e);
1956     }
1957 });
1958
1959  /*
1960  *  - LGPL
1961  *
1962  *  This is BS4's Card element.. - similar to our containers probably..
1963  * 
1964  */
1965 /**
1966  * @class Roo.bootstrap.Card
1967  * @extends Roo.bootstrap.Component
1968  * Bootstrap Card class
1969  *
1970  *
1971  * possible... may not be implemented..
1972  * @cfg {String} header_image  src url of image.
1973  * @cfg {String|Object} header
1974  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1976  * 
1977  * @cfg {String} title
1978  * @cfg {String} subtitle
1979  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980  * @cfg {String} footer
1981  
1982  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983  * 
1984  * @cfg {String} margin (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991  *
1992  * @cfg {String} padding (0|1|2|3|4|5)
1993  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995  * @cfg {String} padding_left (0|1|2|3|4|5)
1996  * @cfg {String} padding_right (0|1|2|3|4|5)
1997  * @cfg {String} padding_x (0|1|2|3|4|5)
1998  * @cfg {String} padding_y (0|1|2|3|4|5)
1999  *
2000  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  
2006  * @config {Boolean} dragable  if this card can be dragged.
2007  * @config {String} drag_group  group for drag
2008  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2009  * @config {String} drop_group  group for drag
2010  * 
2011  * @config {Boolean} collapsable can the body be collapsed.
2012  * @config {Boolean} collapsed is the body collapsed when rendered...
2013  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014  * @config {Boolean} rotated is the body rotated when rendered...
2015  * 
2016  * @constructor
2017  * Create a new Container
2018  * @param {Object} config The config object
2019  */
2020
2021 Roo.bootstrap.Card = function(config){
2022     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2023     
2024     this.addEvents({
2025          // raw events
2026         /**
2027          * @event drop
2028          * When a element a card is dropped
2029          * @param {Roo.bootstrap.Card} this
2030          *
2031          * 
2032          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033          * @param {String} position 'above' or 'below'
2034          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2035         
2036          */
2037         'drop' : true,
2038          /**
2039          * @event rotate
2040          * When a element a card is rotate
2041          * @param {Roo.bootstrap.Card} this
2042          * @param {Roo.Element} n the node being dropped?
2043          * @param {Boolean} rotate status
2044          */
2045         'rotate' : true,
2046         /**
2047          * @event cardover
2048          * When a card element is dragged over ready to drop (return false to block dropable)
2049          * @param {Roo.bootstrap.Card} this
2050          * @param {Object} data from dragdrop 
2051          */
2052          'cardover' : true
2053          
2054     });
2055 };
2056
2057
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2059     
2060     
2061     weight : '',
2062     
2063     margin: '', /// may be better in component?
2064     margin_top: '', 
2065     margin_bottom: '', 
2066     margin_left: '',
2067     margin_right: '',
2068     margin_x: '',
2069     margin_y: '',
2070     
2071     padding : '',
2072     padding_top: '', 
2073     padding_bottom: '', 
2074     padding_left: '',
2075     padding_right: '',
2076     padding_x: '',
2077     padding_y: '',
2078     
2079     display: '', 
2080     display_xs: '', 
2081     display_sm: '', 
2082     display_lg: '',
2083     display_xl: '',
2084  
2085     header_image  : '',
2086     header : '',
2087     header_size : 0,
2088     title : '',
2089     subtitle : '',
2090     html : '',
2091     footer: '',
2092
2093     collapsable : false,
2094     collapsed : false,
2095     rotateable : false,
2096     rotated : false,
2097     
2098     dragable : false,
2099     drag_group : false,
2100     dropable : false,
2101     drop_group : false,
2102     childContainer : false,
2103     dropEl : false, /// the dom placeholde element that indicates drop location.
2104     containerEl: false, // body container
2105     bodyEl: false, // card-body
2106     headerContainerEl : false, //
2107     headerEl : false,
2108     header_imageEl : false,
2109     
2110     
2111     layoutCls : function()
2112     {
2113         var cls = '';
2114         var t = this;
2115         Roo.log(this.margin_bottom.length);
2116         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118             
2119             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2121             }
2122             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2124             }
2125         });
2126         
2127         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2130             }
2131         });
2132         
2133         // more generic support?
2134         if (this.hidden) {
2135             cls += ' d-none';
2136         }
2137         
2138         return cls;
2139     },
2140  
2141        // Roo.log("Call onRender: " + this.xtype);
2142         /*  We are looking at something like this.
2143 <div class="card">
2144     <img src="..." class="card-img-top" alt="...">
2145     <div class="card-body">
2146         <h5 class="card-title">Card title</h5>
2147          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148
2149         >> this bit is really the body...
2150         <div> << we will ad dthis in hopefully it will not break shit.
2151         
2152         ** card text does not actually have any styling...
2153         
2154             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2155         
2156         </div> <<
2157           <a href="#" class="card-link">Card link</a>
2158           
2159     </div>
2160     <div class="card-footer">
2161         <small class="text-muted">Last updated 3 mins ago</small>
2162     </div>
2163 </div>
2164          */
2165     getAutoCreate : function(){
2166         
2167         var cfg = {
2168             tag : 'div',
2169             cls : 'card',
2170             cn : [ ]
2171         };
2172         
2173         if (this.weight.length && this.weight != 'light') {
2174             cfg.cls += ' text-white';
2175         } else {
2176             cfg.cls += ' text-dark'; // need as it's nested..
2177         }
2178         if (this.weight.length) {
2179             cfg.cls += ' bg-' + this.weight;
2180         }
2181         
2182         cfg.cls += ' ' + this.layoutCls(); 
2183         
2184         var hdr = false;
2185         var hdr_ctr = false;
2186         if (this.header.length) {
2187             hdr = {
2188                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190                 cn : []
2191             };
2192             cfg.cn.push(hdr);
2193             hdr_ctr = hdr;
2194         } else {
2195             hdr = {
2196                 tag : 'div',
2197                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198                 cn : []
2199             };
2200             cfg.cn.push(hdr);
2201             hdr_ctr = hdr;
2202         }
2203         if (this.collapsable) {
2204             hdr_ctr = {
2205             tag : 'a',
2206             cls : 'd-block user-select-none',
2207             cn: [
2208                     {
2209                         tag: 'i',
2210                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2211                     }
2212                    
2213                 ]
2214             };
2215             hdr.cn.push(hdr_ctr);
2216         }
2217         
2218         hdr_ctr.cn.push(        {
2219             tag: 'span',
2220             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2221             html : this.header
2222         });
2223         
2224         
2225         if (this.header_image.length) {
2226             cfg.cn.push({
2227                 tag : 'img',
2228                 cls : 'card-img-top',
2229                 src: this.header_image // escape?
2230             });
2231         } else {
2232             cfg.cn.push({
2233                     tag : 'div',
2234                     cls : 'card-img-top d-none' 
2235                 });
2236         }
2237             
2238         var body = {
2239             tag : 'div',
2240             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2241             cn : []
2242         };
2243         var obody = body;
2244         if (this.collapsable || this.rotateable) {
2245             obody = {
2246                 tag: 'div',
2247                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248                 cn : [  body ]
2249             };
2250         }
2251         
2252         cfg.cn.push(obody);
2253         
2254         if (this.title.length) {
2255             body.cn.push({
2256                 tag : 'div',
2257                 cls : 'card-title',
2258                 src: this.title // escape?
2259             });
2260         }  
2261         
2262         if (this.subtitle.length) {
2263             body.cn.push({
2264                 tag : 'div',
2265                 cls : 'card-title',
2266                 src: this.subtitle // escape?
2267             });
2268         }
2269         
2270         body.cn.push({
2271             tag : 'div',
2272             cls : 'roo-card-body-ctr'
2273         });
2274         
2275         if (this.html.length) {
2276             body.cn.push({
2277                 tag: 'div',
2278                 html : this.html
2279             });
2280         }
2281         // fixme ? handle objects?
2282         
2283         if (this.footer.length) {
2284            
2285             cfg.cn.push({
2286                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2287                 html : this.footer
2288             });
2289             
2290         } else {
2291             cfg.cn.push({cls : 'card-footer d-none'});
2292         }
2293         
2294         // footer...
2295         
2296         return cfg;
2297     },
2298     
2299     
2300     getCardHeader : function()
2301     {
2302         var  ret = this.el.select('.card-header',true).first();
2303         if (ret.hasClass('d-none')) {
2304             ret.removeClass('d-none');
2305         }
2306         
2307         return ret;
2308     },
2309     getCardFooter : function()
2310     {
2311         var  ret = this.el.select('.card-footer',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardImageTop : function()
2319     {
2320         var  ret = this.header_imageEl;
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324             
2325         return ret;
2326     },
2327     
2328     getChildContainer : function()
2329     {
2330         
2331         if(!this.el){
2332             return false;
2333         }
2334         return this.el.select('.roo-card-body-ctr',true).first();    
2335     },
2336     
2337     initEvents: function() 
2338     {
2339         this.bodyEl = this.el.select('.card-body',true).first(); 
2340         this.containerEl = this.getChildContainer();
2341         if(this.dragable){
2342             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343                     containerScroll: true,
2344                     ddGroup: this.drag_group || 'default_card_drag_group'
2345             });
2346             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347         }
2348         if (this.dropable) {
2349             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350                 containerScroll: true,
2351                 ddGroup: this.drop_group || 'default_card_drag_group'
2352             });
2353             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2358         }
2359         
2360         if (this.collapsable) {
2361             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362         }
2363         if (this.rotateable) {
2364             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365         }
2366         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367          
2368         this.footerEl = this.el.select('.card-footer',true).first();
2369         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371         this.headerEl = this.el.select('.card-header',true).first();
2372         
2373         if (this.rotated) {
2374             this.el.addClass('roo-card-rotated');
2375             this.fireEvent('rotate', this, true);
2376         }
2377         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2378         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2379         
2380     },
2381     getDragData : function(e)
2382     {
2383         var target = this.getEl();
2384         if (target) {
2385             //this.handleSelection(e);
2386             
2387             var dragData = {
2388                 source: this,
2389                 copy: false,
2390                 nodes: this.getEl(),
2391                 records: []
2392             };
2393             
2394             
2395             dragData.ddel = target.dom ;    // the div element
2396             Roo.log(target.getWidth( ));
2397             dragData.ddel.style.width = target.getWidth() + 'px';
2398             
2399             return dragData;
2400         }
2401         return false;
2402     },
2403     /**
2404     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2405     *    whole Element becomes the target, and this causes the drop gesture to append.
2406     *
2407     *    Returns an object:
2408     *     {
2409            
2410            position : 'below' or 'above'
2411            card  : relateive to card OBJECT (or true for no cards listed)
2412            items_n : relative to nth item in list
2413            card_n : relative to  nth card in list
2414     }
2415     *
2416     *    
2417     */
2418     getTargetFromEvent : function(e, dragged_card_el)
2419     {
2420         var target = e.getTarget();
2421         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422             target = target.parentNode;
2423         }
2424         
2425         var ret = {
2426             position: '',
2427             cards : [],
2428             card_n : -1,
2429             items_n : -1,
2430             card : false 
2431         };
2432         
2433         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434         // see if target is one of the 'cards'...
2435         
2436         
2437         //Roo.log(this.items.length);
2438         var pos = false;
2439         
2440         var last_card_n = 0;
2441         var cards_len  = 0;
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             pos = this.getDropPoint(e, this.items[i].el.dom);
2448             
2449             cards_len = ret.cards.length;
2450             //Roo.log(this.items[i].el.dom.id);
2451             ret.cards.push(this.items[i]);
2452             last_card_n  = i;
2453             if (ret.card_n < 0 && pos == 'above') {
2454                 ret.position = cards_len > 0 ? 'below' : pos;
2455                 ret.items_n = i > 0 ? i - 1 : 0;
2456                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2457                 ret.card = ret.cards[ret.card_n];
2458             }
2459         }
2460         if (!ret.cards.length) {
2461             ret.card = true;
2462             ret.position = 'below';
2463             ret.items_n;
2464             return ret;
2465         }
2466         // could not find a card.. stick it at the end..
2467         if (ret.card_n < 0) {
2468             ret.card_n = last_card_n;
2469             ret.card = ret.cards[last_card_n];
2470             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471             ret.position = 'below';
2472         }
2473         
2474         if (this.items[ret.items_n].el == dragged_card_el) {
2475             return false;
2476         }
2477         
2478         if (ret.position == 'below') {
2479             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480             
2481             if (card_after  && card_after.el == dragged_card_el) {
2482                 return false;
2483             }
2484             return ret;
2485         }
2486         
2487         // its's after ..
2488         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489         
2490         if (card_before  && card_before.el == dragged_card_el) {
2491             return false;
2492         }
2493         
2494         return ret;
2495     },
2496     
2497     onNodeEnter : function(n, dd, e, data){
2498         return false;
2499     },
2500     onNodeOver : function(n, dd, e, data)
2501     {
2502        
2503         var target_info = this.getTargetFromEvent(e,data.source.el);
2504         if (target_info === false) {
2505             this.dropPlaceHolder('hide');
2506             return false;
2507         }
2508         Roo.log(['getTargetFromEvent', target_info ]);
2509         
2510         
2511         if (this.fireEvent('cardover', this, [ data ]) === false) {
2512             return false;
2513         }
2514         
2515         this.dropPlaceHolder('show', target_info,data);
2516         
2517         return false; 
2518     },
2519     onNodeOut : function(n, dd, e, data){
2520         this.dropPlaceHolder('hide');
2521      
2522     },
2523     onNodeDrop : function(n, dd, e, data)
2524     {
2525         
2526         // call drop - return false if
2527         
2528         // this could actually fail - if the Network drops..
2529         // we will ignore this at present..- client should probably reload
2530         // the whole set of cards if stuff like that fails.
2531         
2532         
2533         var info = this.getTargetFromEvent(e,data.source.el);
2534         if (info === false) {
2535             return false;
2536         }
2537         this.dropPlaceHolder('hide');
2538   
2539           
2540     
2541         this.acceptCard(data.source, info.position, info.card, info.items_n);
2542         return true;
2543          
2544     },
2545     firstChildCard : function()
2546     {
2547         for (var i = 0;i< this.items.length;i++) {
2548             
2549             if (!this.items[i].el.hasClass('card')) {
2550                  continue;
2551             }
2552             return this.items[i];
2553         }
2554         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2555     },
2556     /**
2557      * accept card
2558      *
2559      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2560      */
2561     acceptCard : function(move_card,  position, next_to_card )
2562     {
2563         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2564             return false;
2565         }
2566         
2567         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568         
2569         move_card.parent().removeCard(move_card);
2570         
2571         
2572         var dom = move_card.el.dom;
2573         dom.style.width = ''; // clear with - which is set by drag.
2574         
2575         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576             var cardel = next_to_card.el.dom;
2577             
2578             if (position == 'above' ) {
2579                 cardel.parentNode.insertBefore(dom, cardel);
2580             } else if (cardel.nextSibling) {
2581                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582             } else {
2583                 cardel.parentNode.append(dom);
2584             }
2585         } else {
2586             // card container???
2587             this.containerEl.dom.append(dom);
2588         }
2589         
2590         //FIXME HANDLE card = true 
2591         
2592         // add this to the correct place in items.
2593         
2594         // remove Card from items.
2595         
2596        
2597         if (this.items.length) {
2598             var nitems = [];
2599             //Roo.log([info.items_n, info.position, this.items.length]);
2600             for (var i =0; i < this.items.length; i++) {
2601                 if (i == to_items_n && position == 'above') {
2602                     nitems.push(move_card);
2603                 }
2604                 nitems.push(this.items[i]);
2605                 if (i == to_items_n && position == 'below') {
2606                     nitems.push(move_card);
2607                 }
2608             }
2609             this.items = nitems;
2610             Roo.log(this.items);
2611         } else {
2612             this.items.push(move_card);
2613         }
2614         
2615         move_card.parentId = this.id;
2616         
2617         return true;
2618         
2619         
2620     },
2621     removeCard : function(c)
2622     {
2623         this.items = this.items.filter(function(e) { return e != c });
2624  
2625         var dom = c.el.dom;
2626         dom.parentNode.removeChild(dom);
2627         dom.style.width = ''; // clear with - which is set by drag.
2628         c.parentId = false;
2629         
2630     },
2631     
2632     /**    Decide whether to drop above or below a View node. */
2633     getDropPoint : function(e, n, dd)
2634     {
2635         if (dd) {
2636              return false;
2637         }
2638         if (n == this.containerEl.dom) {
2639             return "above";
2640         }
2641         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642         var c = t + (b - t) / 2;
2643         var y = Roo.lib.Event.getPageY(e);
2644         if(y <= c) {
2645             return "above";
2646         }else{
2647             return "below";
2648         }
2649     },
2650     onToggleCollapse : function(e)
2651         {
2652         if (this.collapsed) {
2653             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654             this.collapsableEl.addClass('show');
2655             this.collapsed = false;
2656             return;
2657         }
2658         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659         this.collapsableEl.removeClass('show');
2660         this.collapsed = true;
2661         
2662     
2663     },
2664     
2665     onToggleRotate : function(e)
2666     {
2667         this.collapsableEl.removeClass('show');
2668         this.footerEl.removeClass('d-none');
2669         this.el.removeClass('roo-card-rotated');
2670         this.el.removeClass('d-none');
2671         if (this.rotated) {
2672             
2673             this.collapsableEl.addClass('show');
2674             this.rotated = false;
2675             this.fireEvent('rotate', this, this.rotated);
2676             return;
2677         }
2678         this.el.addClass('roo-card-rotated');
2679         this.footerEl.addClass('d-none');
2680         this.el.select('.roo-collapsable').removeClass('show');
2681         
2682         this.rotated = true;
2683         this.fireEvent('rotate', this, this.rotated);
2684     
2685     },
2686     
2687     dropPlaceHolder: function (action, info, data)
2688     {
2689         if (this.dropEl === false) {
2690             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2691             cls : 'd-none'
2692             },true);
2693         }
2694         this.dropEl.removeClass(['d-none', 'd-block']);        
2695         if (action == 'hide') {
2696             
2697             this.dropEl.addClass('d-none');
2698             return;
2699         }
2700         // FIXME - info.card == true!!!
2701         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702         
2703         if (info.card !== true) {
2704             var cardel = info.card.el.dom;
2705             
2706             if (info.position == 'above') {
2707                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708             } else if (cardel.nextSibling) {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710             } else {
2711                 cardel.parentNode.append(this.dropEl.dom);
2712             }
2713         } else {
2714             // card container???
2715             this.containerEl.dom.append(this.dropEl.dom);
2716         }
2717         
2718         this.dropEl.addClass('d-block roo-card-dropzone');
2719         
2720         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721         
2722         
2723     
2724     
2725     
2726     },
2727     setHeaderText: function(html)
2728     {
2729         this.header = html;
2730         if (this.headerContainerEl) {
2731             this.headerContainerEl.dom.innerHTML = html;
2732         }
2733     },
2734     onHeaderImageLoad : function(ev, he)
2735     {
2736         if (!this.header_image_fit_square) {
2737             return;
2738         }
2739         
2740         var hw = he.naturalHeight / he.naturalWidth;
2741         // wide image = < 0
2742         // tall image = > 1
2743         //var w = he.dom.naturalWidth;
2744         var ww = he.width;
2745         he.style.left =  0;
2746         he.style.position =  'relative';
2747         if (hw > 1) {
2748             var nw = (ww * (1/hw));
2749             Roo.get(he).setSize( ww * (1/hw),  ww);
2750             he.style.left =  ((ww - nw)/ 2) + 'px';
2751             he.style.position =  'relative';
2752         }
2753
2754     }
2755
2756     
2757 });
2758
2759 /*
2760  * - LGPL
2761  *
2762  * Card header - holder for the card header elements.
2763  * 
2764  */
2765
2766 /**
2767  * @class Roo.bootstrap.CardHeader
2768  * @extends Roo.bootstrap.Element
2769  * Bootstrap CardHeader class
2770  * @constructor
2771  * Create a new Card Header - that you can embed children into
2772  * @param {Object} config The config object
2773  */
2774
2775 Roo.bootstrap.CardHeader = function(config){
2776     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2777 };
2778
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2780     
2781     
2782     container_method : 'getCardHeader' 
2783     
2784      
2785     
2786     
2787    
2788 });
2789
2790  
2791
2792  /*
2793  * - LGPL
2794  *
2795  * Card footer - holder for the card footer elements.
2796  * 
2797  */
2798
2799 /**
2800  * @class Roo.bootstrap.CardFooter
2801  * @extends Roo.bootstrap.Element
2802  * Bootstrap CardFooter class
2803  * @constructor
2804  * Create a new Card Footer - that you can embed children into
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.CardFooter = function(config){
2809     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2810 };
2811
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2813     
2814     
2815     container_method : 'getCardFooter' 
2816     
2817      
2818     
2819     
2820    
2821 });
2822
2823  
2824
2825  /*
2826  * - LGPL
2827  *
2828  * Card header - holder for the card header elements.
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.CardImageTop
2834  * @extends Roo.bootstrap.Element
2835  * Bootstrap CardImageTop class
2836  * @constructor
2837  * Create a new Card Image Top container
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.CardImageTop = function(config){
2842     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2846     
2847    
2848     container_method : 'getCardImageTop' 
2849     
2850      
2851     
2852    
2853 });
2854
2855  
2856
2857  
2858 /*
2859 * Licence: LGPL
2860 */
2861
2862 /**
2863  * @class Roo.bootstrap.ButtonUploader
2864  * @extends Roo.bootstrap.Button
2865  * Bootstrap Button Uploader class - it's a button which when you add files to it
2866  *
2867  * 
2868  * @cfg {Number} errorTimeout default 3000
2869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2870  * @cfg {Array}  html The button text.
2871  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2872  *
2873  * @constructor
2874  * Create a new CardUploader
2875  * @param {Object} config The config object
2876  */
2877
2878 Roo.bootstrap.ButtonUploader = function(config){
2879     
2880  
2881     
2882     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2883     
2884      
2885      this.addEvents({
2886          // raw events
2887         /**
2888          * @event beforeselect
2889          * When button is pressed, before show upload files dialog is shown
2890          * @param {Roo.bootstrap.UploaderButton} this
2891          *
2892          */
2893         'beforeselect' : true,
2894          /**
2895          * @event fired when files have been selected, 
2896          * When a the download link is clicked
2897          * @param {Roo.bootstrap.UploaderButton} this
2898          * @param {Array} Array of files that have been uploaded
2899          */
2900         'uploaded' : true
2901         
2902     });
2903 };
2904  
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2906     
2907      
2908     errorTimeout : 3000,
2909      
2910     images : false,
2911    
2912     fileCollection : false,
2913     allowBlank : true,
2914     
2915     multiple : true,
2916     
2917     getAutoCreate : function()
2918     {
2919         var im = {
2920             tag: 'input',
2921             type : 'file',
2922             cls : 'd-none  roo-card-upload-selector' 
2923           
2924         };
2925         if (this.multiple) {
2926             im.multiple = 'multiple';
2927         }
2928         
2929         return  {
2930             cls :'div' ,
2931             cn : [
2932                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2933                 im
2934
2935             ]
2936         };
2937            
2938          
2939     },
2940      
2941    
2942     initEvents : function()
2943     {
2944         
2945         Roo.bootstrap.Button.prototype.initEvents.call(this);
2946         
2947         
2948         
2949         
2950         
2951         this.urlAPI = (window.createObjectURL && window) || 
2952                                 (window.URL && URL.revokeObjectURL && URL) || 
2953                                 (window.webkitURL && webkitURL);
2954                         
2955          
2956          
2957          
2958         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959         
2960         this.selectorEl.on('change', this.onFileSelected, this);
2961          
2962          
2963        
2964     },
2965     
2966    
2967     onClick : function(e)
2968     {
2969         e.preventDefault();
2970         
2971         if ( this.fireEvent('beforeselect', this) === false) {
2972             return;
2973         }
2974          
2975         this.selectorEl.dom.click();
2976          
2977     },
2978     
2979     onFileSelected : function(e)
2980     {
2981         e.preventDefault();
2982         
2983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2984             return;
2985         }
2986         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987         this.selectorEl.dom.value  = '';// hopefully reset..
2988         
2989         this.fireEvent('uploaded', this,  files );
2990         
2991     },
2992     
2993        
2994    
2995     
2996     /**
2997      * addCard - add an Attachment to the uploader
2998      * @param data - the data about the image to upload
2999      *
3000      * {
3001           id : 123
3002           title : "Title of file",
3003           is_uploaded : false,
3004           src : "http://.....",
3005           srcfile : { the File upload object },
3006           mimetype : file.type,
3007           preview : false,
3008           is_deleted : 0
3009           .. any other data...
3010         }
3011      *
3012      * 
3013     */
3014      
3015     reset: function()
3016     {
3017          
3018          this.selectorEl
3019     } 
3020     
3021     
3022     
3023     
3024 });
3025  /*
3026  * - LGPL
3027  *
3028  * image
3029  * 
3030  */
3031
3032
3033 /**
3034  * @class Roo.bootstrap.Img
3035  * @extends Roo.bootstrap.Component
3036  * Bootstrap Img class
3037  * @cfg {Boolean} imgResponsive false | true
3038  * @cfg {String} border rounded | circle | thumbnail
3039  * @cfg {String} src image source
3040  * @cfg {String} alt image alternative text
3041  * @cfg {String} href a tag href
3042  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043  * @cfg {String} xsUrl xs image source
3044  * @cfg {String} smUrl sm image source
3045  * @cfg {String} mdUrl md image source
3046  * @cfg {String} lgUrl lg image source
3047  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Img = function(config){
3055     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true,
3065         /**
3066          * @event load
3067          * The when any image loads
3068          * @param {Roo.EventObject} e
3069          */
3070         "load" : true
3071     });
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3075     
3076     imgResponsive: true,
3077     border: '',
3078     src: 'about:blank',
3079     href: false,
3080     target: false,
3081     xsUrl: '',
3082     smUrl: '',
3083     mdUrl: '',
3084     lgUrl: '',
3085     backgroundContain : false,
3086
3087     getAutoCreate : function()
3088     {   
3089         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090             return this.createSingleImg();
3091         }
3092         
3093         var cfg = {
3094             tag: 'div',
3095             cls: 'roo-image-responsive-group',
3096             cn: []
3097         };
3098         var _this = this;
3099         
3100         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101             
3102             if(!_this[size + 'Url']){
3103                 return;
3104             }
3105             
3106             var img = {
3107                 tag: 'img',
3108                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109                 html: _this.html || cfg.html,
3110                 src: _this[size + 'Url']
3111             };
3112             
3113             img.cls += ' roo-image-responsive-' + size;
3114             
3115             var s = ['xs', 'sm', 'md', 'lg'];
3116             
3117             s.splice(s.indexOf(size), 1);
3118             
3119             Roo.each(s, function(ss){
3120                 img.cls += ' hidden-' + ss;
3121             });
3122             
3123             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124                 cfg.cls += ' img-' + _this.border;
3125             }
3126             
3127             if(_this.alt){
3128                 cfg.alt = _this.alt;
3129             }
3130             
3131             if(_this.href){
3132                 var a = {
3133                     tag: 'a',
3134                     href: _this.href,
3135                     cn: [
3136                         img
3137                     ]
3138                 };
3139
3140                 if(this.target){
3141                     a.target = _this.target;
3142                 }
3143             }
3144             
3145             cfg.cn.push((_this.href) ? a : img);
3146             
3147         });
3148         
3149         return cfg;
3150     },
3151     
3152     createSingleImg : function()
3153     {
3154         var cfg = {
3155             tag: 'img',
3156             cls: (this.imgResponsive) ? 'img-responsive' : '',
3157             html : null,
3158             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3159         };
3160         
3161         if (this.backgroundContain) {
3162             cfg.cls += ' background-contain';
3163         }
3164         
3165         cfg.html = this.html || cfg.html;
3166         
3167         if (this.backgroundContain) {
3168             cfg.style="background-image: url(" + this.src + ')';
3169         } else {
3170             cfg.src = this.src || cfg.src;
3171         }
3172         
3173         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174             cfg.cls += ' img-' + this.border;
3175         }
3176         
3177         if(this.alt){
3178             cfg.alt = this.alt;
3179         }
3180         
3181         if(this.href){
3182             var a = {
3183                 tag: 'a',
3184                 href: this.href,
3185                 cn: [
3186                     cfg
3187                 ]
3188             };
3189             
3190             if(this.target){
3191                 a.target = this.target;
3192             }
3193             
3194         }
3195         
3196         return (this.href) ? a : cfg;
3197     },
3198     
3199     initEvents: function() 
3200     {
3201         if(!this.href){
3202             this.el.on('click', this.onClick, this);
3203         }
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.on('load', this.onImageLoad, this);
3206         } else {
3207             // not sure if this works.. not tested
3208             this.el.select('img', true).on('load', this.onImageLoad, this);
3209         }
3210         
3211     },
3212     
3213     onClick : function(e)
3214     {
3215         Roo.log('img onclick');
3216         this.fireEvent('click', this, e);
3217     },
3218     onImageLoad: function(e)
3219     {
3220         Roo.log('img load');
3221         this.fireEvent('load', this, e);
3222     },
3223     
3224     /**
3225      * Sets the url of the image - used to update it
3226      * @param {String} url the url of the image
3227      */
3228     
3229     setSrc : function(url)
3230     {
3231         this.src =  url;
3232         
3233         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234             if (this.backgroundContain) {
3235                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3236             } else {
3237                 this.el.dom.src =  url;
3238             }
3239             return;
3240         }
3241         
3242         this.el.select('img', true).first().dom.src =  url;
3243     }
3244     
3245     
3246    
3247 });
3248
3249  /*
3250  * - LGPL
3251  *
3252  * image
3253  * 
3254  */
3255
3256
3257 /**
3258  * @class Roo.bootstrap.Link
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap Link Class
3261  * @cfg {String} alt image alternative text
3262  * @cfg {String} href a tag href
3263  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264  * @cfg {String} html the content of the link.
3265  * @cfg {String} anchor name for the anchor link
3266  * @cfg {String} fa - favicon
3267
3268  * @cfg {Boolean} preventDefault (true | false) default false
3269
3270  * 
3271  * @constructor
3272  * Create a new Input
3273  * @param {Object} config The config object
3274  */
3275
3276 Roo.bootstrap.Link = function(config){
3277     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278     
3279     this.addEvents({
3280         // img events
3281         /**
3282          * @event click
3283          * The img click event for the img.
3284          * @param {Roo.EventObject} e
3285          */
3286         "click" : true
3287     });
3288 };
3289
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3291     
3292     href: false,
3293     target: false,
3294     preventDefault: false,
3295     anchor : false,
3296     alt : false,
3297     fa: false,
3298
3299
3300     getAutoCreate : function()
3301     {
3302         var html = this.html || '';
3303         
3304         if (this.fa !== false) {
3305             html = '<i class="fa fa-' + this.fa + '"></i>';
3306         }
3307         var cfg = {
3308             tag: 'a'
3309         };
3310         // anchor's do not require html/href...
3311         if (this.anchor === false) {
3312             cfg.html = html;
3313             cfg.href = this.href || '#';
3314         } else {
3315             cfg.name = this.anchor;
3316             if (this.html !== false || this.fa !== false) {
3317                 cfg.html = html;
3318             }
3319             if (this.href !== false) {
3320                 cfg.href = this.href;
3321             }
3322         }
3323         
3324         if(this.alt !== false){
3325             cfg.alt = this.alt;
3326         }
3327         
3328         
3329         if(this.target !== false) {
3330             cfg.target = this.target;
3331         }
3332         
3333         return cfg;
3334     },
3335     
3336     initEvents: function() {
3337         
3338         if(!this.href || this.preventDefault){
3339             this.el.on('click', this.onClick, this);
3340         }
3341     },
3342     
3343     onClick : function(e)
3344     {
3345         if(this.preventDefault){
3346             e.preventDefault();
3347         }
3348         //Roo.log('img onclick');
3349         this.fireEvent('click', this, e);
3350     }
3351    
3352 });
3353
3354  /*
3355  * - LGPL
3356  *
3357  * header
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Header
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Header class
3365  * @cfg {String} html content of header
3366  * @cfg {Number} level (1|2|3|4|5|6) default 1
3367  * 
3368  * @constructor
3369  * Create a new Header
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Header  = function(config){
3375     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3379     
3380     //href : false,
3381     html : false,
3382     level : 1,
3383     
3384     
3385     
3386     getAutoCreate : function(){
3387         
3388         
3389         
3390         var cfg = {
3391             tag: 'h' + (1 *this.level),
3392             html: this.html || ''
3393         } ;
3394         
3395         return cfg;
3396     }
3397    
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412  
3413 /**
3414  * @class Roo.bootstrap.MenuMgr
3415  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3416  * @singleton
3417  */
3418 Roo.bootstrap.MenuMgr = function(){
3419    var menus, active, groups = {}, attached = false, lastShow = new Date();
3420
3421    // private - called when first menu is created
3422    function init(){
3423        menus = {};
3424        active = new Roo.util.MixedCollection();
3425        Roo.get(document).addKeyListener(27, function(){
3426            if(active.length > 0){
3427                hideAll();
3428            }
3429        });
3430    }
3431
3432    // private
3433    function hideAll(){
3434        if(active && active.length > 0){
3435            var c = active.clone();
3436            c.each(function(m){
3437                m.hide();
3438            });
3439        }
3440    }
3441
3442    // private
3443    function onHide(m){
3444        active.remove(m);
3445        if(active.length < 1){
3446            Roo.get(document).un("mouseup", onMouseDown);
3447             
3448            attached = false;
3449        }
3450    }
3451
3452    // private
3453    function onShow(m){
3454        var last = active.last();
3455        lastShow = new Date();
3456        active.add(m);
3457        if(!attached){
3458           Roo.get(document).on("mouseup", onMouseDown);
3459            
3460            attached = true;
3461        }
3462        if(m.parentMenu){
3463           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464           m.parentMenu.activeChild = m;
3465        }else if(last && last.isVisible()){
3466           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3467        }
3468    }
3469
3470    // private
3471    function onBeforeHide(m){
3472        if(m.activeChild){
3473            m.activeChild.hide();
3474        }
3475        if(m.autoHideTimer){
3476            clearTimeout(m.autoHideTimer);
3477            delete m.autoHideTimer;
3478        }
3479    }
3480
3481    // private
3482    function onBeforeShow(m){
3483        var pm = m.parentMenu;
3484        if(!pm && !m.allowOtherMenus){
3485            hideAll();
3486        }else if(pm && pm.activeChild && active != m){
3487            pm.activeChild.hide();
3488        }
3489    }
3490
3491    // private this should really trigger on mouseup..
3492    function onMouseDown(e){
3493         Roo.log("on Mouse Up");
3494         
3495         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496             Roo.log("MenuManager hideAll");
3497             hideAll();
3498             e.stopEvent();
3499         }
3500         
3501         
3502    }
3503
3504    // private
3505    function onBeforeCheck(mi, state){
3506        if(state){
3507            var g = groups[mi.group];
3508            for(var i = 0, l = g.length; i < l; i++){
3509                if(g[i] != mi){
3510                    g[i].setChecked(false);
3511                }
3512            }
3513        }
3514    }
3515
3516    return {
3517
3518        /**
3519         * Hides all menus that are currently visible
3520         */
3521        hideAll : function(){
3522             hideAll();  
3523        },
3524
3525        // private
3526        register : function(menu){
3527            if(!menus){
3528                init();
3529            }
3530            menus[menu.id] = menu;
3531            menu.on("beforehide", onBeforeHide);
3532            menu.on("hide", onHide);
3533            menu.on("beforeshow", onBeforeShow);
3534            menu.on("show", onShow);
3535            var g = menu.group;
3536            if(g && menu.events["checkchange"]){
3537                if(!groups[g]){
3538                    groups[g] = [];
3539                }
3540                groups[g].push(menu);
3541                menu.on("checkchange", onCheck);
3542            }
3543        },
3544
3545         /**
3546          * Returns a {@link Roo.menu.Menu} object
3547          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548          * be used to generate and return a new Menu instance.
3549          */
3550        get : function(menu){
3551            if(typeof menu == "string"){ // menu id
3552                return menus[menu];
3553            }else if(menu.events){  // menu instance
3554                return menu;
3555            }
3556            /*else if(typeof menu.length == 'number'){ // array of menu items?
3557                return new Roo.bootstrap.Menu({items:menu});
3558            }else{ // otherwise, must be a config
3559                return new Roo.bootstrap.Menu(menu);
3560            }
3561            */
3562            return false;
3563        },
3564
3565        // private
3566        unregister : function(menu){
3567            delete menus[menu.id];
3568            menu.un("beforehide", onBeforeHide);
3569            menu.un("hide", onHide);
3570            menu.un("beforeshow", onBeforeShow);
3571            menu.un("show", onShow);
3572            var g = menu.group;
3573            if(g && menu.events["checkchange"]){
3574                groups[g].remove(menu);
3575                menu.un("checkchange", onCheck);
3576            }
3577        },
3578
3579        // private
3580        registerCheckable : function(menuItem){
3581            var g = menuItem.group;
3582            if(g){
3583                if(!groups[g]){
3584                    groups[g] = [];
3585                }
3586                groups[g].push(menuItem);
3587                menuItem.on("beforecheckchange", onBeforeCheck);
3588            }
3589        },
3590
3591        // private
3592        unregisterCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                groups[g].remove(menuItem);
3596                menuItem.un("beforecheckchange", onBeforeCheck);
3597            }
3598        }
3599    };
3600 }();/*
3601  * - LGPL
3602  *
3603  * menu
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.Menu
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Menu class - container for MenuItems
3611  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3613  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3614  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3615   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3616   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3617  
3618  * @constructor
3619  * Create a new Menu
3620  * @param {Object} config The config object
3621  */
3622
3623
3624 Roo.bootstrap.Menu = function(config){
3625     
3626     if (config.type == 'treeview') {
3627         // normally menu's are drawn attached to the document to handle layering etc..
3628         // however treeview (used by the docs menu is drawn into the parent element)
3629         this.container_method = 'getChildContainer'; 
3630     }
3631     
3632     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633     if (this.registerMenu && this.type != 'treeview')  {
3634         Roo.bootstrap.MenuMgr.register(this);
3635     }
3636     
3637     
3638     this.addEvents({
3639         /**
3640          * @event beforeshow
3641          * Fires before this menu is displayed (return false to block)
3642          * @param {Roo.menu.Menu} this
3643          */
3644         beforeshow : true,
3645         /**
3646          * @event beforehide
3647          * Fires before this menu is hidden (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforehide : true,
3651         /**
3652          * @event show
3653          * Fires after this menu is displayed
3654          * @param {Roo.menu.Menu} this
3655          */
3656         show : true,
3657         /**
3658          * @event hide
3659          * Fires after this menu is hidden
3660          * @param {Roo.menu.Menu} this
3661          */
3662         hide : true,
3663         /**
3664          * @event click
3665          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666          * @param {Roo.menu.Menu} this
3667          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668          * @param {Roo.EventObject} e
3669          */
3670         click : true,
3671         /**
3672          * @event mouseover
3673          * Fires when the mouse is hovering over this menu
3674          * @param {Roo.menu.Menu} this
3675          * @param {Roo.EventObject} e
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          */
3678         mouseover : true,
3679         /**
3680          * @event mouseout
3681          * Fires when the mouse exits this menu
3682          * @param {Roo.menu.Menu} this
3683          * @param {Roo.EventObject} e
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          */
3686         mouseout : true,
3687         /**
3688          * @event itemclick
3689          * Fires when a menu item contained in this menu is clicked
3690          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691          * @param {Roo.EventObject} e
3692          */
3693         itemclick: true
3694     });
3695     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3696 };
3697
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3699     
3700    /// html : false,
3701    
3702     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3703     type: false,
3704     /**
3705      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706      */
3707     registerMenu : true,
3708     
3709     menuItems :false, // stores the menu items..
3710     
3711     hidden:true,
3712         
3713     parentMenu : false,
3714     
3715     stopEvent : true,
3716     
3717     isLink : false,
3718     
3719     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720     
3721     hideTrigger : false,
3722     
3723     align : 'tl-bl?',
3724     
3725     
3726     getChildContainer : function() {
3727         return this.el;  
3728     },
3729     
3730     getAutoCreate : function(){
3731          
3732         //if (['right'].indexOf(this.align)!==-1) {
3733         //    cfg.cn[1].cls += ' pull-right'
3734         //}
3735          
3736         var cfg = {
3737             tag : 'ul',
3738             cls : 'dropdown-menu shadow' ,
3739             style : 'z-index:1000'
3740             
3741         };
3742         
3743         if (this.type === 'submenu') {
3744             cfg.cls = 'submenu active';
3745         }
3746         if (this.type === 'treeview') {
3747             cfg.cls = 'treeview-menu';
3748         }
3749         
3750         return cfg;
3751     },
3752     initEvents : function() {
3753         
3754        // Roo.log("ADD event");
3755        // Roo.log(this.triggerEl.dom);
3756         if (this.triggerEl) {
3757             
3758             this.triggerEl.on('click', this.onTriggerClick, this);
3759             
3760             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761             
3762             if (!this.hideTrigger) {
3763                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764                     // dropdown toggle on the 'a' in BS4?
3765                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766                 } else {
3767                     this.triggerEl.addClass('dropdown-toggle');
3768                 }
3769             }
3770         }
3771         
3772         if (Roo.isTouch) {
3773             this.el.on('touchstart'  , this.onTouch, this);
3774         }
3775         this.el.on('click' , this.onClick, this);
3776
3777         this.el.on("mouseover", this.onMouseOver, this);
3778         this.el.on("mouseout", this.onMouseOut, this);
3779         
3780     },
3781     
3782     findTargetItem : function(e)
3783     {
3784         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3785         if(!t){
3786             return false;
3787         }
3788         //Roo.log(t);         Roo.log(t.id);
3789         if(t && t.id){
3790             //Roo.log(this.menuitems);
3791             return this.menuitems.get(t.id);
3792             
3793             //return this.items.get(t.menuItemId);
3794         }
3795         
3796         return false;
3797     },
3798     
3799     onTouch : function(e) 
3800     {
3801         Roo.log("menu.onTouch");
3802         //e.stopEvent(); this make the user popdown broken
3803         this.onClick(e);
3804     },
3805     
3806     onClick : function(e)
3807     {
3808         Roo.log("menu.onClick");
3809         
3810         var t = this.findTargetItem(e);
3811         if(!t || t.isContainer){
3812             return;
3813         }
3814         Roo.log(e);
3815         /*
3816         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3817             if(t == this.activeItem && t.shouldDeactivate(e)){
3818                 this.activeItem.deactivate();
3819                 delete this.activeItem;
3820                 return;
3821             }
3822             if(t.canActivate){
3823                 this.setActiveItem(t, true);
3824             }
3825             return;
3826             
3827             
3828         }
3829         */
3830        
3831         Roo.log('pass click event');
3832         
3833         t.onClick(e);
3834         
3835         this.fireEvent("click", this, t, e);
3836         
3837         var _this = this;
3838         
3839         if(!t.href.length || t.href == '#'){
3840             (function() { _this.hide(); }).defer(100);
3841         }
3842         
3843     },
3844     
3845     onMouseOver : function(e){
3846         var t  = this.findTargetItem(e);
3847         //Roo.log(t);
3848         //if(t){
3849         //    if(t.canActivate && !t.disabled){
3850         //        this.setActiveItem(t, true);
3851         //    }
3852         //}
3853         
3854         this.fireEvent("mouseover", this, e, t);
3855     },
3856     isVisible : function(){
3857         return !this.hidden;
3858     },
3859     onMouseOut : function(e){
3860         var t  = this.findTargetItem(e);
3861         
3862         //if(t ){
3863         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3864         //        this.activeItem.deactivate();
3865         //        delete this.activeItem;
3866         //    }
3867         //}
3868         this.fireEvent("mouseout", this, e, t);
3869     },
3870     
3871     
3872     /**
3873      * Displays this menu relative to another element
3874      * @param {String/HTMLElement/Roo.Element} element The element to align to
3875      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876      * the element (defaults to this.defaultAlign)
3877      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878      */
3879     show : function(el, pos, parentMenu)
3880     {
3881         if (false === this.fireEvent("beforeshow", this)) {
3882             Roo.log("show canceled");
3883             return;
3884         }
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         this.el.addClass('show'); // show otherwise we do not know how big we are..
3890          
3891         var xy = this.el.getAlignToXY(el, pos);
3892         
3893         // bl-tl << left align  below
3894         // tl-bl << left align 
3895         
3896         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897             // if it goes to far to the right.. -> align left.
3898             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3899         }
3900         if(xy[0] < 0){
3901             // was left align - go right?
3902             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3903         }
3904         
3905         // goes down the bottom
3906         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907            xy[1]  < 0 ){
3908             var a = this.align.replace('?', '').split('-');
3909             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3910             
3911         }
3912         
3913         this.showAt(  xy , parentMenu, false);
3914     },
3915      /**
3916      * Displays this menu at a specific xy position
3917      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919      */
3920     showAt : function(xy, parentMenu, /* private: */_e){
3921         this.parentMenu = parentMenu;
3922         if(!this.el){
3923             this.render();
3924         }
3925         if(_e !== false){
3926             this.fireEvent("beforeshow", this);
3927             //xy = this.el.adjustForConstraints(xy);
3928         }
3929         
3930         //this.el.show();
3931         this.hideMenuItems();
3932         this.hidden = false;
3933         if (this.triggerEl) {
3934             this.triggerEl.addClass('open');
3935         }
3936         
3937         this.el.addClass('show');
3938         
3939         
3940         
3941         // reassign x when hitting right
3942         
3943         // reassign y when hitting bottom
3944         
3945         // but the list may align on trigger left or trigger top... should it be a properity?
3946         
3947         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3948             this.el.setXY(xy);
3949         }
3950         
3951         this.focus();
3952         this.fireEvent("show", this);
3953     },
3954     
3955     focus : function(){
3956         return;
3957         if(!this.hidden){
3958             this.doFocus.defer(50, this);
3959         }
3960     },
3961
3962     doFocus : function(){
3963         if(!this.hidden){
3964             this.focusEl.focus();
3965         }
3966     },
3967
3968     /**
3969      * Hides this menu and optionally all parent menus
3970      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971      */
3972     hide : function(deep)
3973     {
3974         if (false === this.fireEvent("beforehide", this)) {
3975             Roo.log("hide canceled");
3976             return;
3977         }
3978         this.hideMenuItems();
3979         if(this.el && this.isVisible()){
3980            
3981             if(this.activeItem){
3982                 this.activeItem.deactivate();
3983                 this.activeItem = null;
3984             }
3985             if (this.triggerEl) {
3986                 this.triggerEl.removeClass('open');
3987             }
3988             
3989             this.el.removeClass('show');
3990             this.hidden = true;
3991             this.fireEvent("hide", this);
3992         }
3993         if(deep === true && this.parentMenu){
3994             this.parentMenu.hide(true);
3995         }
3996     },
3997     
3998     onTriggerClick : function(e)
3999     {
4000         Roo.log('trigger click');
4001         
4002         var target = e.getTarget();
4003         
4004         Roo.log(target.nodeName.toLowerCase());
4005         
4006         if(target.nodeName.toLowerCase() === 'i'){
4007             e.preventDefault();
4008         }
4009         
4010     },
4011     
4012     onTriggerPress  : function(e)
4013     {
4014         Roo.log('trigger press');
4015         //Roo.log(e.getTarget());
4016        // Roo.log(this.triggerEl.dom);
4017        
4018         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019         var pel = Roo.get(e.getTarget());
4020         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021             Roo.log('is treeview or dropdown?');
4022             return;
4023         }
4024         
4025         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4026             return;
4027         }
4028         
4029         if (this.isVisible()) {
4030             Roo.log('hide');
4031             this.hide();
4032         } else {
4033             Roo.log('show');
4034             
4035             this.show(this.triggerEl, this.align, false);
4036         }
4037         
4038         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039             e.stopEvent();
4040         }
4041         
4042     },
4043        
4044     
4045     hideMenuItems : function()
4046     {
4047         Roo.log("hide Menu Items");
4048         if (!this.el) { 
4049             return;
4050         }
4051         
4052         this.el.select('.open',true).each(function(aa) {
4053             
4054             aa.removeClass('open');
4055          
4056         });
4057     },
4058     addxtypeChild : function (tree, cntr) {
4059         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060           
4061         this.menuitems.add(comp);
4062         return comp;
4063
4064     },
4065     getEl : function()
4066     {
4067         Roo.log(this.el);
4068         return this.el;
4069     },
4070     
4071     clear : function()
4072     {
4073         this.getEl().dom.innerHTML = '';
4074         this.menuitems.clear();
4075     }
4076 });
4077
4078  
4079  /*
4080  * - LGPL
4081  *
4082  * menu item
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.MenuItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap MenuItem class
4091  * @cfg {String} html the menu label
4092  * @cfg {String} href the link
4093  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4096  * @cfg {String} fa favicon to show on left of menu item.
4097  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4098  * 
4099  * 
4100  * @constructor
4101  * Create a new MenuItem
4102  * @param {Object} config The config object
4103  */
4104
4105
4106 Roo.bootstrap.MenuItem = function(config){
4107     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4108     this.addEvents({
4109         // raw events
4110         /**
4111          * @event click
4112          * The raw click event for the entire grid.
4113          * @param {Roo.bootstrap.MenuItem} this
4114          * @param {Roo.EventObject} e
4115          */
4116         "click" : true
4117     });
4118 };
4119
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4121     
4122     href : false,
4123     html : false,
4124     preventDefault: false,
4125     isContainer : false,
4126     active : false,
4127     fa: false,
4128     
4129     getAutoCreate : function(){
4130         
4131         if(this.isContainer){
4132             return {
4133                 tag: 'li',
4134                 cls: 'dropdown-menu-item '
4135             };
4136         }
4137         var ctag = {
4138             tag: 'span',
4139             html: 'Link'
4140         };
4141         
4142         var anc = {
4143             tag : 'a',
4144             cls : 'dropdown-item',
4145             href : '#',
4146             cn : [  ]
4147         };
4148         
4149         if (this.fa !== false) {
4150             anc.cn.push({
4151                 tag : 'i',
4152                 cls : 'fa fa-' + this.fa
4153             });
4154         }
4155         
4156         anc.cn.push(ctag);
4157         
4158         
4159         var cfg= {
4160             tag: 'li',
4161             cls: 'dropdown-menu-item',
4162             cn: [ anc ]
4163         };
4164         if (this.parent().type == 'treeview') {
4165             cfg.cls = 'treeview-menu';
4166         }
4167         if (this.active) {
4168             cfg.cls += ' active';
4169         }
4170         
4171         
4172         
4173         anc.href = this.href || cfg.cn[0].href ;
4174         ctag.html = this.html || cfg.cn[0].html ;
4175         return cfg;
4176     },
4177     
4178     initEvents: function()
4179     {
4180         if (this.parent().type == 'treeview') {
4181             this.el.select('a').on('click', this.onClick, this);
4182         }
4183         
4184         if (this.menu) {
4185             this.menu.parentType = this.xtype;
4186             this.menu.triggerEl = this.el;
4187             this.menu = this.addxtype(Roo.apply({}, this.menu));
4188         }
4189         
4190     },
4191     onClick : function(e)
4192     {
4193         Roo.log('item on click ');
4194         
4195         if(this.preventDefault){
4196             e.preventDefault();
4197         }
4198         //this.parent().hideMenuItems();
4199         
4200         this.fireEvent('click', this, e);
4201     },
4202     getEl : function()
4203     {
4204         return this.el;
4205     } 
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * menu separator
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.MenuSeparator
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap MenuSeparator class
4222  * 
4223  * @constructor
4224  * Create a new MenuItem
4225  * @param {Object} config The config object
4226  */
4227
4228
4229 Roo.bootstrap.MenuSeparator = function(config){
4230     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4231 };
4232
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4234     
4235     getAutoCreate : function(){
4236         var cfg = {
4237             cls: 'divider',
4238             tag : 'li'
4239         };
4240         
4241         return cfg;
4242     }
4243    
4244 });
4245
4246  
4247
4248  
4249 /*
4250 * Licence: LGPL
4251 */
4252
4253 /**
4254  * @class Roo.bootstrap.Modal
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Modal class
4257  * @cfg {String} title Title of dialog
4258  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4260  * @cfg {Boolean} specificTitle default false
4261  * @cfg {Array} buttons Array of buttons or standard button set..
4262  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263  * @cfg {Boolean} animate default true
4264  * @cfg {Boolean} allow_close default true
4265  * @cfg {Boolean} fitwindow default false
4266  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269  * @cfg {String} size (sm|lg|xl) default empty
4270  * @cfg {Number} max_width set the max width of modal
4271  * @cfg {Boolean} editableTitle can the title be edited
4272
4273  *
4274  *
4275  * @constructor
4276  * Create a new Modal Dialog
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Modal = function(config){
4281     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4282     this.addEvents({
4283         // raw events
4284         /**
4285          * @event btnclick
4286          * The raw btnclick event for the button
4287          * @param {Roo.EventObject} e
4288          */
4289         "btnclick" : true,
4290         /**
4291          * @event resize
4292          * Fire when dialog resize
4293          * @param {Roo.bootstrap.Modal} this
4294          * @param {Roo.EventObject} e
4295          */
4296         "resize" : true,
4297         /**
4298          * @event titlechanged
4299          * Fire when the editable title has been changed
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} value
4302          */
4303         "titlechanged" : true 
4304         
4305     });
4306     this.buttons = this.buttons || [];
4307
4308     if (this.tmpl) {
4309         this.tmpl = Roo.factory(this.tmpl);
4310     }
4311
4312 };
4313
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4315
4316     title : 'test dialog',
4317
4318     buttons : false,
4319
4320     // set on load...
4321
4322     html: false,
4323
4324     tmp: false,
4325
4326     specificTitle: false,
4327
4328     buttonPosition: 'right',
4329
4330     allow_close : true,
4331
4332     animate : true,
4333
4334     fitwindow: false,
4335     
4336      // private
4337     dialogEl: false,
4338     bodyEl:  false,
4339     footerEl:  false,
4340     titleEl:  false,
4341     closeEl:  false,
4342
4343     size: '',
4344     
4345     max_width: 0,
4346     
4347     max_height: 0,
4348     
4349     fit_content: false,
4350     editableTitle  : false,
4351
4352     onRender : function(ct, position)
4353     {
4354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4355
4356         if(!this.el){
4357             var cfg = Roo.apply({},  this.getAutoCreate());
4358             cfg.id = Roo.id();
4359             //if(!cfg.name){
4360             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361             //}
4362             //if (!cfg.name.length) {
4363             //    delete cfg.name;
4364            // }
4365             if (this.cls) {
4366                 cfg.cls += ' ' + this.cls;
4367             }
4368             if (this.style) {
4369                 cfg.style = this.style;
4370             }
4371             this.el = Roo.get(document.body).createChild(cfg, position);
4372         }
4373         //var type = this.el.dom.type;
4374
4375
4376         if(this.tabIndex !== undefined){
4377             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4378         }
4379
4380         this.dialogEl = this.el.select('.modal-dialog',true).first();
4381         this.bodyEl = this.el.select('.modal-body',true).first();
4382         this.closeEl = this.el.select('.modal-header .close', true).first();
4383         this.headerEl = this.el.select('.modal-header',true).first();
4384         this.titleEl = this.el.select('.modal-title',true).first();
4385         this.footerEl = this.el.select('.modal-footer',true).first();
4386
4387         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388         
4389         //this.el.addClass("x-dlg-modal");
4390
4391         if (this.buttons.length) {
4392             Roo.each(this.buttons, function(bb) {
4393                 var b = Roo.apply({}, bb);
4394                 b.xns = b.xns || Roo.bootstrap;
4395                 b.xtype = b.xtype || 'Button';
4396                 if (typeof(b.listeners) == 'undefined') {
4397                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4398                 }
4399
4400                 var btn = Roo.factory(b);
4401
4402                 btn.render(this.getButtonContainer());
4403
4404             },this);
4405         }
4406         // render the children.
4407         var nitems = [];
4408
4409         if(typeof(this.items) != 'undefined'){
4410             var items = this.items;
4411             delete this.items;
4412
4413             for(var i =0;i < items.length;i++) {
4414                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4415             }
4416         }
4417
4418         this.items = nitems;
4419
4420         // where are these used - they used to be body/close/footer
4421
4422
4423         this.initEvents();
4424         //this.el.addClass([this.fieldClass, this.cls]);
4425
4426     },
4427
4428     getAutoCreate : function()
4429     {
4430         // we will default to modal-body-overflow - might need to remove or make optional later.
4431         var bdy = {
4432                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4433                 html : this.html || ''
4434         };
4435
4436         var title = {
4437             tag: 'h5',
4438             cls : 'modal-title',
4439             html : this.title
4440         };
4441
4442         if(this.specificTitle){ // WTF is this?
4443             title = this.title;
4444         }
4445
4446         var header = [];
4447         if (this.allow_close && Roo.bootstrap.version == 3) {
4448             header.push({
4449                 tag: 'button',
4450                 cls : 'close',
4451                 html : '&times'
4452             });
4453         }
4454
4455         header.push(title);
4456
4457         if (this.editableTitle) {
4458             header.push({
4459                 cls: 'form-control roo-editable-title d-none',
4460                 tag: 'input',
4461                 type: 'text'
4462             });
4463         }
4464         
4465         if (this.allow_close && Roo.bootstrap.version == 4) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472         
4473         var size = '';
4474
4475         if(this.size.length){
4476             size = 'modal-' + this.size;
4477         }
4478         
4479         var footer = Roo.bootstrap.version == 3 ?
4480             {
4481                 cls : 'modal-footer',
4482                 cn : [
4483                     {
4484                         tag: 'div',
4485                         cls: 'btn-' + this.buttonPosition
4486                     }
4487                 ]
4488
4489             } :
4490             {  // BS4 uses mr-auto on left buttons....
4491                 cls : 'modal-footer'
4492             };
4493
4494             
4495
4496         
4497         
4498         var modal = {
4499             cls: "modal",
4500              cn : [
4501                 {
4502                     cls: "modal-dialog " + size,
4503                     cn : [
4504                         {
4505                             cls : "modal-content",
4506                             cn : [
4507                                 {
4508                                     cls : 'modal-header',
4509                                     cn : header
4510                                 },
4511                                 bdy,
4512                                 footer
4513                             ]
4514
4515                         }
4516                     ]
4517
4518                 }
4519             ]
4520         };
4521
4522         if(this.animate){
4523             modal.cls += ' fade';
4524         }
4525
4526         return modal;
4527
4528     },
4529     getChildContainer : function() {
4530
4531          return this.bodyEl;
4532
4533     },
4534     getButtonContainer : function() {
4535         
4536          return Roo.bootstrap.version == 4 ?
4537             this.el.select('.modal-footer',true).first()
4538             : this.el.select('.modal-footer div',true).first();
4539
4540     },
4541     initEvents : function()
4542     {
4543         if (this.allow_close) {
4544             this.closeEl.on('click', this.hide, this);
4545         }
4546         Roo.EventManager.onWindowResize(this.resize, this, true);
4547         if (this.editableTitle) {
4548             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4549             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550             this.headerEditEl.on('keyup', function(e) {
4551                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552                         this.toggleHeaderInput(false)
4553                     }
4554                 }, this);
4555             this.headerEditEl.on('blur', function(e) {
4556                 this.toggleHeaderInput(false)
4557             },this);
4558         }
4559
4560     },
4561   
4562
4563     resize : function()
4564     {
4565         this.maskEl.setSize(
4566             Roo.lib.Dom.getViewWidth(true),
4567             Roo.lib.Dom.getViewHeight(true)
4568         );
4569         
4570         if (this.fitwindow) {
4571             
4572            this.dialogEl.setStyle( { 'max-width' : '100%' });
4573             this.setSize(
4574                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4576             );
4577             return;
4578         }
4579         
4580         if(this.max_width !== 0) {
4581             
4582             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4583             
4584             if(this.height) {
4585                 this.setSize(w, this.height);
4586                 return;
4587             }
4588             
4589             if(this.max_height) {
4590                 this.setSize(w,Math.min(
4591                     this.max_height,
4592                     Roo.lib.Dom.getViewportHeight(true) - 60
4593                 ));
4594                 
4595                 return;
4596             }
4597             
4598             if(!this.fit_content) {
4599                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4600                 return;
4601             }
4602             
4603             this.setSize(w, Math.min(
4604                 60 +
4605                 this.headerEl.getHeight() + 
4606                 this.footerEl.getHeight() + 
4607                 this.getChildHeight(this.bodyEl.dom.childNodes),
4608                 Roo.lib.Dom.getViewportHeight(true) - 60)
4609             );
4610         }
4611         
4612     },
4613
4614     setSize : function(w,h)
4615     {
4616         if (!w && !h) {
4617             return;
4618         }
4619         
4620         this.resizeTo(w,h);
4621     },
4622
4623     show : function() {
4624
4625         if (!this.rendered) {
4626             this.render();
4627         }
4628         this.toggleHeaderInput(false);
4629         //this.el.setStyle('display', 'block');
4630         this.el.removeClass('hideing');
4631         this.el.dom.style.display='block';
4632         
4633         Roo.get(document.body).addClass('modal-open');
4634  
4635         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4636             
4637             (function(){
4638                 this.el.addClass('show');
4639                 this.el.addClass('in');
4640             }).defer(50, this);
4641         }else{
4642             this.el.addClass('show');
4643             this.el.addClass('in');
4644         }
4645
4646         // not sure how we can show data in here..
4647         //if (this.tmpl) {
4648         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4649         //}
4650
4651         Roo.get(document.body).addClass("x-body-masked");
4652         
4653         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4654         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655         this.maskEl.dom.style.display = 'block';
4656         this.maskEl.addClass('show');
4657         
4658         
4659         this.resize();
4660         
4661         this.fireEvent('show', this);
4662
4663         // set zindex here - otherwise it appears to be ignored...
4664         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4665
4666         (function () {
4667             this.items.forEach( function(e) {
4668                 e.layout ? e.layout() : false;
4669
4670             });
4671         }).defer(100,this);
4672
4673     },
4674     hide : function()
4675     {
4676         if(this.fireEvent("beforehide", this) !== false){
4677             
4678             this.maskEl.removeClass('show');
4679             
4680             this.maskEl.dom.style.display = '';
4681             Roo.get(document.body).removeClass("x-body-masked");
4682             this.el.removeClass('in');
4683             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684
4685             if(this.animate){ // why
4686                 this.el.addClass('hideing');
4687                 this.el.removeClass('show');
4688                 (function(){
4689                     if (!this.el.hasClass('hideing')) {
4690                         return; // it's been shown again...
4691                     }
4692                     
4693                     this.el.dom.style.display='';
4694
4695                     Roo.get(document.body).removeClass('modal-open');
4696                     this.el.removeClass('hideing');
4697                 }).defer(150,this);
4698                 
4699             }else{
4700                 this.el.removeClass('show');
4701                 this.el.dom.style.display='';
4702                 Roo.get(document.body).removeClass('modal-open');
4703
4704             }
4705             this.fireEvent('hide', this);
4706         }
4707     },
4708     isVisible : function()
4709     {
4710         
4711         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4712         
4713     },
4714
4715     addButton : function(str, cb)
4716     {
4717
4718
4719         var b = Roo.apply({}, { html : str } );
4720         b.xns = b.xns || Roo.bootstrap;
4721         b.xtype = b.xtype || 'Button';
4722         if (typeof(b.listeners) == 'undefined') {
4723             b.listeners = { click : cb.createDelegate(this)  };
4724         }
4725
4726         var btn = Roo.factory(b);
4727
4728         btn.render(this.getButtonContainer());
4729
4730         return btn;
4731
4732     },
4733
4734     setDefaultButton : function(btn)
4735     {
4736         //this.el.select('.modal-footer').()
4737     },
4738
4739     resizeTo: function(w,h)
4740     {
4741         this.dialogEl.setWidth(w);
4742         
4743         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4744
4745         this.bodyEl.setHeight(h - diff);
4746         
4747         this.fireEvent('resize', this);
4748     },
4749     
4750     setContentSize  : function(w, h)
4751     {
4752
4753     },
4754     onButtonClick: function(btn,e)
4755     {
4756         //Roo.log([a,b,c]);
4757         this.fireEvent('btnclick', btn.name, e);
4758     },
4759      /**
4760      * Set the title of the Dialog
4761      * @param {String} str new Title
4762      */
4763     setTitle: function(str) {
4764         this.titleEl.dom.innerHTML = str;
4765         this.title = str;
4766     },
4767     /**
4768      * Set the body of the Dialog
4769      * @param {String} str new Title
4770      */
4771     setBody: function(str) {
4772         this.bodyEl.dom.innerHTML = str;
4773     },
4774     /**
4775      * Set the body of the Dialog using the template
4776      * @param {Obj} data - apply this data to the template and replace the body contents.
4777      */
4778     applyBody: function(obj)
4779     {
4780         if (!this.tmpl) {
4781             Roo.log("Error - using apply Body without a template");
4782             //code
4783         }
4784         this.tmpl.overwrite(this.bodyEl, obj);
4785     },
4786     
4787     getChildHeight : function(child_nodes)
4788     {
4789         if(
4790             !child_nodes ||
4791             child_nodes.length == 0
4792         ) {
4793             return 0;
4794         }
4795         
4796         var child_height = 0;
4797         
4798         for(var i = 0; i < child_nodes.length; i++) {
4799             
4800             /*
4801             * for modal with tabs...
4802             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803                 
4804                 var layout_childs = child_nodes[i].childNodes;
4805                 
4806                 for(var j = 0; j < layout_childs.length; j++) {
4807                     
4808                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809                         
4810                         var layout_body_childs = layout_childs[j].childNodes;
4811                         
4812                         for(var k = 0; k < layout_body_childs.length; k++) {
4813                             
4814                             if(layout_body_childs[k].classList.contains('navbar')) {
4815                                 child_height += layout_body_childs[k].offsetHeight;
4816                                 continue;
4817                             }
4818                             
4819                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820                                 
4821                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822                                 
4823                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824                                     
4825                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827                                         continue;
4828                                     }
4829                                     
4830                                 }
4831                                 
4832                             }
4833                             
4834                         }
4835                     }
4836                 }
4837                 continue;
4838             }
4839             */
4840             
4841             child_height += child_nodes[i].offsetHeight;
4842             // Roo.log(child_nodes[i].offsetHeight);
4843         }
4844         
4845         return child_height;
4846     },
4847     toggleHeaderInput : function(is_edit)
4848     {
4849         if (!this.editableTitle) {
4850             return; // not editable.
4851         }
4852         if (is_edit && this.is_header_editing) {
4853             return; // already editing..
4854         }
4855         if (is_edit) {
4856     
4857             this.headerEditEl.dom.value = this.title;
4858             this.headerEditEl.removeClass('d-none');
4859             this.headerEditEl.dom.focus();
4860             this.titleEl.addClass('d-none');
4861             
4862             this.is_header_editing = true;
4863             return
4864         }
4865         // flip back to not editing.
4866         this.title = this.headerEditEl.dom.value;
4867         this.headerEditEl.addClass('d-none');
4868         this.titleEl.removeClass('d-none');
4869         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870         this.is_header_editing = false;
4871         this.fireEvent('titlechanged', this, this.title);
4872     
4873             
4874         
4875     }
4876
4877 });
4878
4879
4880 Roo.apply(Roo.bootstrap.Modal,  {
4881     /**
4882          * Button config that displays a single OK button
4883          * @type Object
4884          */
4885         OK :  [{
4886             name : 'ok',
4887             weight : 'primary',
4888             html : 'OK'
4889         }],
4890         /**
4891          * Button config that displays Yes and No buttons
4892          * @type Object
4893          */
4894         YESNO : [
4895             {
4896                 name  : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name  :'yes',
4901                 weight : 'primary',
4902                 html : 'Yes'
4903             }
4904         ],
4905
4906         /**
4907          * Button config that displays OK and Cancel buttons
4908          * @type Object
4909          */
4910         OKCANCEL : [
4911             {
4912                name : 'cancel',
4913                 html : 'Cancel'
4914             },
4915             {
4916                 name : 'ok',
4917                 weight : 'primary',
4918                 html : 'OK'
4919             }
4920         ],
4921         /**
4922          * Button config that displays Yes, No and Cancel buttons
4923          * @type Object
4924          */
4925         YESNOCANCEL : [
4926             {
4927                 name : 'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             },
4931             {
4932                 name : 'no',
4933                 html : 'No'
4934             },
4935             {
4936                 name : 'cancel',
4937                 html : 'Cancel'
4938             }
4939         ],
4940         
4941         zIndex : 10001
4942 });
4943
4944 /*
4945  * - LGPL
4946  *
4947  * messagebox - can be used as a replace
4948  * 
4949  */
4950 /**
4951  * @class Roo.MessageBox
4952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4953  * Example usage:
4954  *<pre><code>
4955 // Basic alert:
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960     if (btn == 'ok'){
4961         // process text value...
4962     }
4963 });
4964
4965 // Show a dialog using config options:
4966 Roo.Msg.show({
4967    title:'Save Changes?',
4968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969    buttons: Roo.Msg.YESNOCANCEL,
4970    fn: processResult,
4971    animEl: 'elId'
4972 });
4973 </code></pre>
4974  * @singleton
4975  */
4976 Roo.bootstrap.MessageBox = function(){
4977     var dlg, opt, mask, waitTimer;
4978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979     var buttons, activeTextEl, bwidth;
4980
4981     
4982     // private
4983     var handleButton = function(button){
4984         dlg.hide();
4985         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4986     };
4987
4988     // private
4989     var handleHide = function(){
4990         if(opt && opt.cls){
4991             dlg.el.removeClass(opt.cls);
4992         }
4993         //if(waitTimer){
4994         //    Roo.TaskMgr.stop(waitTimer);
4995         //    waitTimer = null;
4996         //}
4997     };
4998
4999     // private
5000     var updateButtons = function(b){
5001         var width = 0;
5002         if(!b){
5003             buttons["ok"].hide();
5004             buttons["cancel"].hide();
5005             buttons["yes"].hide();
5006             buttons["no"].hide();
5007             dlg.footerEl.hide();
5008             
5009             return width;
5010         }
5011         dlg.footerEl.show();
5012         for(var k in buttons){
5013             if(typeof buttons[k] != "function"){
5014                 if(b[k]){
5015                     buttons[k].show();
5016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017                     width += buttons[k].el.getWidth()+15;
5018                 }else{
5019                     buttons[k].hide();
5020                 }
5021             }
5022         }
5023         return width;
5024     };
5025
5026     // private
5027     var handleEsc = function(d, k, e){
5028         if(opt && opt.closable !== false){
5029             dlg.hide();
5030         }
5031         if(e){
5032             e.stopEvent();
5033         }
5034     };
5035
5036     return {
5037         /**
5038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039          * @return {Roo.BasicDialog} The BasicDialog element
5040          */
5041         getDialog : function(){
5042            if(!dlg){
5043                 dlg = new Roo.bootstrap.Modal( {
5044                     //draggable: true,
5045                     //resizable:false,
5046                     //constraintoviewport:false,
5047                     //fixedcenter:true,
5048                     //collapsible : false,
5049                     //shim:true,
5050                     //modal: true,
5051                 //    width: 'auto',
5052                   //  height:100,
5053                     //buttonAlign:"center",
5054                     closeClick : function(){
5055                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5056                             handleButton("no");
5057                         }else{
5058                             handleButton("cancel");
5059                         }
5060                     }
5061                 });
5062                 dlg.render();
5063                 dlg.on("hide", handleHide);
5064                 mask = dlg.mask;
5065                 //dlg.addKeyListener(27, handleEsc);
5066                 buttons = {};
5067                 this.buttons = buttons;
5068                 var bt = this.buttonText;
5069                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073                 //Roo.log(buttons);
5074                 bodyEl = dlg.bodyEl.createChild({
5075
5076                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077                         '<textarea class="roo-mb-textarea"></textarea>' +
5078                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5079                 });
5080                 msgEl = bodyEl.dom.firstChild;
5081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082                 textboxEl.enableDisplayMode();
5083                 textboxEl.addKeyListener([10,13], function(){
5084                     if(dlg.isVisible() && opt && opt.buttons){
5085                         if(opt.buttons.ok){
5086                             handleButton("ok");
5087                         }else if(opt.buttons.yes){
5088                             handleButton("yes");
5089                         }
5090                     }
5091                 });
5092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093                 textareaEl.enableDisplayMode();
5094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095                 progressEl.enableDisplayMode();
5096                 
5097                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098                 var pf = progressEl.dom.firstChild;
5099                 if (pf) {
5100                     pp = Roo.get(pf.firstChild);
5101                     pp.setHeight(pf.offsetHeight);
5102                 }
5103                 
5104             }
5105             return dlg;
5106         },
5107
5108         /**
5109          * Updates the message box body text
5110          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111          * the XHTML-compliant non-breaking space character '&amp;#160;')
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         updateText : function(text)
5115         {
5116             if(!dlg.isVisible() && !opt.width){
5117                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119             }
5120             msgEl.innerHTML = text || '&#160;';
5121       
5122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124             var w = Math.max(
5125                     Math.min(opt.width || cw , this.maxWidth), 
5126                     Math.max(opt.minWidth || this.minWidth, bwidth)
5127             );
5128             if(opt.prompt){
5129                 activeTextEl.setWidth(w);
5130             }
5131             if(dlg.isVisible()){
5132                 dlg.fixedcenter = false;
5133             }
5134             // to big, make it scroll. = But as usual stupid IE does not support
5135             // !important..
5136             
5137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140             } else {
5141                 bodyEl.dom.style.height = '';
5142                 bodyEl.dom.style.overflowY = '';
5143             }
5144             if (cw > w) {
5145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.overflowX = '';
5148             }
5149             
5150             dlg.setContentSize(w, bodyEl.getHeight());
5151             if(dlg.isVisible()){
5152                 dlg.fixedcenter = true;
5153             }
5154             return this;
5155         },
5156
5157         /**
5158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         updateProgress : function(value, text){
5165             if(text){
5166                 this.updateText(text);
5167             }
5168             
5169             if (pp) { // weird bug on my firefox - for some reason this is not defined
5170                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172             }
5173             return this;
5174         },        
5175
5176         /**
5177          * Returns true if the message box is currently displayed
5178          * @return {Boolean} True if the message box is visible, else false
5179          */
5180         isVisible : function(){
5181             return dlg && dlg.isVisible();  
5182         },
5183
5184         /**
5185          * Hides the message box if it is displayed
5186          */
5187         hide : function(){
5188             if(this.isVisible()){
5189                 dlg.hide();
5190             }  
5191         },
5192
5193         /**
5194          * Displays a new message box, or reinitializes an existing message box, based on the config options
5195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196          * The following config object properties are supported:
5197          * <pre>
5198 Property    Type             Description
5199 ----------  ---------------  ------------------------------------------------------------------------------------
5200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5201                                    closes (defaults to undefined)
5202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5205                                    progress and wait dialogs will ignore this property and always hide the
5206                                    close button as they can only be closed programmatically.
5207 cls               String           A custom CSS class to apply to the message box element
5208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5209                                    displayed (defaults to 75)
5210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5211                                    function will be btn (the name of the button that was clicked, if applicable,
5212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5213                                    Progress and wait dialogs will ignore this option since they do not respond to
5214                                    user actions and can only be closed programmatically, so any required function
5215                                    should be called by the same code after it closes the dialog.
5216 icon              String           A CSS class that provides a background image to be used as an icon for
5217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5220 modal             Boolean          False to allow user interaction with the page while the message box is
5221                                    displayed (defaults to true)
5222 msg               String           A string that will replace the existing message box body text (defaults
5223                                    to the XHTML-compliant non-breaking space character '&#160;')
5224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5225 progress          Boolean          True to display a progress bar (defaults to false)
5226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5229 title             String           The title text
5230 value             String           The string value to set into the active textbox element if displayed
5231 wait              Boolean          True to display a progress bar (defaults to false)
5232 width             Number           The width of the dialog in pixels
5233 </pre>
5234          *
5235          * Example usage:
5236          * <pre><code>
5237 Roo.Msg.show({
5238    title: 'Address',
5239    msg: 'Please enter your address:',
5240    width: 300,
5241    buttons: Roo.MessageBox.OKCANCEL,
5242    multiline: true,
5243    fn: saveAddress,
5244    animEl: 'addAddressBtn'
5245 });
5246 </code></pre>
5247          * @param {Object} config Configuration options
5248          * @return {Roo.MessageBox} This message box
5249          */
5250         show : function(options)
5251         {
5252             
5253             // this causes nightmares if you show one dialog after another
5254             // especially on callbacks..
5255              
5256             if(this.isVisible()){
5257                 
5258                 this.hide();
5259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5261                 Roo.log("New Dialog Message:" +  options.msg )
5262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5264                 
5265             }
5266             var d = this.getDialog();
5267             opt = options;
5268             d.setTitle(opt.title || "&#160;");
5269             d.closeEl.setDisplayed(opt.closable !== false);
5270             activeTextEl = textboxEl;
5271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5272             if(opt.prompt){
5273                 if(opt.multiline){
5274                     textboxEl.hide();
5275                     textareaEl.show();
5276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5277                         opt.multiline : this.defaultTextHeight);
5278                     activeTextEl = textareaEl;
5279                 }else{
5280                     textboxEl.show();
5281                     textareaEl.hide();
5282                 }
5283             }else{
5284                 textboxEl.hide();
5285                 textareaEl.hide();
5286             }
5287             progressEl.setDisplayed(opt.progress === true);
5288             if (opt.progress) {
5289                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290             }
5291             this.updateProgress(0);
5292             activeTextEl.dom.value = opt.value || "";
5293             if(opt.prompt){
5294                 dlg.setDefaultButton(activeTextEl);
5295             }else{
5296                 var bs = opt.buttons;
5297                 var db = null;
5298                 if(bs && bs.ok){
5299                     db = buttons["ok"];
5300                 }else if(bs && bs.yes){
5301                     db = buttons["yes"];
5302                 }
5303                 dlg.setDefaultButton(db);
5304             }
5305             bwidth = updateButtons(opt.buttons);
5306             this.updateText(opt.msg);
5307             if(opt.cls){
5308                 d.el.addClass(opt.cls);
5309             }
5310             d.proxyDrag = opt.proxyDrag === true;
5311             d.modal = opt.modal !== false;
5312             d.mask = opt.modal !== false ? mask : false;
5313             if(!d.isVisible()){
5314                 // force it to the end of the z-index stack so it gets a cursor in FF
5315                 document.body.appendChild(dlg.el.dom);
5316                 d.animateTarget = null;
5317                 d.show(options.animEl);
5318             }
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325          * and closing the message box when the process is complete.
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @return {Roo.MessageBox} This message box
5329          */
5330         progress : function(title, msg){
5331             this.show({
5332                 title : title,
5333                 msg : msg,
5334                 buttons: false,
5335                 progress:true,
5336                 closable:false,
5337                 minWidth: this.minProgressWidth,
5338                 modal : true
5339             });
5340             return this;
5341         },
5342
5343         /**
5344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345          * If a callback function is passed it will be called after the user clicks the button, and the
5346          * id of the button that was clicked will be passed as the only parameter to the callback
5347          * (could also be the top-right close button).
5348          * @param {String} title The title bar text
5349          * @param {String} msg The message box body text
5350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351          * @param {Object} scope (optional) The scope of the callback function
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         alert : function(title, msg, fn, scope)
5355         {
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: this.OK,
5360                 fn: fn,
5361                 closable : false,
5362                 scope : scope,
5363                 modal : true
5364             });
5365             return this;
5366         },
5367
5368         /**
5369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371          * You are responsible for closing the message box when the process is complete.
5372          * @param {String} msg The message box body text
5373          * @param {String} title (optional) The title bar text
5374          * @return {Roo.MessageBox} This message box
5375          */
5376         wait : function(msg, title){
5377             this.show({
5378                 title : title,
5379                 msg : msg,
5380                 buttons: false,
5381                 closable:false,
5382                 progress:true,
5383                 modal:true,
5384                 width:300,
5385                 wait:true
5386             });
5387             waitTimer = Roo.TaskMgr.start({
5388                 run: function(i){
5389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5390                 },
5391                 interval: 1000
5392             });
5393             return this;
5394         },
5395
5396         /**
5397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400          * @param {String} title The title bar text
5401          * @param {String} msg The message box body text
5402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403          * @param {Object} scope (optional) The scope of the callback function
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         confirm : function(title, msg, fn, scope){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: this.YESNO,
5411                 fn: fn,
5412                 scope : scope,
5413                 modal : true
5414             });
5415             return this;
5416         },
5417
5418         /**
5419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422          * (could also be the top-right close button) and the text that was entered will be passed as the two
5423          * parameters to the callback.
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         prompt : function(title, msg, fn, scope, multiline){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.OKCANCEL,
5437                 fn: fn,
5438                 minWidth:250,
5439                 scope : scope,
5440                 prompt:true,
5441                 multiline: multiline,
5442                 modal : true
5443             });
5444             return this;
5445         },
5446
5447         /**
5448          * Button config that displays a single OK button
5449          * @type Object
5450          */
5451         OK : {ok:true},
5452         /**
5453          * Button config that displays Yes and No buttons
5454          * @type Object
5455          */
5456         YESNO : {yes:true, no:true},
5457         /**
5458          * Button config that displays OK and Cancel buttons
5459          * @type Object
5460          */
5461         OKCANCEL : {ok:true, cancel:true},
5462         /**
5463          * Button config that displays Yes, No and Cancel buttons
5464          * @type Object
5465          */
5466         YESNOCANCEL : {yes:true, no:true, cancel:true},
5467
5468         /**
5469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5470          * @type Number
5471          */
5472         defaultTextHeight : 75,
5473         /**
5474          * The maximum width in pixels of the message box (defaults to 600)
5475          * @type Number
5476          */
5477         maxWidth : 600,
5478         /**
5479          * The minimum width in pixels of the message box (defaults to 100)
5480          * @type Number
5481          */
5482         minWidth : 100,
5483         /**
5484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5486          * @type Number
5487          */
5488         minProgressWidth : 250,
5489         /**
5490          * An object containing the default button text strings that can be overriden for localized language support.
5491          * Supported properties are: ok, cancel, yes and no.
5492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5493          * @type Object
5494          */
5495         buttonText : {
5496             ok : "OK",
5497             cancel : "Cancel",
5498             yes : "Yes",
5499             no : "No"
5500         }
5501     };
5502 }();
5503
5504 /**
5505  * Shorthand for {@link Roo.MessageBox}
5506  */
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5509 /*
5510  * - LGPL
5511  *
5512  * navbar
5513  * 
5514  */
5515
5516 /**
5517  * @class Roo.bootstrap.Navbar
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Navbar class
5520
5521  * @constructor
5522  * Create a new Navbar
5523  * @param {Object} config The config object
5524  */
5525
5526
5527 Roo.bootstrap.Navbar = function(config){
5528     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5529     this.addEvents({
5530         // raw events
5531         /**
5532          * @event beforetoggle
5533          * Fire before toggle the menu
5534          * @param {Roo.EventObject} e
5535          */
5536         "beforetoggle" : true
5537     });
5538 };
5539
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5541     
5542     
5543    
5544     // private
5545     navItems : false,
5546     loadMask : false,
5547     
5548     
5549     getAutoCreate : function(){
5550         
5551         
5552         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5553         
5554     },
5555     
5556     initEvents :function ()
5557     {
5558         //Roo.log(this.el.select('.navbar-toggle',true));
5559         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560         
5561         var mark = {
5562             tag: "div",
5563             cls:"x-dlg-mask"
5564         };
5565         
5566         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567         
5568         var size = this.el.getSize();
5569         this.maskEl.setSize(size.width, size.height);
5570         this.maskEl.enableDisplayMode("block");
5571         this.maskEl.hide();
5572         
5573         if(this.loadMask){
5574             this.maskEl.show();
5575         }
5576     },
5577     
5578     
5579     getChildContainer : function()
5580     {
5581         if (this.el && this.el.select('.collapse').getCount()) {
5582             return this.el.select('.collapse',true).first();
5583         }
5584         
5585         return this.el;
5586     },
5587     
5588     mask : function()
5589     {
5590         this.maskEl.show();
5591     },
5592     
5593     unmask : function()
5594     {
5595         this.maskEl.hide();
5596     },
5597     onToggle : function()
5598     {
5599         
5600         if(this.fireEvent('beforetoggle', this) === false){
5601             return;
5602         }
5603         var ce = this.el.select('.navbar-collapse',true).first();
5604       
5605         if (!ce.hasClass('show')) {
5606            this.expand();
5607         } else {
5608             this.collapse();
5609         }
5610         
5611         
5612     
5613     },
5614     /**
5615      * Expand the navbar pulldown 
5616      */
5617     expand : function ()
5618     {
5619        
5620         var ce = this.el.select('.navbar-collapse',true).first();
5621         if (ce.hasClass('collapsing')) {
5622             return;
5623         }
5624         ce.dom.style.height = '';
5625                // show it...
5626         ce.addClass('in'); // old...
5627         ce.removeClass('collapse');
5628         ce.addClass('show');
5629         var h = ce.getHeight();
5630         Roo.log(h);
5631         ce.removeClass('show');
5632         // at this point we should be able to see it..
5633         ce.addClass('collapsing');
5634         
5635         ce.setHeight(0); // resize it ...
5636         ce.on('transitionend', function() {
5637             //Roo.log('done transition');
5638             ce.removeClass('collapsing');
5639             ce.addClass('show');
5640             ce.removeClass('collapse');
5641
5642             ce.dom.style.height = '';
5643         }, this, { single: true} );
5644         ce.setHeight(h);
5645         ce.dom.scrollTop = 0;
5646     },
5647     /**
5648      * Collapse the navbar pulldown 
5649      */
5650     collapse : function()
5651     {
5652          var ce = this.el.select('.navbar-collapse',true).first();
5653        
5654         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655             // it's collapsed or collapsing..
5656             return;
5657         }
5658         ce.removeClass('in'); // old...
5659         ce.setHeight(ce.getHeight());
5660         ce.removeClass('show');
5661         ce.addClass('collapsing');
5662         
5663         ce.on('transitionend', function() {
5664             ce.dom.style.height = '';
5665             ce.removeClass('collapsing');
5666             ce.addClass('collapse');
5667         }, this, { single: true} );
5668         ce.setHeight(0);
5669     }
5670     
5671     
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSimplebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  *
5691  * @cfg {Boolean} inverse is inverted color
5692  * 
5693  * @cfg {String} type (nav | pills | tabs)
5694  * @cfg {Boolean} arrangement stacked | justified
5695  * @cfg {String} align (left | right) alignment
5696  * 
5697  * @cfg {Boolean} main (true|false) main nav bar? default false
5698  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699  * 
5700  * @cfg {String} tag (header|footer|nav|div) default is nav 
5701
5702  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5703  * 
5704  * 
5705  * @constructor
5706  * Create a new Sidebar
5707  * @param {Object} config The config object
5708  */
5709
5710
5711 Roo.bootstrap.NavSimplebar = function(config){
5712     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5713 };
5714
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5716     
5717     inverse: false,
5718     
5719     type: false,
5720     arrangement: '',
5721     align : false,
5722     
5723     weight : 'light',
5724     
5725     main : false,
5726     
5727     
5728     tag : false,
5729     
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         var cfg = {
5735             tag : this.tag || 'div',
5736             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737         };
5738         if (['light','white'].indexOf(this.weight) > -1) {
5739             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740         }
5741         cfg.cls += ' bg-' + this.weight;
5742         
5743         if (this.inverse) {
5744             cfg.cls += ' navbar-inverse';
5745             
5746         }
5747         
5748         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749         
5750         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5751             return cfg;
5752         }
5753         
5754         
5755     
5756         
5757         cfg.cn = [
5758             {
5759                 cls: 'nav nav-' + this.xtype,
5760                 tag : 'ul'
5761             }
5762         ];
5763         
5764          
5765         this.type = this.type || 'nav';
5766         if (['tabs','pills'].indexOf(this.type) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.type
5768         
5769         
5770         } else {
5771             if (this.type!=='nav') {
5772                 Roo.log('nav type must be nav/tabs/pills')
5773             }
5774             cfg.cn[0].cls += ' navbar-nav'
5775         }
5776         
5777         
5778         
5779         
5780         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.arrangement;
5782         }
5783         
5784         
5785         if (this.align === 'right') {
5786             cfg.cn[0].cls += ' navbar-right';
5787         }
5788         
5789         
5790         
5791         
5792         return cfg;
5793     
5794         
5795     }
5796     
5797     
5798     
5799 });
5800
5801
5802
5803  
5804
5805  
5806        /*
5807  * - LGPL
5808  *
5809  * navbar
5810  * navbar-fixed-top
5811  * navbar-expand-md  fixed-top 
5812  */
5813
5814 /**
5815  * @class Roo.bootstrap.NavHeaderbar
5816  * @extends Roo.bootstrap.NavSimplebar
5817  * Bootstrap Sidebar class
5818  *
5819  * @cfg {String} brand what is brand
5820  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821  * @cfg {String} brand_href href of the brand
5822  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5823  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5826  * 
5827  * @constructor
5828  * Create a new Sidebar
5829  * @param {Object} config The config object
5830  */
5831
5832
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5835       
5836 };
5837
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5839     
5840     position: '',
5841     brand: '',
5842     brand_href: false,
5843     srButton : true,
5844     autohide : false,
5845     desktopCenter : false,
5846    
5847     
5848     getAutoCreate : function(){
5849         
5850         var   cfg = {
5851             tag: this.nav || 'nav',
5852             cls: 'navbar navbar-expand-md',
5853             role: 'navigation',
5854             cn: []
5855         };
5856         
5857         var cn = cfg.cn;
5858         if (this.desktopCenter) {
5859             cn.push({cls : 'container', cn : []});
5860             cn = cn[0].cn;
5861         }
5862         
5863         if(this.srButton){
5864             var btn = {
5865                 tag: 'button',
5866                 type: 'button',
5867                 cls: 'navbar-toggle navbar-toggler',
5868                 'data-toggle': 'collapse',
5869                 cn: [
5870                     {
5871                         tag: 'span',
5872                         cls: 'sr-only',
5873                         html: 'Toggle navigation'
5874                     },
5875                     {
5876                         tag: 'span',
5877                         cls: 'icon-bar navbar-toggler-icon'
5878                     },
5879                     {
5880                         tag: 'span',
5881                         cls: 'icon-bar'
5882                     },
5883                     {
5884                         tag: 'span',
5885                         cls: 'icon-bar'
5886                     }
5887                 ]
5888             };
5889             
5890             cn.push( Roo.bootstrap.version == 4 ? btn : {
5891                 tag: 'div',
5892                 cls: 'navbar-header',
5893                 cn: [
5894                     btn
5895                 ]
5896             });
5897         }
5898         
5899         cn.push({
5900             tag: 'div',
5901             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5902             cn : []
5903         });
5904         
5905         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906         
5907         if (['light','white'].indexOf(this.weight) > -1) {
5908             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909         }
5910         cfg.cls += ' bg-' + this.weight;
5911         
5912         
5913         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915             
5916             // tag can override this..
5917             
5918             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5919         }
5920         
5921         if (this.brand !== '') {
5922             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924                 tag: 'a',
5925                 href: this.brand_href ? this.brand_href : '#',
5926                 cls: 'navbar-brand',
5927                 cn: [
5928                 this.brand
5929                 ]
5930             });
5931         }
5932         
5933         if(this.main){
5934             cfg.cls += ' main-nav';
5935         }
5936         
5937         
5938         return cfg;
5939
5940         
5941     },
5942     getHeaderChildContainer : function()
5943     {
5944         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945             return this.el.select('.navbar-header',true).first();
5946         }
5947         
5948         return this.getChildContainer();
5949     },
5950     
5951     getChildContainer : function()
5952     {
5953          
5954         return this.el.select('.roo-navbar-collapse',true).first();
5955          
5956         
5957     },
5958     
5959     initEvents : function()
5960     {
5961         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962         
5963         if (this.autohide) {
5964             
5965             var prevScroll = 0;
5966             var ft = this.el;
5967             
5968             Roo.get(document).on('scroll',function(e) {
5969                 var ns = Roo.get(document).getScroll().top;
5970                 var os = prevScroll;
5971                 prevScroll = ns;
5972                 
5973                 if(ns > os){
5974                     ft.removeClass('slideDown');
5975                     ft.addClass('slideUp');
5976                     return;
5977                 }
5978                 ft.removeClass('slideUp');
5979                 ft.addClass('slideDown');
5980                  
5981               
5982           },this);
5983         }
5984     }    
5985     
5986 });
5987
5988
5989
5990  
5991
5992  /*
5993  * - LGPL
5994  *
5995  * navbar
5996  * 
5997  */
5998
5999 /**
6000  * @class Roo.bootstrap.NavSidebar
6001  * @extends Roo.bootstrap.Navbar
6002  * Bootstrap Sidebar class
6003  * 
6004  * @constructor
6005  * Create a new Sidebar
6006  * @param {Object} config The config object
6007  */
6008
6009
6010 Roo.bootstrap.NavSidebar = function(config){
6011     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6012 };
6013
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6015     
6016     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017     
6018     getAutoCreate : function(){
6019         
6020         
6021         return  {
6022             tag: 'div',
6023             cls: 'sidebar sidebar-nav'
6024         };
6025     
6026         
6027     }
6028     
6029     
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * nav group
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavGroup
6046  * @extends Roo.bootstrap.Component
6047  * Bootstrap NavGroup class
6048  * @cfg {String} align (left|right)
6049  * @cfg {Boolean} inverse
6050  * @cfg {String} type (nav|pills|tab) default nav
6051  * @cfg {String} navId - reference Id for navbar.
6052  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6053  * 
6054  * @constructor
6055  * Create a new nav group
6056  * @param {Object} config The config object
6057  */
6058
6059 Roo.bootstrap.NavGroup = function(config){
6060     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6061     this.navItems = [];
6062    
6063     Roo.bootstrap.NavGroup.register(this);
6064      this.addEvents({
6065         /**
6066              * @event changed
6067              * Fires when the active item changes
6068              * @param {Roo.bootstrap.NavGroup} this
6069              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6071          */
6072         'changed': true
6073      });
6074     
6075 };
6076
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6078     
6079     align: '',
6080     inverse: false,
6081     form: false,
6082     type: 'nav',
6083     navId : '',
6084     // private
6085     pilltype : true,
6086     
6087     navItems : false, 
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag : 'ul',
6095             cls: 'nav' 
6096         };
6097         if (Roo.bootstrap.version == 4) {
6098             if (['tabs','pills'].indexOf(this.type) != -1) {
6099                 cfg.cls += ' nav-' + this.type; 
6100             } else {
6101                 // trying to remove so header bar can right align top?
6102                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103                     // do not use on header bar... 
6104                     cfg.cls += ' navbar-nav';
6105                 }
6106             }
6107             
6108         } else {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type
6111             } else {
6112                 if (this.type !== 'nav') {
6113                     Roo.log('nav type must be nav/tabs/pills')
6114                 }
6115                 cfg.cls += ' navbar-nav'
6116             }
6117         }
6118         
6119         if (this.parent() && this.parent().sidebar) {
6120             cfg = {
6121                 tag: 'ul',
6122                 cls: 'dashboard-menu sidebar-menu'
6123             };
6124             
6125             return cfg;
6126         }
6127         
6128         if (this.form === true) {
6129             cfg = {
6130                 tag: 'form',
6131                 cls: 'navbar-form form-inline'
6132             };
6133             //nav navbar-right ml-md-auto
6134             if (this.align === 'right') {
6135                 cfg.cls += ' navbar-right ml-md-auto';
6136             } else {
6137                 cfg.cls += ' navbar-left';
6138             }
6139         }
6140         
6141         if (this.align === 'right') {
6142             cfg.cls += ' navbar-right ml-md-auto';
6143         } else {
6144             cfg.cls += ' mr-auto';
6145         }
6146         
6147         if (this.inverse) {
6148             cfg.cls += ' navbar-inverse';
6149             
6150         }
6151         
6152         
6153         return cfg;
6154     },
6155     /**
6156     * sets the active Navigation item
6157     * @param {Roo.bootstrap.NavItem} the new current navitem
6158     */
6159     setActiveItem : function(item)
6160     {
6161         var prev = false;
6162         Roo.each(this.navItems, function(v){
6163             if (v == item) {
6164                 return ;
6165             }
6166             if (v.isActive()) {
6167                 v.setActive(false, true);
6168                 prev = v;
6169                 
6170             }
6171             
6172         });
6173
6174         item.setActive(true, true);
6175         this.fireEvent('changed', this, item, prev);
6176         
6177         
6178     },
6179     /**
6180     * gets the active Navigation item
6181     * @return {Roo.bootstrap.NavItem} the current navitem
6182     */
6183     getActive : function()
6184     {
6185         
6186         var prev = false;
6187         Roo.each(this.navItems, function(v){
6188             
6189             if (v.isActive()) {
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195         return prev;
6196     },
6197     
6198     indexOfNav : function()
6199     {
6200         
6201         var prev = false;
6202         Roo.each(this.navItems, function(v,i){
6203             
6204             if (v.isActive()) {
6205                 prev = i;
6206                 
6207             }
6208             
6209         });
6210         return prev;
6211     },
6212     /**
6213     * adds a Navigation item
6214     * @param {Roo.bootstrap.NavItem} the navitem to add
6215     */
6216     addItem : function(cfg)
6217     {
6218         if (this.form && Roo.bootstrap.version == 4) {
6219             cfg.tag = 'div';
6220         }
6221         var cn = new Roo.bootstrap.NavItem(cfg);
6222         this.register(cn);
6223         cn.parentId = this.id;
6224         cn.onRender(this.el, null);
6225         return cn;
6226     },
6227     /**
6228     * register a Navigation item
6229     * @param {Roo.bootstrap.NavItem} the navitem to add
6230     */
6231     register : function(item)
6232     {
6233         this.navItems.push( item);
6234         item.navId = this.navId;
6235     
6236     },
6237     
6238     /**
6239     * clear all the Navigation item
6240     */
6241    
6242     clearAll : function()
6243     {
6244         this.navItems = [];
6245         this.el.dom.innerHTML = '';
6246     },
6247     
6248     getNavItem: function(tabId)
6249     {
6250         var ret = false;
6251         Roo.each(this.navItems, function(e) {
6252             if (e.tabId == tabId) {
6253                ret =  e;
6254                return false;
6255             }
6256             return true;
6257             
6258         });
6259         return ret;
6260     },
6261     
6262     setActiveNext : function()
6263     {
6264         var i = this.indexOfNav(this.getActive());
6265         if (i > this.navItems.length) {
6266             return;
6267         }
6268         this.setActiveItem(this.navItems[i+1]);
6269     },
6270     setActivePrev : function()
6271     {
6272         var i = this.indexOfNav(this.getActive());
6273         if (i  < 1) {
6274             return;
6275         }
6276         this.setActiveItem(this.navItems[i-1]);
6277     },
6278     clearWasActive : function(except) {
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId != except.tabId && e.was_active) {
6281                e.was_active = false;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287     },
6288     getWasActive : function ()
6289     {
6290         var r = false;
6291         Roo.each(this.navItems, function(e) {
6292             if (e.was_active) {
6293                r = e;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299         return r;
6300     }
6301     
6302     
6303 });
6304
6305  
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6307     
6308     groups: {},
6309      /**
6310     * register a Navigation Group
6311     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312     */
6313     register : function(navgrp)
6314     {
6315         this.groups[navgrp.navId] = navgrp;
6316         
6317     },
6318     /**
6319     * fetch a Navigation Group based on the navigation ID
6320     * @param {string} the navgroup to add
6321     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6322     */
6323     get: function(navId) {
6324         if (typeof(this.groups[navId]) == 'undefined') {
6325             return false;
6326             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327         }
6328         return this.groups[navId] ;
6329     }
6330     
6331     
6332     
6333 });
6334
6335  /*
6336  * - LGPL
6337  *
6338  * row
6339  * 
6340  */
6341
6342 /**
6343  * @class Roo.bootstrap.NavItem
6344  * @extends Roo.bootstrap.Component
6345  * Bootstrap Navbar.NavItem class
6346  * @cfg {String} href  link to
6347  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348  * @cfg {Boolean} button_outline show and outlined button
6349  * @cfg {String} html content of button
6350  * @cfg {String} badge text inside badge
6351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352  * @cfg {String} glyphicon DEPRICATED - use fa
6353  * @cfg {String} icon DEPRICATED - use fa
6354  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355  * @cfg {Boolean} active Is item active
6356  * @cfg {Boolean} disabled Is item disabled
6357  * @cfg {String} linkcls  Link Class
6358  * @cfg {Boolean} preventDefault (true | false) default false
6359  * @cfg {String} tabId the tab that this item activates.
6360  * @cfg {String} tagtype (a|span) render as a href or span?
6361  * @cfg {Boolean} animateRef (true|false) link to element default false  
6362   
6363  * @constructor
6364  * Create a new Navbar Item
6365  * @param {Object} config The config object
6366  */
6367 Roo.bootstrap.NavItem = function(config){
6368     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6369     this.addEvents({
6370         // raw events
6371         /**
6372          * @event click
6373          * The raw click event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "click" : true,
6377          /**
6378             * @event changed
6379             * Fires when the active item active state changes
6380             * @param {Roo.bootstrap.NavItem} this
6381             * @param {boolean} state the new state
6382              
6383          */
6384         'changed': true,
6385         /**
6386             * @event scrollto
6387             * Fires when scroll to element
6388             * @param {Roo.bootstrap.NavItem} this
6389             * @param {Object} options
6390             * @param {Roo.EventObject} e
6391              
6392          */
6393         'scrollto': true
6394     });
6395    
6396 };
6397
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6399     
6400     href: false,
6401     html: '',
6402     badge: '',
6403     icon: false,
6404     fa : false,
6405     glyphicon: false,
6406     active: false,
6407     preventDefault : false,
6408     tabId : false,
6409     tagtype : 'a',
6410     tag: 'li',
6411     disabled : false,
6412     animateRef : false,
6413     was_active : false,
6414     button_weight : '',
6415     button_outline : false,
6416     linkcls : '',
6417     navLink: false,
6418     
6419     getAutoCreate : function(){
6420          
6421         var cfg = {
6422             tag: this.tag,
6423             cls: 'nav-item'
6424         };
6425         
6426         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6427         
6428         if (this.active) {
6429             cfg.cls +=  ' active' ;
6430         }
6431         if (this.disabled) {
6432             cfg.cls += ' disabled';
6433         }
6434         
6435         // BS4 only?
6436         if (this.button_weight.length) {
6437             cfg.tag = this.href ? 'a' : 'button';
6438             cfg.html = this.html || '';
6439             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440             if (this.href) {
6441                 cfg.href = this.href;
6442             }
6443             if (this.fa) {
6444                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445             } else {
6446                 cfg.cls += " nav-html";
6447             }
6448             
6449             // menu .. should add dropdown-menu class - so no need for carat..
6450             
6451             if (this.badge !== '') {
6452                  
6453                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6454             }
6455             return cfg;
6456         }
6457         
6458         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6459             cfg.cn = [
6460                 {
6461                     tag: this.tagtype,
6462                     href : this.href || "#",
6463                     html: this.html || '',
6464                     cls : ''
6465                 }
6466             ];
6467             if (this.tagtype == 'a') {
6468                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6469         
6470             }
6471             if (this.icon) {
6472                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473             } else  if (this.fa) {
6474                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else if(this.glyphicon) {
6476                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6477             } else {
6478                 cfg.cn[0].cls += " nav-html";
6479             }
6480             
6481             if (this.menu) {
6482                 cfg.cn[0].html += " <span class='caret'></span>";
6483              
6484             }
6485             
6486             if (this.badge !== '') {
6487                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6488             }
6489         }
6490         
6491         
6492         
6493         return cfg;
6494     },
6495     onRender : function(ct, position)
6496     {
6497        // Roo.log("Call onRender: " + this.xtype);
6498         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6499             this.tag = 'div';
6500         }
6501         
6502         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503         this.navLink = this.el.select('.nav-link',true).first();
6504         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6505         return ret;
6506     },
6507       
6508     
6509     initEvents: function() 
6510     {
6511         if (typeof (this.menu) != 'undefined') {
6512             this.menu.parentType = this.xtype;
6513             this.menu.triggerEl = this.el;
6514             this.menu = this.addxtype(Roo.apply({}, this.menu));
6515         }
6516         
6517         this.el.on('click', this.onClick, this);
6518         
6519         //if(this.tagtype == 'span'){
6520         //    this.el.select('span',true).on('click', this.onClick, this);
6521         //}
6522        
6523         // at this point parent should be available..
6524         this.parent().register(this);
6525     },
6526     
6527     onClick : function(e)
6528     {
6529         if (e.getTarget('.dropdown-menu-item')) {
6530             // did you click on a menu itemm.... - then don't trigger onclick..
6531             return;
6532         }
6533         
6534         if(
6535                 this.preventDefault || 
6536                 this.href == '#' 
6537         ){
6538             Roo.log("NavItem - prevent Default?");
6539             e.preventDefault();
6540         }
6541         
6542         if (this.disabled) {
6543             return;
6544         }
6545         
6546         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547         if (tg && tg.transition) {
6548             Roo.log("waiting for the transitionend");
6549             return;
6550         }
6551         
6552         
6553         
6554         //Roo.log("fire event clicked");
6555         if(this.fireEvent('click', this, e) === false){
6556             return;
6557         };
6558         
6559         if(this.tagtype == 'span'){
6560             return;
6561         }
6562         
6563         //Roo.log(this.href);
6564         var ael = this.el.select('a',true).first();
6565         //Roo.log(ael);
6566         
6567         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570                 return; // ignore... - it's a 'hash' to another page.
6571             }
6572             Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574             this.scrollToElement(e);
6575         }
6576         
6577         
6578         var p =  this.parent();
6579    
6580         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581             if (typeof(p.setActiveItem) !== 'undefined') {
6582                 p.setActiveItem(this);
6583             }
6584         }
6585         
6586         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588             // remove the collapsed menu expand...
6589             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6590         }
6591     },
6592     
6593     isActive: function () {
6594         return this.active
6595     },
6596     setActive : function(state, fire, is_was_active)
6597     {
6598         if (this.active && !state && this.navId) {
6599             this.was_active = true;
6600             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601             if (nv) {
6602                 nv.clearWasActive(this);
6603             }
6604             
6605         }
6606         this.active = state;
6607         
6608         if (!state ) {
6609             this.el.removeClass('active');
6610             this.navLink ? this.navLink.removeClass('active') : false;
6611         } else if (!this.el.hasClass('active')) {
6612             
6613             this.el.addClass('active');
6614             if (Roo.bootstrap.version == 4 && this.navLink ) {
6615                 this.navLink.addClass('active');
6616             }
6617             
6618         }
6619         if (fire) {
6620             this.fireEvent('changed', this, state);
6621         }
6622         
6623         // show a panel if it's registered and related..
6624         
6625         if (!this.navId || !this.tabId || !state || is_was_active) {
6626             return;
6627         }
6628         
6629         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6630         if (!tg) {
6631             return;
6632         }
6633         var pan = tg.getPanelByName(this.tabId);
6634         if (!pan) {
6635             return;
6636         }
6637         // if we can not flip to new panel - go back to old nav highlight..
6638         if (false == tg.showPanel(pan)) {
6639             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640             if (nv) {
6641                 var onav = nv.getWasActive();
6642                 if (onav) {
6643                     onav.setActive(true, false, true);
6644                 }
6645             }
6646             
6647         }
6648         
6649         
6650         
6651     },
6652      // this should not be here...
6653     setDisabled : function(state)
6654     {
6655         this.disabled = state;
6656         if (!state ) {
6657             this.el.removeClass('disabled');
6658         } else if (!this.el.hasClass('disabled')) {
6659             this.el.addClass('disabled');
6660         }
6661         
6662     },
6663     
6664     /**
6665      * Fetch the element to display the tooltip on.
6666      * @return {Roo.Element} defaults to this.el
6667      */
6668     tooltipEl : function()
6669     {
6670         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6671     },
6672     
6673     scrollToElement : function(e)
6674     {
6675         var c = document.body;
6676         
6677         /*
6678          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679          */
6680         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681             c = document.documentElement;
6682         }
6683         
6684         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685         
6686         if(!target){
6687             return;
6688         }
6689
6690         var o = target.calcOffsetsTo(c);
6691         
6692         var options = {
6693             target : target,
6694             value : o[1]
6695         };
6696         
6697         this.fireEvent('scrollto', this, options, e);
6698         
6699         Roo.get(c).scrollTo('top', options.value, true);
6700         
6701         return;
6702     },
6703     /**
6704      * Set the HTML (text content) of the item
6705      * @param {string} html  content for the nav item
6706      */
6707     setHtml : function(html)
6708     {
6709         this.html = html;
6710         this.htmlEl.dom.innerHTML = html;
6711         
6712     } 
6713 });
6714  
6715
6716  /*
6717  * - LGPL
6718  *
6719  * sidebar item
6720  *
6721  *  li
6722  *    <span> icon </span>
6723  *    <span> text </span>
6724  *    <span>badge </span>
6725  */
6726
6727 /**
6728  * @class Roo.bootstrap.NavSidebarItem
6729  * @extends Roo.bootstrap.NavItem
6730  * Bootstrap Navbar.NavSidebarItem class
6731  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732  * {Boolean} open is the menu open
6733  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735  * {String} buttonSize (sm|md|lg)the extra classes for the button
6736  * {Boolean} showArrow show arrow next to the text (default true)
6737  * @constructor
6738  * Create a new Navbar Button
6739  * @param {Object} config The config object
6740  */
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6743     this.addEvents({
6744         // raw events
6745         /**
6746          * @event click
6747          * The raw click event for the entire grid.
6748          * @param {Roo.EventObject} e
6749          */
6750         "click" : true,
6751          /**
6752             * @event changed
6753             * Fires when the active item active state changes
6754             * @param {Roo.bootstrap.NavSidebarItem} this
6755             * @param {boolean} state the new state
6756              
6757          */
6758         'changed': true
6759     });
6760    
6761 };
6762
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6764     
6765     badgeWeight : 'default',
6766     
6767     open: false,
6768     
6769     buttonView : false,
6770     
6771     buttonWeight : 'default',
6772     
6773     buttonSize : 'md',
6774     
6775     showArrow : true,
6776     
6777     getAutoCreate : function(){
6778         
6779         
6780         var a = {
6781                 tag: 'a',
6782                 href : this.href || '#',
6783                 cls: '',
6784                 html : '',
6785                 cn : []
6786         };
6787         
6788         if(this.buttonView){
6789             a = {
6790                 tag: 'button',
6791                 href : this.href || '#',
6792                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6793                 html : this.html,
6794                 cn : []
6795             };
6796         }
6797         
6798         var cfg = {
6799             tag: 'li',
6800             cls: '',
6801             cn: [ a ]
6802         };
6803         
6804         if (this.active) {
6805             cfg.cls += ' active';
6806         }
6807         
6808         if (this.disabled) {
6809             cfg.cls += ' disabled';
6810         }
6811         if (this.open) {
6812             cfg.cls += ' open x-open';
6813         }
6814         // left icon..
6815         if (this.glyphicon || this.icon) {
6816             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6817             a.cn.push({ tag : 'i', cls : c }) ;
6818         }
6819         
6820         if(!this.buttonView){
6821             var span = {
6822                 tag: 'span',
6823                 html : this.html || ''
6824             };
6825
6826             a.cn.push(span);
6827             
6828         }
6829         
6830         if (this.badge !== '') {
6831             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6832         }
6833         
6834         if (this.menu) {
6835             
6836             if(this.showArrow){
6837                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6838             }
6839             
6840             a.cls += ' dropdown-toggle treeview' ;
6841         }
6842         
6843         return cfg;
6844     },
6845     
6846     initEvents : function()
6847     { 
6848         if (typeof (this.menu) != 'undefined') {
6849             this.menu.parentType = this.xtype;
6850             this.menu.triggerEl = this.el;
6851             this.menu = this.addxtype(Roo.apply({}, this.menu));
6852         }
6853         
6854         this.el.on('click', this.onClick, this);
6855         
6856         if(this.badge !== ''){
6857             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6858         }
6859         
6860     },
6861     
6862     onClick : function(e)
6863     {
6864         if(this.disabled){
6865             e.preventDefault();
6866             return;
6867         }
6868         
6869         if(this.preventDefault){
6870             e.preventDefault();
6871         }
6872         
6873         this.fireEvent('click', this, e);
6874     },
6875     
6876     disable : function()
6877     {
6878         this.setDisabled(true);
6879     },
6880     
6881     enable : function()
6882     {
6883         this.setDisabled(false);
6884     },
6885     
6886     setDisabled : function(state)
6887     {
6888         if(this.disabled == state){
6889             return;
6890         }
6891         
6892         this.disabled = state;
6893         
6894         if (state) {
6895             this.el.addClass('disabled');
6896             return;
6897         }
6898         
6899         this.el.removeClass('disabled');
6900         
6901         return;
6902     },
6903     
6904     setActive : function(state)
6905     {
6906         if(this.active == state){
6907             return;
6908         }
6909         
6910         this.active = state;
6911         
6912         if (state) {
6913             this.el.addClass('active');
6914             return;
6915         }
6916         
6917         this.el.removeClass('active');
6918         
6919         return;
6920     },
6921     
6922     isActive: function () 
6923     {
6924         return this.active;
6925     },
6926     
6927     setBadge : function(str)
6928     {
6929         if(!this.badgeEl){
6930             return;
6931         }
6932         
6933         this.badgeEl.dom.innerHTML = str;
6934     }
6935     
6936    
6937      
6938  
6939 });
6940  
6941
6942  /*
6943  * - LGPL
6944  *
6945  *  Breadcrumb Nav
6946  * 
6947  */
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6949
6950
6951 /**
6952  * @class Roo.bootstrap.breadcrumb.Nav
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Breadcrumb Nav Class
6955  *  
6956  * @children Roo.bootstrap.breadcrumb.Item
6957  * 
6958  * @constructor
6959  * Create a new breadcrumb.Nav
6960  * @param {Object} config The config object
6961  */
6962
6963
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6966     
6967     
6968 };
6969
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'nav',
6977             cn : [
6978                 {
6979                     tag : 'ol',
6980                     cls : 'breadcrumb'
6981                 }
6982             ]
6983             
6984         };
6985           
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         this.olEl = this.el.select('ol',true).first();    
6992     },
6993     getChildContainer : function()
6994     {
6995         return this.olEl;  
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  *  Breadcrumb Item
7004  * 
7005  */
7006
7007
7008 /**
7009  * @class Roo.bootstrap.breadcrumb.Nav
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap Breadcrumb Nav Class
7012  *  
7013  * @children Roo.bootstrap.breadcrumb.Component
7014  * @cfg {String} html the content of the link.
7015  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016  * @cfg {Boolean} active is it active
7017
7018  * 
7019  * @constructor
7020  * Create a new breadcrumb.Nav
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7026     this.addEvents({
7027         // img events
7028         /**
7029          * @event click
7030          * The img click event for the img.
7031          * @param {Roo.EventObject} e
7032          */
7033         "click" : true
7034     });
7035     
7036 };
7037
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7039     
7040     href: false,
7041     html : '',
7042     
7043     getAutoCreate : function()
7044     {
7045
7046         var cfg = {
7047             tag: 'li',
7048             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049         };
7050         if (this.href !== false) {
7051             cfg.cn = [{
7052                 tag : 'a',
7053                 href : this.href,
7054                 html : this.html
7055             }];
7056         } else {
7057             cfg.html = this.html;
7058         }
7059         
7060         return cfg;
7061     },
7062     
7063     initEvents: function()
7064     {
7065         if (this.href) {
7066             this.el.select('a', true).first().on('click',this.onClick, this)
7067         }
7068         
7069     },
7070     onClick : function(e)
7071     {
7072         e.preventDefault();
7073         this.fireEvent('click',this,  e);
7074     }
7075     
7076 });
7077
7078  /*
7079  * - LGPL
7080  *
7081  * row
7082  * 
7083  */
7084
7085 /**
7086  * @class Roo.bootstrap.Row
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap Row class (contains columns...)
7089  * 
7090  * @constructor
7091  * Create a new Row
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Row = function(config){
7096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7100     
7101     getAutoCreate : function(){
7102        return {
7103             cls: 'row clearfix'
7104        };
7105     }
7106     
7107     
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * pagination
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.Pagination
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap Pagination class
7123  * @cfg {String} size xs | sm | md | lg
7124  * @cfg {Boolean} inverse false | true
7125  * 
7126  * @constructor
7127  * Create a new Pagination
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.Pagination = function(config){
7132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     size: false,
7139     inverse: false,
7140     
7141     getAutoCreate : function(){
7142         var cfg = {
7143             tag: 'ul',
7144                 cls: 'pagination'
7145         };
7146         if (this.inverse) {
7147             cfg.cls += ' inverse';
7148         }
7149         if (this.html) {
7150             cfg.html=this.html;
7151         }
7152         if (this.cls) {
7153             cfg.cls += " " + this.cls;
7154         }
7155         return cfg;
7156     }
7157    
7158 });
7159
7160  
7161
7162  /*
7163  * - LGPL
7164  *
7165  * Pagination item
7166  * 
7167  */
7168
7169
7170 /**
7171  * @class Roo.bootstrap.PaginationItem
7172  * @extends Roo.bootstrap.Component
7173  * Bootstrap PaginationItem class
7174  * @cfg {String} html text
7175  * @cfg {String} href the link
7176  * @cfg {Boolean} preventDefault (true | false) default true
7177  * @cfg {Boolean} active (true | false) default false
7178  * @cfg {Boolean} disabled default false
7179  * 
7180  * 
7181  * @constructor
7182  * Create a new PaginationItem
7183  * @param {Object} config The config object
7184  */
7185
7186
7187 Roo.bootstrap.PaginationItem = function(config){
7188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7189     this.addEvents({
7190         // raw events
7191         /**
7192          * @event click
7193          * The raw click event for the entire grid.
7194          * @param {Roo.EventObject} e
7195          */
7196         "click" : true
7197     });
7198 };
7199
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7201     
7202     href : false,
7203     html : false,
7204     preventDefault: true,
7205     active : false,
7206     cls : false,
7207     disabled: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg= {
7211             tag: 'li',
7212             cn: [
7213                 {
7214                     tag : 'a',
7215                     href : this.href ? this.href : '#',
7216                     html : this.html ? this.html : ''
7217                 }
7218             ]
7219         };
7220         
7221         if(this.cls){
7222             cfg.cls = this.cls;
7223         }
7224         
7225         if(this.disabled){
7226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7227         }
7228         
7229         if(this.active){
7230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231         }
7232         
7233         return cfg;
7234     },
7235     
7236     initEvents: function() {
7237         
7238         this.el.on('click', this.onClick, this);
7239         
7240     },
7241     onClick : function(e)
7242     {
7243         Roo.log('PaginationItem on click ');
7244         if(this.preventDefault){
7245             e.preventDefault();
7246         }
7247         
7248         if(this.disabled){
7249             return;
7250         }
7251         
7252         this.fireEvent('click', this, e);
7253     }
7254    
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * slider
7263  * 
7264  */
7265
7266
7267 /**
7268  * @class Roo.bootstrap.Slider
7269  * @extends Roo.bootstrap.Component
7270  * Bootstrap Slider class
7271  *    
7272  * @constructor
7273  * Create a new Slider
7274  * @param {Object} config The config object
7275  */
7276
7277 Roo.bootstrap.Slider = function(config){
7278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7279 };
7280
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7282     
7283     getAutoCreate : function(){
7284         
7285         var cfg = {
7286             tag: 'div',
7287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7288             cn: [
7289                 {
7290                     tag: 'a',
7291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7292                 }
7293             ]
7294         };
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  /*
7302  * Based on:
7303  * Ext JS Library 1.1.1
7304  * Copyright(c) 2006-2007, Ext JS, LLC.
7305  *
7306  * Originally Released Under LGPL - original licence link has changed is not relivant.
7307  *
7308  * Fork - LGPL
7309  * <script type="text/javascript">
7310  */
7311  /**
7312  * @extends Roo.dd.DDProxy
7313  * @class Roo.grid.SplitDragZone
7314  * Support for Column Header resizing
7315  * @constructor
7316  * @param {Object} config
7317  */
7318 // private
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321     this.grid = grid;
7322     this.view = grid.getView();
7323     this.proxy = this.view.resizeProxy;
7324     Roo.grid.SplitDragZone.superclass.constructor.call(
7325         this,
7326         hd, // ID
7327         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328         {  // CONFIG
7329             dragElId : Roo.id(this.proxy.dom),
7330             resizeFrame:false
7331         }
7332     );
7333     
7334     this.setHandleElId(Roo.id(hd));
7335     if (hd2 !== false) {
7336         this.setOuterHandleElId(Roo.id(hd2));
7337     }
7338     
7339     this.scroll = false;
7340 };
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342     fly: Roo.Element.fly,
7343
7344     b4StartDrag : function(x, y){
7345         this.view.headersDisabled = true;
7346         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348         );
7349         this.proxy.setHeight(h);
7350         
7351         // for old system colWidth really stored the actual width?
7352         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353         // which in reality did not work.. - it worked only for fixed sizes
7354         // for resizable we need to use actual sizes.
7355         var w = this.cm.getColumnWidth(this.cellIndex);
7356         if (!this.view.mainWrap) {
7357             // bootstrap.
7358             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7359         }
7360         
7361         
7362         
7363         // this was w-this.grid.minColumnWidth;
7364         // doesnt really make sense? - w = thie curren width or the rendered one?
7365         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366         this.resetConstraints();
7367         this.setXConstraint(minw, 1000);
7368         this.setYConstraint(0, 0);
7369         this.minX = x - minw;
7370         this.maxX = x + 1000;
7371         this.startPos = x;
7372         if (!this.view.mainWrap) { // this is Bootstrap code..
7373             this.getDragEl().style.display='block';
7374         }
7375         
7376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7377     },
7378
7379
7380     handleMouseDown : function(e){
7381         ev = Roo.EventObject.setEvent(e);
7382         var t = this.fly(ev.getTarget());
7383         if(t.hasClass("x-grid-split")){
7384             this.cellIndex = this.view.getCellIndex(t.dom);
7385             this.split = t.dom;
7386             this.cm = this.grid.colModel;
7387             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7389             }
7390         }
7391     },
7392
7393     endDrag : function(e){
7394         this.view.headersDisabled = false;
7395         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396         var diff = endX - this.startPos;
7397         // 
7398         var w = this.cm.getColumnWidth(this.cellIndex);
7399         if (!this.view.mainWrap) {
7400             w = 0;
7401         }
7402         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7403     },
7404
7405     autoOffset : function(){
7406         this.setDelta(0,0);
7407     }
7408 });/*
7409  * Based on:
7410  * Ext JS Library 1.1.1
7411  * Copyright(c) 2006-2007, Ext JS, LLC.
7412  *
7413  * Originally Released Under LGPL - original licence link has changed is not relivant.
7414  *
7415  * Fork - LGPL
7416  * <script type="text/javascript">
7417  */
7418
7419 /**
7420  * @class Roo.grid.AbstractSelectionModel
7421  * @extends Roo.util.Observable
7422  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7423  * implemented by descendant classes.  This class should not be directly instantiated.
7424  * @constructor
7425  */
7426 Roo.grid.AbstractSelectionModel = function(){
7427     this.locked = false;
7428     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7429 };
7430
7431 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7432     /** @ignore Called by the grid automatically. Do not call directly. */
7433     init : function(grid){
7434         this.grid = grid;
7435         this.initEvents();
7436     },
7437
7438     /**
7439      * Locks the selections.
7440      */
7441     lock : function(){
7442         this.locked = true;
7443     },
7444
7445     /**
7446      * Unlocks the selections.
7447      */
7448     unlock : function(){
7449         this.locked = false;
7450     },
7451
7452     /**
7453      * Returns true if the selections are locked.
7454      * @return {Boolean}
7455      */
7456     isLocked : function(){
7457         return this.locked;
7458     }
7459 });/*
7460  * Based on:
7461  * Ext JS Library 1.1.1
7462  * Copyright(c) 2006-2007, Ext JS, LLC.
7463  *
7464  * Originally Released Under LGPL - original licence link has changed is not relivant.
7465  *
7466  * Fork - LGPL
7467  * <script type="text/javascript">
7468  */
7469 /**
7470  * @extends Roo.grid.AbstractSelectionModel
7471  * @class Roo.grid.RowSelectionModel
7472  * The default SelectionModel used by {@link Roo.grid.Grid}.
7473  * It supports multiple selections and keyboard selection/navigation. 
7474  * @constructor
7475  * @param {Object} config
7476  */
7477 Roo.grid.RowSelectionModel = function(config){
7478     Roo.apply(this, config);
7479     this.selections = new Roo.util.MixedCollection(false, function(o){
7480         return o.id;
7481     });
7482
7483     this.last = false;
7484     this.lastActive = false;
7485
7486     this.addEvents({
7487         /**
7488         * @event selectionchange
7489         * Fires when the selection changes
7490         * @param {SelectionModel} this
7491         */
7492        "selectionchange" : true,
7493        /**
7494         * @event afterselectionchange
7495         * Fires after the selection changes (eg. by key press or clicking)
7496         * @param {SelectionModel} this
7497         */
7498        "afterselectionchange" : true,
7499        /**
7500         * @event beforerowselect
7501         * Fires when a row is selected being selected, return false to cancel.
7502         * @param {SelectionModel} this
7503         * @param {Number} rowIndex The selected index
7504         * @param {Boolean} keepExisting False if other selections will be cleared
7505         */
7506        "beforerowselect" : true,
7507        /**
7508         * @event rowselect
7509         * Fires when a row is selected.
7510         * @param {SelectionModel} this
7511         * @param {Number} rowIndex The selected index
7512         * @param {Roo.data.Record} r The record
7513         */
7514        "rowselect" : true,
7515        /**
7516         * @event rowdeselect
7517         * Fires when a row is deselected.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         */
7521         "rowdeselect" : true
7522     });
7523     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7524     this.locked = false;
7525 };
7526
7527 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7528     /**
7529      * @cfg {Boolean} singleSelect
7530      * True to allow selection of only one row at a time (defaults to false)
7531      */
7532     singleSelect : false,
7533
7534     // private
7535     initEvents : function(){
7536
7537         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7538             this.grid.on("mousedown", this.handleMouseDown, this);
7539         }else{ // allow click to work like normal
7540             this.grid.on("rowclick", this.handleDragableRowClick, this);
7541         }
7542         // bootstrap does not have a view..
7543         var view = this.grid.view ? this.grid.view : this.grid;
7544         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7545             "up" : function(e){
7546                 if(!e.shiftKey){
7547                     this.selectPrevious(e.shiftKey);
7548                 }else if(this.last !== false && this.lastActive !== false){
7549                     var last = this.last;
7550                     this.selectRange(this.last,  this.lastActive-1);
7551                     view.focusRow(this.lastActive);
7552                     if(last !== false){
7553                         this.last = last;
7554                     }
7555                 }else{
7556                     this.selectFirstRow();
7557                 }
7558                 this.fireEvent("afterselectionchange", this);
7559             },
7560             "down" : function(e){
7561                 if(!e.shiftKey){
7562                     this.selectNext(e.shiftKey);
7563                 }else if(this.last !== false && this.lastActive !== false){
7564                     var last = this.last;
7565                     this.selectRange(this.last,  this.lastActive+1);
7566                     view.focusRow(this.lastActive);
7567                     if(last !== false){
7568                         this.last = last;
7569                     }
7570                 }else{
7571                     this.selectFirstRow();
7572                 }
7573                 this.fireEvent("afterselectionchange", this);
7574             },
7575             scope: this
7576         });
7577
7578          
7579         view.on("refresh", this.onRefresh, this);
7580         view.on("rowupdated", this.onRowUpdated, this);
7581         view.on("rowremoved", this.onRemove, this);
7582     },
7583
7584     // private
7585     onRefresh : function(){
7586         var ds = this.grid.ds, i, v = this.grid.view;
7587         var s = this.selections;
7588         s.each(function(r){
7589             if((i = ds.indexOfId(r.id)) != -1){
7590                 v.onRowSelect(i);
7591                 s.add(ds.getAt(i)); // updating the selection relate data
7592             }else{
7593                 s.remove(r);
7594             }
7595         });
7596     },
7597
7598     // private
7599     onRemove : function(v, index, r){
7600         this.selections.remove(r);
7601     },
7602
7603     // private
7604     onRowUpdated : function(v, index, r){
7605         if(this.isSelected(r)){
7606             v.onRowSelect(index);
7607         }
7608     },
7609
7610     /**
7611      * Select records.
7612      * @param {Array} records The records to select
7613      * @param {Boolean} keepExisting (optional) True to keep existing selections
7614      */
7615     selectRecords : function(records, keepExisting){
7616         if(!keepExisting){
7617             this.clearSelections();
7618         }
7619         var ds = this.grid.ds;
7620         for(var i = 0, len = records.length; i < len; i++){
7621             this.selectRow(ds.indexOf(records[i]), true);
7622         }
7623     },
7624
7625     /**
7626      * Gets the number of selected rows.
7627      * @return {Number}
7628      */
7629     getCount : function(){
7630         return this.selections.length;
7631     },
7632
7633     /**
7634      * Selects the first row in the grid.
7635      */
7636     selectFirstRow : function(){
7637         this.selectRow(0);
7638     },
7639
7640     /**
7641      * Select the last row.
7642      * @param {Boolean} keepExisting (optional) True to keep existing selections
7643      */
7644     selectLastRow : function(keepExisting){
7645         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7646     },
7647
7648     /**
7649      * Selects the row immediately following the last selected row.
7650      * @param {Boolean} keepExisting (optional) True to keep existing selections
7651      */
7652     selectNext : function(keepExisting){
7653         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7654             this.selectRow(this.last+1, keepExisting);
7655             var view = this.grid.view ? this.grid.view : this.grid;
7656             view.focusRow(this.last);
7657         }
7658     },
7659
7660     /**
7661      * Selects the row that precedes the last selected row.
7662      * @param {Boolean} keepExisting (optional) True to keep existing selections
7663      */
7664     selectPrevious : function(keepExisting){
7665         if(this.last){
7666             this.selectRow(this.last-1, keepExisting);
7667             var view = this.grid.view ? this.grid.view : this.grid;
7668             view.focusRow(this.last);
7669         }
7670     },
7671
7672     /**
7673      * Returns the selected records
7674      * @return {Array} Array of selected records
7675      */
7676     getSelections : function(){
7677         return [].concat(this.selections.items);
7678     },
7679
7680     /**
7681      * Returns the first selected record.
7682      * @return {Record}
7683      */
7684     getSelected : function(){
7685         return this.selections.itemAt(0);
7686     },
7687
7688
7689     /**
7690      * Clears all selections.
7691      */
7692     clearSelections : function(fast){
7693         if(this.locked) {
7694             return;
7695         }
7696         if(fast !== true){
7697             var ds = this.grid.ds;
7698             var s = this.selections;
7699             s.each(function(r){
7700                 this.deselectRow(ds.indexOfId(r.id));
7701             }, this);
7702             s.clear();
7703         }else{
7704             this.selections.clear();
7705         }
7706         this.last = false;
7707     },
7708
7709
7710     /**
7711      * Selects all rows.
7712      */
7713     selectAll : function(){
7714         if(this.locked) {
7715             return;
7716         }
7717         this.selections.clear();
7718         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7719             this.selectRow(i, true);
7720         }
7721     },
7722
7723     /**
7724      * Returns True if there is a selection.
7725      * @return {Boolean}
7726      */
7727     hasSelection : function(){
7728         return this.selections.length > 0;
7729     },
7730
7731     /**
7732      * Returns True if the specified row is selected.
7733      * @param {Number/Record} record The record or index of the record to check
7734      * @return {Boolean}
7735      */
7736     isSelected : function(index){
7737         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7738         return (r && this.selections.key(r.id) ? true : false);
7739     },
7740
7741     /**
7742      * Returns True if the specified record id is selected.
7743      * @param {String} id The id of record to check
7744      * @return {Boolean}
7745      */
7746     isIdSelected : function(id){
7747         return (this.selections.key(id) ? true : false);
7748     },
7749
7750     // private
7751     handleMouseDown : function(e, t)
7752     {
7753         var view = this.grid.view ? this.grid.view : this.grid;
7754         var rowIndex;
7755         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7756             return;
7757         };
7758         if(e.shiftKey && this.last !== false){
7759             var last = this.last;
7760             this.selectRange(last, rowIndex, e.ctrlKey);
7761             this.last = last; // reset the last
7762             view.focusRow(rowIndex);
7763         }else{
7764             var isSelected = this.isSelected(rowIndex);
7765             if(e.button !== 0 && isSelected){
7766                 view.focusRow(rowIndex);
7767             }else if(e.ctrlKey && isSelected){
7768                 this.deselectRow(rowIndex);
7769             }else if(!isSelected){
7770                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7771                 view.focusRow(rowIndex);
7772             }
7773         }
7774         this.fireEvent("afterselectionchange", this);
7775     },
7776     // private
7777     handleDragableRowClick :  function(grid, rowIndex, e) 
7778     {
7779         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7780             this.selectRow(rowIndex, false);
7781             var view = this.grid.view ? this.grid.view : this.grid;
7782             view.focusRow(rowIndex);
7783              this.fireEvent("afterselectionchange", this);
7784         }
7785     },
7786     
7787     /**
7788      * Selects multiple rows.
7789      * @param {Array} rows Array of the indexes of the row to select
7790      * @param {Boolean} keepExisting (optional) True to keep existing selections
7791      */
7792     selectRows : function(rows, keepExisting){
7793         if(!keepExisting){
7794             this.clearSelections();
7795         }
7796         for(var i = 0, len = rows.length; i < len; i++){
7797             this.selectRow(rows[i], true);
7798         }
7799     },
7800
7801     /**
7802      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7803      * @param {Number} startRow The index of the first row in the range
7804      * @param {Number} endRow The index of the last row in the range
7805      * @param {Boolean} keepExisting (optional) True to retain existing selections
7806      */
7807     selectRange : function(startRow, endRow, keepExisting){
7808         if(this.locked) {
7809             return;
7810         }
7811         if(!keepExisting){
7812             this.clearSelections();
7813         }
7814         if(startRow <= endRow){
7815             for(var i = startRow; i <= endRow; i++){
7816                 this.selectRow(i, true);
7817             }
7818         }else{
7819             for(var i = startRow; i >= endRow; i--){
7820                 this.selectRow(i, true);
7821             }
7822         }
7823     },
7824
7825     /**
7826      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7827      * @param {Number} startRow The index of the first row in the range
7828      * @param {Number} endRow The index of the last row in the range
7829      */
7830     deselectRange : function(startRow, endRow, preventViewNotify){
7831         if(this.locked) {
7832             return;
7833         }
7834         for(var i = startRow; i <= endRow; i++){
7835             this.deselectRow(i, preventViewNotify);
7836         }
7837     },
7838
7839     /**
7840      * Selects a row.
7841      * @param {Number} row The index of the row to select
7842      * @param {Boolean} keepExisting (optional) True to keep existing selections
7843      */
7844     selectRow : function(index, keepExisting, preventViewNotify){
7845         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7846             return;
7847         }
7848         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7849             if(!keepExisting || this.singleSelect){
7850                 this.clearSelections();
7851             }
7852             var r = this.grid.ds.getAt(index);
7853             this.selections.add(r);
7854             this.last = this.lastActive = index;
7855             if(!preventViewNotify){
7856                 var view = this.grid.view ? this.grid.view : this.grid;
7857                 view.onRowSelect(index);
7858             }
7859             this.fireEvent("rowselect", this, index, r);
7860             this.fireEvent("selectionchange", this);
7861         }
7862     },
7863
7864     /**
7865      * Deselects a row.
7866      * @param {Number} row The index of the row to deselect
7867      */
7868     deselectRow : function(index, preventViewNotify){
7869         if(this.locked) {
7870             return;
7871         }
7872         if(this.last == index){
7873             this.last = false;
7874         }
7875         if(this.lastActive == index){
7876             this.lastActive = false;
7877         }
7878         var r = this.grid.ds.getAt(index);
7879         this.selections.remove(r);
7880         if(!preventViewNotify){
7881             var view = this.grid.view ? this.grid.view : this.grid;
7882             view.onRowDeselect(index);
7883         }
7884         this.fireEvent("rowdeselect", this, index);
7885         this.fireEvent("selectionchange", this);
7886     },
7887
7888     // private
7889     restoreLast : function(){
7890         if(this._last){
7891             this.last = this._last;
7892         }
7893     },
7894
7895     // private
7896     acceptsNav : function(row, col, cm){
7897         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7898     },
7899
7900     // private
7901     onEditorKey : function(field, e){
7902         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7903         if(k == e.TAB){
7904             e.stopEvent();
7905             ed.completeEdit();
7906             if(e.shiftKey){
7907                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908             }else{
7909                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910             }
7911         }else if(k == e.ENTER && !e.ctrlKey){
7912             e.stopEvent();
7913             ed.completeEdit();
7914             if(e.shiftKey){
7915                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916             }else{
7917                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918             }
7919         }else if(k == e.ESC){
7920             ed.cancelEdit();
7921         }
7922         if(newCell){
7923             g.startEditing(newCell[0], newCell[1]);
7924         }
7925     }
7926 });/*
7927  * Based on:
7928  * Ext JS Library 1.1.1
7929  * Copyright(c) 2006-2007, Ext JS, LLC.
7930  *
7931  * Originally Released Under LGPL - original licence link has changed is not relivant.
7932  *
7933  * Fork - LGPL
7934  * <script type="text/javascript">
7935  */
7936  
7937
7938 /**
7939  * @class Roo.grid.ColumnModel
7940  * @extends Roo.util.Observable
7941  * This is the default implementation of a ColumnModel used by the Grid. It defines
7942  * the columns in the grid.
7943  * <br>Usage:<br>
7944  <pre><code>
7945  var colModel = new Roo.grid.ColumnModel([
7946         {header: "Ticker", width: 60, sortable: true, locked: true},
7947         {header: "Company Name", width: 150, sortable: true},
7948         {header: "Market Cap.", width: 100, sortable: true},
7949         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7950         {header: "Employees", width: 100, sortable: true, resizable: false}
7951  ]);
7952  </code></pre>
7953  * <p>
7954  
7955  * The config options listed for this class are options which may appear in each
7956  * individual column definition.
7957  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958  * @constructor
7959  * @param {Object} config An Array of column config objects. See this class's
7960  * config objects for details.
7961 */
7962 Roo.grid.ColumnModel = function(config){
7963         /**
7964      * The config passed into the constructor
7965      */
7966     this.config = []; //config;
7967     this.lookup = {};
7968
7969     // if no id, create one
7970     // if the column does not have a dataIndex mapping,
7971     // map it to the order it is in the config
7972     for(var i = 0, len = config.length; i < len; i++){
7973         this.addColumn(config[i]);
7974         
7975     }
7976
7977     /**
7978      * The width of columns which have no width specified (defaults to 100)
7979      * @type Number
7980      */
7981     this.defaultWidth = 100;
7982
7983     /**
7984      * Default sortable of columns which have no sortable specified (defaults to false)
7985      * @type Boolean
7986      */
7987     this.defaultSortable = false;
7988
7989     this.addEvents({
7990         /**
7991              * @event widthchange
7992              * Fires when the width of a column changes.
7993              * @param {ColumnModel} this
7994              * @param {Number} columnIndex The column index
7995              * @param {Number} newWidth The new width
7996              */
7997             "widthchange": true,
7998         /**
7999              * @event headerchange
8000              * Fires when the text of a header changes.
8001              * @param {ColumnModel} this
8002              * @param {Number} columnIndex The column index
8003              * @param {Number} newText The new header text
8004              */
8005             "headerchange": true,
8006         /**
8007              * @event hiddenchange
8008              * Fires when a column is hidden or "unhidden".
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Boolean} hidden true if hidden, false otherwise
8012              */
8013             "hiddenchange": true,
8014             /**
8015          * @event columnmoved
8016          * Fires when a column is moved.
8017          * @param {ColumnModel} this
8018          * @param {Number} oldIndex
8019          * @param {Number} newIndex
8020          */
8021         "columnmoved" : true,
8022         /**
8023          * @event columlockchange
8024          * Fires when a column's locked state is changed
8025          * @param {ColumnModel} this
8026          * @param {Number} colIndex
8027          * @param {Boolean} locked true if locked
8028          */
8029         "columnlockchange" : true
8030     });
8031     Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 };
8033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034     /**
8035      * @cfg {String} header The header text to display in the Grid view.
8036      */
8037         /**
8038      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8039      */
8040         /**
8041      * @cfg {String} smHeader Header at Bootsrap Small width
8042      */
8043         /**
8044      * @cfg {String} mdHeader Header at Bootsrap Medium width
8045      */
8046         /**
8047      * @cfg {String} lgHeader Header at Bootsrap Large width
8048      */
8049         /**
8050      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8051      */
8052     /**
8053      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8054      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8055      * specified, the column's index is used as an index into the Record's data Array.
8056      */
8057     /**
8058      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8059      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8060      */
8061     /**
8062      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8063      * Defaults to the value of the {@link #defaultSortable} property.
8064      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8065      */
8066     /**
8067      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8068      */
8069     /**
8070      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8071      */
8072     /**
8073      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8074      */
8075     /**
8076      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8077      */
8078     /**
8079      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8080      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8081      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8082      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8083      */
8084        /**
8085      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8086      */
8087     /**
8088      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8089      */
8090     /**
8091      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8092      */
8093     /**
8094      * @cfg {String} cursor (Optional)
8095      */
8096     /**
8097      * @cfg {String} tooltip (Optional)
8098      */
8099     /**
8100      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8101      */
8102     /**
8103      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8104      */
8105     /**
8106      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8107      */
8108     /**
8109      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8110      */
8111         /**
8112      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8113      */
8114     /**
8115      * Returns the id of the column at the specified index.
8116      * @param {Number} index The column index
8117      * @return {String} the id
8118      */
8119     getColumnId : function(index){
8120         return this.config[index].id;
8121     },
8122
8123     /**
8124      * Returns the column for a specified id.
8125      * @param {String} id The column id
8126      * @return {Object} the column
8127      */
8128     getColumnById : function(id){
8129         return this.lookup[id];
8130     },
8131
8132     
8133     /**
8134      * Returns the column Object for a specified dataIndex.
8135      * @param {String} dataIndex The column dataIndex
8136      * @return {Object|Boolean} the column or false if not found
8137      */
8138     getColumnByDataIndex: function(dataIndex){
8139         var index = this.findColumnIndex(dataIndex);
8140         return index > -1 ? this.config[index] : false;
8141     },
8142     
8143     /**
8144      * Returns the index for a specified column id.
8145      * @param {String} id The column id
8146      * @return {Number} the index, or -1 if not found
8147      */
8148     getIndexById : function(id){
8149         for(var i = 0, len = this.config.length; i < len; i++){
8150             if(this.config[i].id == id){
8151                 return i;
8152             }
8153         }
8154         return -1;
8155     },
8156     
8157     /**
8158      * Returns the index for a specified column dataIndex.
8159      * @param {String} dataIndex The column dataIndex
8160      * @return {Number} the index, or -1 if not found
8161      */
8162     
8163     findColumnIndex : function(dataIndex){
8164         for(var i = 0, len = this.config.length; i < len; i++){
8165             if(this.config[i].dataIndex == dataIndex){
8166                 return i;
8167             }
8168         }
8169         return -1;
8170     },
8171     
8172     
8173     moveColumn : function(oldIndex, newIndex){
8174         var c = this.config[oldIndex];
8175         this.config.splice(oldIndex, 1);
8176         this.config.splice(newIndex, 0, c);
8177         this.dataMap = null;
8178         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8179     },
8180
8181     isLocked : function(colIndex){
8182         return this.config[colIndex].locked === true;
8183     },
8184
8185     setLocked : function(colIndex, value, suppressEvent){
8186         if(this.isLocked(colIndex) == value){
8187             return;
8188         }
8189         this.config[colIndex].locked = value;
8190         if(!suppressEvent){
8191             this.fireEvent("columnlockchange", this, colIndex, value);
8192         }
8193     },
8194
8195     getTotalLockedWidth : function(){
8196         var totalWidth = 0;
8197         for(var i = 0; i < this.config.length; i++){
8198             if(this.isLocked(i) && !this.isHidden(i)){
8199                 this.totalWidth += this.getColumnWidth(i);
8200             }
8201         }
8202         return totalWidth;
8203     },
8204
8205     getLockedCount : function(){
8206         for(var i = 0, len = this.config.length; i < len; i++){
8207             if(!this.isLocked(i)){
8208                 return i;
8209             }
8210         }
8211         
8212         return this.config.length;
8213     },
8214
8215     /**
8216      * Returns the number of columns.
8217      * @return {Number}
8218      */
8219     getColumnCount : function(visibleOnly){
8220         if(visibleOnly === true){
8221             var c = 0;
8222             for(var i = 0, len = this.config.length; i < len; i++){
8223                 if(!this.isHidden(i)){
8224                     c++;
8225                 }
8226             }
8227             return c;
8228         }
8229         return this.config.length;
8230     },
8231
8232     /**
8233      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8234      * @param {Function} fn
8235      * @param {Object} scope (optional)
8236      * @return {Array} result
8237      */
8238     getColumnsBy : function(fn, scope){
8239         var r = [];
8240         for(var i = 0, len = this.config.length; i < len; i++){
8241             var c = this.config[i];
8242             if(fn.call(scope||this, c, i) === true){
8243                 r[r.length] = c;
8244             }
8245         }
8246         return r;
8247     },
8248
8249     /**
8250      * Returns true if the specified column is sortable.
8251      * @param {Number} col The column index
8252      * @return {Boolean}
8253      */
8254     isSortable : function(col){
8255         if(typeof this.config[col].sortable == "undefined"){
8256             return this.defaultSortable;
8257         }
8258         return this.config[col].sortable;
8259     },
8260
8261     /**
8262      * Returns the rendering (formatting) function defined for the column.
8263      * @param {Number} col The column index.
8264      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265      */
8266     getRenderer : function(col){
8267         if(!this.config[col].renderer){
8268             return Roo.grid.ColumnModel.defaultRenderer;
8269         }
8270         return this.config[col].renderer;
8271     },
8272
8273     /**
8274      * Sets the rendering (formatting) function for a column.
8275      * @param {Number} col The column index
8276      * @param {Function} fn The function to use to process the cell's raw data
8277      * to return HTML markup for the grid view. The render function is called with
8278      * the following parameters:<ul>
8279      * <li>Data value.</li>
8280      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8281      * <li>css A CSS style string to apply to the table cell.</li>
8282      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8283      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8284      * <li>Row index</li>
8285      * <li>Column index</li>
8286      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287      */
8288     setRenderer : function(col, fn){
8289         this.config[col].renderer = fn;
8290     },
8291
8292     /**
8293      * Returns the width for the specified column.
8294      * @param {Number} col The column index
8295      * @param (optional) {String} gridSize bootstrap width size.
8296      * @return {Number}
8297      */
8298     getColumnWidth : function(col, gridSize)
8299         {
8300                 var cfg = this.config[col];
8301                 
8302                 if (typeof(gridSize) == 'undefined') {
8303                         return cfg.width * 1 || this.defaultWidth;
8304                 }
8305                 if (gridSize === false) { // if we set it..
8306                         return cfg.width || false;
8307                 }
8308                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309                 
8310                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8311                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8312                                 continue;
8313                         }
8314                         return cfg[ sizes[i] ];
8315                 }
8316                 return 1;
8317                 
8318     },
8319
8320     /**
8321      * Sets the width for a column.
8322      * @param {Number} col The column index
8323      * @param {Number} width The new width
8324      */
8325     setColumnWidth : function(col, width, suppressEvent){
8326         this.config[col].width = width;
8327         this.totalWidth = null;
8328         if(!suppressEvent){
8329              this.fireEvent("widthchange", this, col, width);
8330         }
8331     },
8332
8333     /**
8334      * Returns the total width of all columns.
8335      * @param {Boolean} includeHidden True to include hidden column widths
8336      * @return {Number}
8337      */
8338     getTotalWidth : function(includeHidden){
8339         if(!this.totalWidth){
8340             this.totalWidth = 0;
8341             for(var i = 0, len = this.config.length; i < len; i++){
8342                 if(includeHidden || !this.isHidden(i)){
8343                     this.totalWidth += this.getColumnWidth(i);
8344                 }
8345             }
8346         }
8347         return this.totalWidth;
8348     },
8349
8350     /**
8351      * Returns the header for the specified column.
8352      * @param {Number} col The column index
8353      * @return {String}
8354      */
8355     getColumnHeader : function(col){
8356         return this.config[col].header;
8357     },
8358
8359     /**
8360      * Sets the header for a column.
8361      * @param {Number} col The column index
8362      * @param {String} header The new header
8363      */
8364     setColumnHeader : function(col, header){
8365         this.config[col].header = header;
8366         this.fireEvent("headerchange", this, col, header);
8367     },
8368
8369     /**
8370      * Returns the tooltip for the specified column.
8371      * @param {Number} col The column index
8372      * @return {String}
8373      */
8374     getColumnTooltip : function(col){
8375             return this.config[col].tooltip;
8376     },
8377     /**
8378      * Sets the tooltip for a column.
8379      * @param {Number} col The column index
8380      * @param {String} tooltip The new tooltip
8381      */
8382     setColumnTooltip : function(col, tooltip){
8383             this.config[col].tooltip = tooltip;
8384     },
8385
8386     /**
8387      * Returns the dataIndex for the specified column.
8388      * @param {Number} col The column index
8389      * @return {Number}
8390      */
8391     getDataIndex : function(col){
8392         return this.config[col].dataIndex;
8393     },
8394
8395     /**
8396      * Sets the dataIndex for a column.
8397      * @param {Number} col The column index
8398      * @param {Number} dataIndex The new dataIndex
8399      */
8400     setDataIndex : function(col, dataIndex){
8401         this.config[col].dataIndex = dataIndex;
8402     },
8403
8404     
8405     
8406     /**
8407      * Returns true if the cell is editable.
8408      * @param {Number} colIndex The column index
8409      * @param {Number} rowIndex The row index - this is nto actually used..?
8410      * @return {Boolean}
8411      */
8412     isCellEditable : function(colIndex, rowIndex){
8413         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8414     },
8415
8416     /**
8417      * Returns the editor defined for the cell/column.
8418      * return false or null to disable editing.
8419      * @param {Number} colIndex The column index
8420      * @param {Number} rowIndex The row index
8421      * @return {Object}
8422      */
8423     getCellEditor : function(colIndex, rowIndex){
8424         return this.config[colIndex].editor;
8425     },
8426
8427     /**
8428      * Sets if a column is editable.
8429      * @param {Number} col The column index
8430      * @param {Boolean} editable True if the column is editable
8431      */
8432     setEditable : function(col, editable){
8433         this.config[col].editable = editable;
8434     },
8435
8436
8437     /**
8438      * Returns true if the column is hidden.
8439      * @param {Number} colIndex The column index
8440      * @return {Boolean}
8441      */
8442     isHidden : function(colIndex){
8443         return this.config[colIndex].hidden;
8444     },
8445
8446
8447     /**
8448      * Returns true if the column width cannot be changed
8449      */
8450     isFixed : function(colIndex){
8451         return this.config[colIndex].fixed;
8452     },
8453
8454     /**
8455      * Returns true if the column can be resized
8456      * @return {Boolean}
8457      */
8458     isResizable : function(colIndex){
8459         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8460     },
8461     /**
8462      * Sets if a column is hidden.
8463      * @param {Number} colIndex The column index
8464      * @param {Boolean} hidden True if the column is hidden
8465      */
8466     setHidden : function(colIndex, hidden){
8467         this.config[colIndex].hidden = hidden;
8468         this.totalWidth = null;
8469         this.fireEvent("hiddenchange", this, colIndex, hidden);
8470     },
8471
8472     /**
8473      * Sets the editor for a column.
8474      * @param {Number} col The column index
8475      * @param {Object} editor The editor object
8476      */
8477     setEditor : function(col, editor){
8478         this.config[col].editor = editor;
8479     },
8480     /**
8481      * Add a column (experimental...) - defaults to adding to the end..
8482      * @param {Object} config 
8483     */
8484     addColumn : function(c)
8485     {
8486     
8487         var i = this.config.length;
8488         this.config[i] = c;
8489         
8490         if(typeof c.dataIndex == "undefined"){
8491             c.dataIndex = i;
8492         }
8493         if(typeof c.renderer == "string"){
8494             c.renderer = Roo.util.Format[c.renderer];
8495         }
8496         if(typeof c.id == "undefined"){
8497             c.id = Roo.id();
8498         }
8499         if(c.editor && c.editor.xtype){
8500             c.editor  = Roo.factory(c.editor, Roo.grid);
8501         }
8502         if(c.editor && c.editor.isFormField){
8503             c.editor = new Roo.grid.GridEditor(c.editor);
8504         }
8505         this.lookup[c.id] = c;
8506     }
8507     
8508 });
8509
8510 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 {
8512     if(typeof value == "object") {
8513         return value;
8514     }
8515         if(typeof value == "string" && value.length < 1){
8516             return "&#160;";
8517         }
8518     
8519         return String.format("{0}", value);
8520 };
8521
8522 // Alias for backwards compatibility
8523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8524 /*
8525  * Based on:
8526  * Ext JS Library 1.1.1
8527  * Copyright(c) 2006-2007, Ext JS, LLC.
8528  *
8529  * Originally Released Under LGPL - original licence link has changed is not relivant.
8530  *
8531  * Fork - LGPL
8532  * <script type="text/javascript">
8533  */
8534  
8535 /**
8536  * @class Roo.LoadMask
8537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8540  * element's UpdateManager load indicator and will be destroyed after the initial load.
8541  * @constructor
8542  * Create a new LoadMask
8543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8544  * @param {Object} config The config object
8545  */
8546 Roo.LoadMask = function(el, config){
8547     this.el = Roo.get(el);
8548     Roo.apply(this, config);
8549     if(this.store){
8550         this.store.on('beforeload', this.onBeforeLoad, this);
8551         this.store.on('load', this.onLoad, this);
8552         this.store.on('loadexception', this.onLoadException, this);
8553         this.removeMask = false;
8554     }else{
8555         var um = this.el.getUpdateManager();
8556         um.showLoadIndicator = false; // disable the default indicator
8557         um.on('beforeupdate', this.onBeforeLoad, this);
8558         um.on('update', this.onLoad, this);
8559         um.on('failure', this.onLoad, this);
8560         this.removeMask = true;
8561     }
8562 };
8563
8564 Roo.LoadMask.prototype = {
8565     /**
8566      * @cfg {Boolean} removeMask
8567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8569      */
8570     /**
8571      * @cfg {String} msg
8572      * The text to display in a centered loading message box (defaults to 'Loading...')
8573      */
8574     msg : 'Loading...',
8575     /**
8576      * @cfg {String} msgCls
8577      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8578      */
8579     msgCls : 'x-mask-loading',
8580
8581     /**
8582      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8583      * @type Boolean
8584      */
8585     disabled: false,
8586
8587     /**
8588      * Disables the mask to prevent it from being displayed
8589      */
8590     disable : function(){
8591        this.disabled = true;
8592     },
8593
8594     /**
8595      * Enables the mask so that it can be displayed
8596      */
8597     enable : function(){
8598         this.disabled = false;
8599     },
8600     
8601     onLoadException : function()
8602     {
8603         Roo.log(arguments);
8604         
8605         if (typeof(arguments[3]) != 'undefined') {
8606             Roo.MessageBox.alert("Error loading",arguments[3]);
8607         } 
8608         /*
8609         try {
8610             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8611                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8612             }   
8613         } catch(e) {
8614             
8615         }
8616         */
8617     
8618         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8619     },
8620     // private
8621     onLoad : function()
8622     {
8623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8624     },
8625
8626     // private
8627     onBeforeLoad : function(){
8628         if(!this.disabled){
8629             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8630         }
8631     },
8632
8633     // private
8634     destroy : function(){
8635         if(this.store){
8636             this.store.un('beforeload', this.onBeforeLoad, this);
8637             this.store.un('load', this.onLoad, this);
8638             this.store.un('loadexception', this.onLoadException, this);
8639         }else{
8640             var um = this.el.getUpdateManager();
8641             um.un('beforeupdate', this.onBeforeLoad, this);
8642             um.un('update', this.onLoad, this);
8643             um.un('failure', this.onLoad, this);
8644         }
8645     }
8646 };/**
8647  * @class Roo.bootstrap.Table
8648  * @licence LGBL
8649  * @extends Roo.bootstrap.Component
8650  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8651  * Similar to Roo.grid.Grid
8652  * <pre><code>
8653  var table = Roo.factory({
8654     xtype : 'Table',
8655     xns : Roo.bootstrap,
8656     autoSizeColumns: true,
8657     
8658     
8659     store : {
8660         xtype : 'Store',
8661         xns : Roo.data,
8662         remoteSort : true,
8663         sortInfo : { direction : 'ASC', field: 'name' },
8664         proxy : {
8665            xtype : 'HttpProxy',
8666            xns : Roo.data,
8667            method : 'GET',
8668            url : 'https://example.com/some.data.url.json'
8669         },
8670         reader : {
8671            xtype : 'JsonReader',
8672            xns : Roo.data,
8673            fields : [ 'id', 'name', whatever' ],
8674            id : 'id',
8675            root : 'data'
8676         }
8677     },
8678     cm : [
8679         {
8680             xtype : 'ColumnModel',
8681             xns : Roo.grid,
8682             align : 'center',
8683             cursor : 'pointer',
8684             dataIndex : 'is_in_group',
8685             header : "Name",
8686             sortable : true,
8687             renderer : function(v, x , r) {  
8688             
8689                 return String.format("{0}", v)
8690             }
8691             width : 3
8692         } // more columns..
8693     ],
8694     selModel : {
8695         xtype : 'RowSelectionModel',
8696         xns : Roo.bootstrap.Table
8697         // you can add listeners to catch selection change here....
8698     }
8699      
8700
8701  });
8702  // set any options
8703  grid.render(Roo.get("some-div"));
8704 </code></pre>
8705
8706 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8707
8708
8709
8710  *
8711  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8712  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8713  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8714  * 
8715  * @cfg {String} cls table class
8716  *
8717  * 
8718  * @cfg {boolean} striped Should the rows be alternative striped
8719  * @cfg {boolean} bordered Add borders to the table
8720  * @cfg {boolean} hover Add hover highlighting
8721  * @cfg {boolean} condensed Format condensed
8722  * @cfg {boolean} responsive Format condensed
8723  * @cfg {Boolean} loadMask (true|false) default false
8724  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8725  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8726  * @cfg {Boolean} rowSelection (true|false) default false
8727  * @cfg {Boolean} cellSelection (true|false) default false
8728  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8729  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8730  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8731  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8732  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8733  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8734  * 
8735  * @constructor
8736  * Create a new Table
8737  * @param {Object} config The config object
8738  */
8739
8740 Roo.bootstrap.Table = function(config)
8741 {
8742     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8743      
8744     // BC...
8745     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8746     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8747     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8748     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8749     
8750     this.view = this; // compat with grid.
8751     
8752     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8753     if (this.sm) {
8754         this.sm.grid = this;
8755         this.selModel = Roo.factory(this.sm, Roo.grid);
8756         this.sm = this.selModel;
8757         this.sm.xmodule = this.xmodule || false;
8758     }
8759     
8760     if (this.cm && typeof(this.cm.config) == 'undefined') {
8761         this.colModel = new Roo.grid.ColumnModel(this.cm);
8762         this.cm = this.colModel;
8763         this.cm.xmodule = this.xmodule || false;
8764     }
8765     if (this.store) {
8766         this.store= Roo.factory(this.store, Roo.data);
8767         this.ds = this.store;
8768         this.ds.xmodule = this.xmodule || false;
8769          
8770     }
8771     if (this.footer && this.store) {
8772         this.footer.dataSource = this.ds;
8773         this.footer = Roo.factory(this.footer);
8774     }
8775     
8776     /** @private */
8777     this.addEvents({
8778         /**
8779          * @event cellclick
8780          * Fires when a cell is clicked
8781          * @param {Roo.bootstrap.Table} this
8782          * @param {Roo.Element} el
8783          * @param {Number} rowIndex
8784          * @param {Number} columnIndex
8785          * @param {Roo.EventObject} e
8786          */
8787         "cellclick" : true,
8788         /**
8789          * @event celldblclick
8790          * Fires when a cell is double clicked
8791          * @param {Roo.bootstrap.Table} this
8792          * @param {Roo.Element} el
8793          * @param {Number} rowIndex
8794          * @param {Number} columnIndex
8795          * @param {Roo.EventObject} e
8796          */
8797         "celldblclick" : true,
8798         /**
8799          * @event rowclick
8800          * Fires when a row is clicked
8801          * @param {Roo.bootstrap.Table} this
8802          * @param {Roo.Element} el
8803          * @param {Number} rowIndex
8804          * @param {Roo.EventObject} e
8805          */
8806         "rowclick" : true,
8807         /**
8808          * @event rowdblclick
8809          * Fires when a row is double clicked
8810          * @param {Roo.bootstrap.Table} this
8811          * @param {Roo.Element} el
8812          * @param {Number} rowIndex
8813          * @param {Roo.EventObject} e
8814          */
8815         "rowdblclick" : true,
8816         /**
8817          * @event mouseover
8818          * Fires when a mouseover occur
8819          * @param {Roo.bootstrap.Table} this
8820          * @param {Roo.Element} el
8821          * @param {Number} rowIndex
8822          * @param {Number} columnIndex
8823          * @param {Roo.EventObject} e
8824          */
8825         "mouseover" : true,
8826         /**
8827          * @event mouseout
8828          * Fires when a mouseout occur
8829          * @param {Roo.bootstrap.Table} this
8830          * @param {Roo.Element} el
8831          * @param {Number} rowIndex
8832          * @param {Number} columnIndex
8833          * @param {Roo.EventObject} e
8834          */
8835         "mouseout" : true,
8836         /**
8837          * @event rowclass
8838          * Fires when a row is rendered, so you can change add a style to it.
8839          * @param {Roo.bootstrap.Table} this
8840          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8841          */
8842         'rowclass' : true,
8843           /**
8844          * @event rowsrendered
8845          * Fires when all the  rows have been rendered
8846          * @param {Roo.bootstrap.Table} this
8847          */
8848         'rowsrendered' : true,
8849         /**
8850          * @event contextmenu
8851          * The raw contextmenu event for the entire grid.
8852          * @param {Roo.EventObject} e
8853          */
8854         "contextmenu" : true,
8855         /**
8856          * @event rowcontextmenu
8857          * Fires when a row is right clicked
8858          * @param {Roo.bootstrap.Table} this
8859          * @param {Number} rowIndex
8860          * @param {Roo.EventObject} e
8861          */
8862         "rowcontextmenu" : true,
8863         /**
8864          * @event cellcontextmenu
8865          * Fires when a cell is right clicked
8866          * @param {Roo.bootstrap.Table} this
8867          * @param {Number} rowIndex
8868          * @param {Number} cellIndex
8869          * @param {Roo.EventObject} e
8870          */
8871          "cellcontextmenu" : true,
8872          /**
8873          * @event headercontextmenu
8874          * Fires when a header is right clicked
8875          * @param {Roo.bootstrap.Table} this
8876          * @param {Number} columnIndex
8877          * @param {Roo.EventObject} e
8878          */
8879         "headercontextmenu" : true,
8880         /**
8881          * @event mousedown
8882          * The raw mousedown event for the entire grid.
8883          * @param {Roo.EventObject} e
8884          */
8885         "mousedown" : true
8886         
8887     });
8888 };
8889
8890 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8891     
8892     cls: false,
8893     
8894     striped : false,
8895     scrollBody : false,
8896     bordered: false,
8897     hover:  false,
8898     condensed : false,
8899     responsive : false,
8900     sm : false,
8901     cm : false,
8902     store : false,
8903     loadMask : false,
8904     footerShow : true,
8905     headerShow : true,
8906     enableColumnResize: true,
8907   
8908     rowSelection : false,
8909     cellSelection : false,
8910     layout : false,
8911
8912     minColumnWidth : 50,
8913     
8914     // Roo.Element - the tbody
8915     bodyEl: false,  // <tbody> Roo.Element - thead element    
8916     headEl: false,  // <thead> Roo.Element - thead element
8917     resizeProxy : false, // proxy element for dragging?
8918
8919
8920     
8921     container: false, // used by gridpanel...
8922     
8923     lazyLoad : false,
8924     
8925     CSS : Roo.util.CSS,
8926     
8927     auto_hide_footer : false,
8928     
8929     view: false, // actually points to this..
8930     
8931     getAutoCreate : function()
8932     {
8933         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8934         
8935         cfg = {
8936             tag: 'table',
8937             cls : 'table', 
8938             cn : []
8939         };
8940         // this get's auto added by panel.Grid
8941         if (this.scrollBody) {
8942             cfg.cls += ' table-body-fixed';
8943         }    
8944         if (this.striped) {
8945             cfg.cls += ' table-striped';
8946         }
8947         
8948         if (this.hover) {
8949             cfg.cls += ' table-hover';
8950         }
8951         if (this.bordered) {
8952             cfg.cls += ' table-bordered';
8953         }
8954         if (this.condensed) {
8955             cfg.cls += ' table-condensed';
8956         }
8957         
8958         if (this.responsive) {
8959             cfg.cls += ' table-responsive';
8960         }
8961         
8962         if (this.cls) {
8963             cfg.cls+=  ' ' +this.cls;
8964         }
8965         
8966         
8967         
8968         if (this.layout) {
8969             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8970         }
8971         
8972         if(this.store || this.cm){
8973             if(this.headerShow){
8974                 cfg.cn.push(this.renderHeader());
8975             }
8976             
8977             cfg.cn.push(this.renderBody());
8978             
8979             if(this.footerShow){
8980                 cfg.cn.push(this.renderFooter());
8981             }
8982             // where does this come from?
8983             //cfg.cls+=  ' TableGrid';
8984         }
8985         
8986         return { cn : [ cfg ] };
8987     },
8988     
8989     initEvents : function()
8990     {   
8991         if(!this.store || !this.cm){
8992             return;
8993         }
8994         if (this.selModel) {
8995             this.selModel.initEvents();
8996         }
8997         
8998         
8999         //Roo.log('initEvents with ds!!!!');
9000         
9001         this.bodyEl = this.el.select('tbody', true).first();
9002         this.headEl = this.el.select('thead', true).first();
9003         this.mainFoot = this.el.select('tfoot', true).first();
9004         
9005         
9006         
9007         
9008         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9009             e.on('click', this.sort, this);
9010         }, this);
9011         
9012         
9013         // why is this done????? = it breaks dialogs??
9014         //this.parent().el.setStyle('position', 'relative');
9015         
9016         
9017         if (this.footer) {
9018             this.footer.parentId = this.id;
9019             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9020             
9021             if(this.lazyLoad){
9022                 this.el.select('tfoot tr td').first().addClass('hide');
9023             }
9024         } 
9025         
9026         if(this.loadMask) {
9027             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9028         }
9029         
9030         this.store.on('load', this.onLoad, this);
9031         this.store.on('beforeload', this.onBeforeLoad, this);
9032         this.store.on('update', this.onUpdate, this);
9033         this.store.on('add', this.onAdd, this);
9034         this.store.on("clear", this.clear, this);
9035         
9036         this.el.on("contextmenu", this.onContextMenu, this);
9037         
9038         
9039         this.cm.on("headerchange", this.onHeaderChange, this);
9040         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9041
9042  //?? does bodyEl get replaced on render?
9043         this.bodyEl.on("click", this.onClick, this);
9044         this.bodyEl.on("dblclick", this.onDblClick, this);        
9045         this.bodyEl.on('scroll', this.onBodyScroll, this);
9046
9047         // guessing mainbody will work - this relays usually caught by selmodel at present.
9048         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9049   
9050   
9051         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9052         
9053   
9054         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9055             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9056         }
9057         
9058         this.initCSS();
9059     },
9060     // Compatibility with grid - we implement all the view features at present.
9061     getView : function()
9062     {
9063         return this;
9064     },
9065     
9066     initCSS : function()
9067     {
9068         
9069         
9070         var cm = this.cm, styles = [];
9071         this.CSS.removeStyleSheet(this.id + '-cssrules');
9072         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9073         // we can honour xs/sm/md/xl  as widths...
9074         // we first have to decide what widht we are currently at...
9075         var sz = Roo.getGridSize();
9076         
9077         var total = 0;
9078         var last = -1;
9079         var cols = []; // visable cols.
9080         var total_abs = 0;
9081         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9082             var w = cm.getColumnWidth(i, false);
9083             if(cm.isHidden(i)){
9084                 cols.push( { rel : false, abs : 0 });
9085                 continue;
9086             }
9087             if (w !== false) {
9088                 cols.push( { rel : false, abs : w });
9089                 total_abs += w;
9090                 last = i; // not really..
9091                 continue;
9092             }
9093             var w = cm.getColumnWidth(i, sz);
9094             if (w > 0) {
9095                 last = i
9096             }
9097             total += w;
9098             cols.push( { rel : w, abs : false });
9099         }
9100         
9101         var avail = this.bodyEl.dom.clientWidth - total_abs;
9102         
9103         var unitWidth = Math.floor(avail / total);
9104         var rem = avail - (unitWidth * total);
9105         
9106         var hidden, width, pos = 0 , splithide , left;
9107         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9108             
9109             hidden = 'display:none;';
9110             left = '';
9111             width  = 'width:0px;';
9112             splithide = '';
9113             if(!cm.isHidden(i)){
9114                 hidden = '';
9115                 
9116                 
9117                 // we can honour xs/sm/md/xl ?
9118                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9119                 if (w===0) {
9120                     hidden = 'display:none;';
9121                 }
9122                 // width should return a small number...
9123                 if (i == last) {
9124                     w+=rem; // add the remaining with..
9125                 }
9126                 pos += w;
9127                 left = "left:" + (pos -4) + "px;";
9128                 width = "width:" + w+ "px;";
9129                 
9130             }
9131             if (this.responsive) {
9132                 width = '';
9133                 left = '';
9134                 hidden = cm.isHidden(i) ? 'display:none' : '';
9135                 splithide = 'display: none';
9136             }
9137             
9138             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9139             if (this.headEl) {
9140                 if (i == last) {
9141                     splithide = 'display:none;';
9142                 }
9143                 
9144                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9145                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9146                 );
9147             }
9148             
9149         }
9150         //Roo.log(styles.join(''));
9151         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9152         
9153     },
9154     
9155     
9156     
9157     onContextMenu : function(e, t)
9158     {
9159         this.processEvent("contextmenu", e);
9160     },
9161     
9162     processEvent : function(name, e)
9163     {
9164         if (name != 'touchstart' ) {
9165             this.fireEvent(name, e);    
9166         }
9167         
9168         var t = e.getTarget();
9169         
9170         var cell = Roo.get(t);
9171         
9172         if(!cell){
9173             return;
9174         }
9175         
9176         if(cell.findParent('tfoot', false, true)){
9177             return;
9178         }
9179         
9180         if(cell.findParent('thead', false, true)){
9181             
9182             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9183                 cell = Roo.get(t).findParent('th', false, true);
9184                 if (!cell) {
9185                     Roo.log("failed to find th in thead?");
9186                     Roo.log(e.getTarget());
9187                     return;
9188                 }
9189             }
9190             
9191             var cellIndex = cell.dom.cellIndex;
9192             
9193             var ename = name == 'touchstart' ? 'click' : name;
9194             this.fireEvent("header" + ename, this, cellIndex, e);
9195             
9196             return;
9197         }
9198         
9199         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9200             cell = Roo.get(t).findParent('td', false, true);
9201             if (!cell) {
9202                 Roo.log("failed to find th in tbody?");
9203                 Roo.log(e.getTarget());
9204                 return;
9205             }
9206         }
9207         
9208         var row = cell.findParent('tr', false, true);
9209         var cellIndex = cell.dom.cellIndex;
9210         var rowIndex = row.dom.rowIndex - 1;
9211         
9212         if(row !== false){
9213             
9214             this.fireEvent("row" + name, this, rowIndex, e);
9215             
9216             if(cell !== false){
9217             
9218                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9219             }
9220         }
9221         
9222     },
9223     
9224     onMouseover : function(e, el)
9225     {
9226         var cell = Roo.get(el);
9227         
9228         if(!cell){
9229             return;
9230         }
9231         
9232         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9233             cell = cell.findParent('td', false, true);
9234         }
9235         
9236         var row = cell.findParent('tr', false, true);
9237         var cellIndex = cell.dom.cellIndex;
9238         var rowIndex = row.dom.rowIndex - 1; // start from 0
9239         
9240         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9241         
9242     },
9243     
9244     onMouseout : function(e, el)
9245     {
9246         var cell = Roo.get(el);
9247         
9248         if(!cell){
9249             return;
9250         }
9251         
9252         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9253             cell = cell.findParent('td', false, true);
9254         }
9255         
9256         var row = cell.findParent('tr', false, true);
9257         var cellIndex = cell.dom.cellIndex;
9258         var rowIndex = row.dom.rowIndex - 1; // start from 0
9259         
9260         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9261         
9262     },
9263     
9264     onClick : function(e, el)
9265     {
9266         var cell = Roo.get(el);
9267         
9268         if(!cell || (!this.cellSelection && !this.rowSelection)){
9269             return;
9270         }
9271         
9272         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9273             cell = cell.findParent('td', false, true);
9274         }
9275         
9276         if(!cell || typeof(cell) == 'undefined'){
9277             return;
9278         }
9279         
9280         var row = cell.findParent('tr', false, true);
9281         
9282         if(!row || typeof(row) == 'undefined'){
9283             return;
9284         }
9285         
9286         var cellIndex = cell.dom.cellIndex;
9287         var rowIndex = this.getRowIndex(row);
9288         
9289         // why??? - should these not be based on SelectionModel?
9290         //if(this.cellSelection){
9291             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9292         //}
9293         
9294         //if(this.rowSelection){
9295             this.fireEvent('rowclick', this, row, rowIndex, e);
9296         //}
9297          
9298     },
9299         
9300     onDblClick : function(e,el)
9301     {
9302         var cell = Roo.get(el);
9303         
9304         if(!cell || (!this.cellSelection && !this.rowSelection)){
9305             return;
9306         }
9307         
9308         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9309             cell = cell.findParent('td', false, true);
9310         }
9311         
9312         if(!cell || typeof(cell) == 'undefined'){
9313             return;
9314         }
9315         
9316         var row = cell.findParent('tr', false, true);
9317         
9318         if(!row || typeof(row) == 'undefined'){
9319             return;
9320         }
9321         
9322         var cellIndex = cell.dom.cellIndex;
9323         var rowIndex = this.getRowIndex(row);
9324         
9325         if(this.cellSelection){
9326             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9327         }
9328         
9329         if(this.rowSelection){
9330             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9331         }
9332     },
9333     findRowIndex : function(el)
9334     {
9335         var cell = Roo.get(el);
9336         if(!cell) {
9337             return false;
9338         }
9339         var row = cell.findParent('tr', false, true);
9340         
9341         if(!row || typeof(row) == 'undefined'){
9342             return false;
9343         }
9344         return this.getRowIndex(row);
9345     },
9346     sort : function(e,el)
9347     {
9348         var col = Roo.get(el);
9349         
9350         if(!col.hasClass('sortable')){
9351             return;
9352         }
9353         
9354         var sort = col.attr('sort');
9355         var dir = 'ASC';
9356         
9357         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9358             dir = 'DESC';
9359         }
9360         
9361         this.store.sortInfo = {field : sort, direction : dir};
9362         
9363         if (this.footer) {
9364             Roo.log("calling footer first");
9365             this.footer.onClick('first');
9366         } else {
9367         
9368             this.store.load({ params : { start : 0 } });
9369         }
9370     },
9371     
9372     renderHeader : function()
9373     {
9374         var header = {
9375             tag: 'thead',
9376             cn : []
9377         };
9378         
9379         var cm = this.cm;
9380         this.totalWidth = 0;
9381         
9382         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9383             
9384             var config = cm.config[i];
9385             
9386             var c = {
9387                 tag: 'th',
9388                 cls : 'x-hcol-' + i,
9389                 style : '',
9390                 
9391                 html: cm.getColumnHeader(i)
9392             };
9393             
9394             var tooltip = cm.getColumnTooltip(i);
9395             if (tooltip) {
9396                 c.tooltip = tooltip;
9397             }
9398             
9399             
9400             var hh = '';
9401             
9402             if(typeof(config.sortable) != 'undefined' && config.sortable){
9403                 c.cls += ' sortable';
9404                 c.html = '<i class="fa"></i>' + c.html;
9405             }
9406             
9407             // could use BS4 hidden-..-down 
9408             
9409             if(typeof(config.lgHeader) != 'undefined'){
9410                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9411             }
9412             
9413             if(typeof(config.mdHeader) != 'undefined'){
9414                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9415             }
9416             
9417             if(typeof(config.smHeader) != 'undefined'){
9418                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9419             }
9420             
9421             if(typeof(config.xsHeader) != 'undefined'){
9422                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9423             }
9424             
9425             if(hh.length){
9426                 c.html = hh;
9427             }
9428             
9429             if(typeof(config.tooltip) != 'undefined'){
9430                 c.tooltip = config.tooltip;
9431             }
9432             
9433             if(typeof(config.colspan) != 'undefined'){
9434                 c.colspan = config.colspan;
9435             }
9436             
9437             // hidden is handled by CSS now
9438             
9439             if(typeof(config.dataIndex) != 'undefined'){
9440                 c.sort = config.dataIndex;
9441             }
9442             
9443            
9444             
9445             if(typeof(config.align) != 'undefined' && config.align.length){
9446                 c.style += ' text-align:' + config.align + ';';
9447             }
9448             
9449             /* width is done in CSS
9450              *if(typeof(config.width) != 'undefined'){
9451                 c.style += ' width:' + config.width + 'px;';
9452                 this.totalWidth += config.width;
9453             } else {
9454                 this.totalWidth += 100; // assume minimum of 100 per column?
9455             }
9456             */
9457             
9458             if(typeof(config.cls) != 'undefined'){
9459                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9460             }
9461             // this is the bit that doesnt reall work at all...
9462             
9463             if (this.responsive) {
9464                  
9465             
9466                 ['xs','sm','md','lg'].map(function(size){
9467                     
9468                     if(typeof(config[size]) == 'undefined'){
9469                         return;
9470                     }
9471                      
9472                     if (!config[size]) { // 0 = hidden
9473                         // BS 4 '0' is treated as hide that column and below.
9474                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9475                         return;
9476                     }
9477                     
9478                     c.cls += ' col-' + size + '-' + config[size] + (
9479                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9480                     );
9481                     
9482                     
9483                 });
9484             }
9485             // at the end?
9486             
9487             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9488             
9489             
9490             
9491             
9492             header.cn.push(c)
9493         }
9494         
9495         return header;
9496     },
9497     
9498     renderBody : function()
9499     {
9500         var body = {
9501             tag: 'tbody',
9502             cn : [
9503                 {
9504                     tag: 'tr',
9505                     cn : [
9506                         {
9507                             tag : 'td',
9508                             colspan :  this.cm.getColumnCount()
9509                         }
9510                     ]
9511                 }
9512             ]
9513         };
9514         
9515         return body;
9516     },
9517     
9518     renderFooter : function()
9519     {
9520         var footer = {
9521             tag: 'tfoot',
9522             cn : [
9523                 {
9524                     tag: 'tr',
9525                     cn : [
9526                         {
9527                             tag : 'td',
9528                             colspan :  this.cm.getColumnCount()
9529                         }
9530                     ]
9531                 }
9532             ]
9533         };
9534         
9535         return footer;
9536     },
9537     
9538     
9539     
9540     onLoad : function()
9541     {
9542 //        Roo.log('ds onload');
9543         this.clear();
9544         
9545         var _this = this;
9546         var cm = this.cm;
9547         var ds = this.store;
9548         
9549         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9550             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9551             if (_this.store.sortInfo) {
9552                     
9553                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9554                     e.select('i', true).addClass(['fa-arrow-up']);
9555                 }
9556                 
9557                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9558                     e.select('i', true).addClass(['fa-arrow-down']);
9559                 }
9560             }
9561         });
9562         
9563         var tbody =  this.bodyEl;
9564               
9565         if(ds.getCount() > 0){
9566             ds.data.each(function(d,rowIndex){
9567                 var row =  this.renderRow(cm, ds, rowIndex);
9568                 
9569                 tbody.createChild(row);
9570                 
9571                 var _this = this;
9572                 
9573                 if(row.cellObjects.length){
9574                     Roo.each(row.cellObjects, function(r){
9575                         _this.renderCellObject(r);
9576                     })
9577                 }
9578                 
9579             }, this);
9580         }
9581         
9582         var tfoot = this.el.select('tfoot', true).first();
9583         
9584         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9585             
9586             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9587             
9588             var total = this.ds.getTotalCount();
9589             
9590             if(this.footer.pageSize < total){
9591                 this.mainFoot.show();
9592             }
9593         }
9594         
9595         Roo.each(this.el.select('tbody td', true).elements, function(e){
9596             e.on('mouseover', _this.onMouseover, _this);
9597         });
9598         
9599         Roo.each(this.el.select('tbody td', true).elements, function(e){
9600             e.on('mouseout', _this.onMouseout, _this);
9601         });
9602         this.fireEvent('rowsrendered', this);
9603         
9604         this.autoSize();
9605         
9606         this.initCSS(); /// resize cols
9607
9608         
9609     },
9610     
9611     
9612     onUpdate : function(ds,record)
9613     {
9614         this.refreshRow(record);
9615         this.autoSize();
9616     },
9617     
9618     onRemove : function(ds, record, index, isUpdate){
9619         if(isUpdate !== true){
9620             this.fireEvent("beforerowremoved", this, index, record);
9621         }
9622         var bt = this.bodyEl.dom;
9623         
9624         var rows = this.el.select('tbody > tr', true).elements;
9625         
9626         if(typeof(rows[index]) != 'undefined'){
9627             bt.removeChild(rows[index].dom);
9628         }
9629         
9630 //        if(bt.rows[index]){
9631 //            bt.removeChild(bt.rows[index]);
9632 //        }
9633         
9634         if(isUpdate !== true){
9635             //this.stripeRows(index);
9636             //this.syncRowHeights(index, index);
9637             //this.layout();
9638             this.fireEvent("rowremoved", this, index, record);
9639         }
9640     },
9641     
9642     onAdd : function(ds, records, rowIndex)
9643     {
9644         //Roo.log('on Add called');
9645         // - note this does not handle multiple adding very well..
9646         var bt = this.bodyEl.dom;
9647         for (var i =0 ; i < records.length;i++) {
9648             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9649             //Roo.log(records[i]);
9650             //Roo.log(this.store.getAt(rowIndex+i));
9651             this.insertRow(this.store, rowIndex + i, false);
9652             return;
9653         }
9654         
9655     },
9656     
9657     
9658     refreshRow : function(record){
9659         var ds = this.store, index;
9660         if(typeof record == 'number'){
9661             index = record;
9662             record = ds.getAt(index);
9663         }else{
9664             index = ds.indexOf(record);
9665             if (index < 0) {
9666                 return; // should not happen - but seems to 
9667             }
9668         }
9669         this.insertRow(ds, index, true);
9670         this.autoSize();
9671         this.onRemove(ds, record, index+1, true);
9672         this.autoSize();
9673         //this.syncRowHeights(index, index);
9674         //this.layout();
9675         this.fireEvent("rowupdated", this, index, record);
9676     },
9677     // private - called by RowSelection
9678     onRowSelect : function(rowIndex){
9679         var row = this.getRowDom(rowIndex);
9680         row.addClass(['bg-info','info']);
9681     },
9682     // private - called by RowSelection
9683     onRowDeselect : function(rowIndex)
9684     {
9685         if (rowIndex < 0) {
9686             return;
9687         }
9688         var row = this.getRowDom(rowIndex);
9689         row.removeClass(['bg-info','info']);
9690     },
9691       /**
9692      * Focuses the specified row.
9693      * @param {Number} row The row index
9694      */
9695     focusRow : function(row)
9696     {
9697         //Roo.log('GridView.focusRow');
9698         var x = this.bodyEl.dom.scrollLeft;
9699         this.focusCell(row, 0, false);
9700         this.bodyEl.dom.scrollLeft = x;
9701
9702     },
9703      /**
9704      * Focuses the specified cell.
9705      * @param {Number} row The row index
9706      * @param {Number} col The column index
9707      * @param {Boolean} hscroll false to disable horizontal scrolling
9708      */
9709     focusCell : function(row, col, hscroll)
9710     {
9711         //Roo.log('GridView.focusCell');
9712         var el = this.ensureVisible(row, col, hscroll);
9713         // not sure what focusEL achives = it's a <a> pos relative 
9714         //this.focusEl.alignTo(el, "tl-tl");
9715         //if(Roo.isGecko){
9716         //    this.focusEl.focus();
9717         //}else{
9718         //    this.focusEl.focus.defer(1, this.focusEl);
9719         //}
9720     },
9721     
9722      /**
9723      * Scrolls the specified cell into view
9724      * @param {Number} row The row index
9725      * @param {Number} col The column index
9726      * @param {Boolean} hscroll false to disable horizontal scrolling
9727      */
9728     ensureVisible : function(row, col, hscroll)
9729     {
9730         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9731         //return null; //disable for testing.
9732         if(typeof row != "number"){
9733             row = row.rowIndex;
9734         }
9735         if(row < 0 && row >= this.ds.getCount()){
9736             return  null;
9737         }
9738         col = (col !== undefined ? col : 0);
9739         var cm = this.cm;
9740         while(cm.isHidden(col)){
9741             col++;
9742         }
9743
9744         var el = this.getCellDom(row, col);
9745         if(!el){
9746             return null;
9747         }
9748         var c = this.bodyEl.dom;
9749
9750         var ctop = parseInt(el.offsetTop, 10);
9751         var cleft = parseInt(el.offsetLeft, 10);
9752         var cbot = ctop + el.offsetHeight;
9753         var cright = cleft + el.offsetWidth;
9754
9755         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9756         var ch = 0; //?? header is not withing the area?
9757         var stop = parseInt(c.scrollTop, 10);
9758         var sleft = parseInt(c.scrollLeft, 10);
9759         var sbot = stop + ch;
9760         var sright = sleft + c.clientWidth;
9761         /*
9762         Roo.log('GridView.ensureVisible:' +
9763                 ' ctop:' + ctop +
9764                 ' c.clientHeight:' + c.clientHeight +
9765                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9766                 ' stop:' + stop +
9767                 ' cbot:' + cbot +
9768                 ' sbot:' + sbot +
9769                 ' ch:' + ch  
9770                 );
9771         */
9772         if(ctop < stop){
9773             c.scrollTop = ctop;
9774             //Roo.log("set scrolltop to ctop DISABLE?");
9775         }else if(cbot > sbot){
9776             //Roo.log("set scrolltop to cbot-ch");
9777             c.scrollTop = cbot-ch;
9778         }
9779
9780         if(hscroll !== false){
9781             if(cleft < sleft){
9782                 c.scrollLeft = cleft;
9783             }else if(cright > sright){
9784                 c.scrollLeft = cright-c.clientWidth;
9785             }
9786         }
9787
9788         return el;
9789     },
9790     
9791     
9792     insertRow : function(dm, rowIndex, isUpdate){
9793         
9794         if(!isUpdate){
9795             this.fireEvent("beforerowsinserted", this, rowIndex);
9796         }
9797             //var s = this.getScrollState();
9798         var row = this.renderRow(this.cm, this.store, rowIndex);
9799         // insert before rowIndex..
9800         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9801         
9802         var _this = this;
9803                 
9804         if(row.cellObjects.length){
9805             Roo.each(row.cellObjects, function(r){
9806                 _this.renderCellObject(r);
9807             })
9808         }
9809             
9810         if(!isUpdate){
9811             this.fireEvent("rowsinserted", this, rowIndex);
9812             //this.syncRowHeights(firstRow, lastRow);
9813             //this.stripeRows(firstRow);
9814             //this.layout();
9815         }
9816         
9817     },
9818     
9819     
9820     getRowDom : function(rowIndex)
9821     {
9822         var rows = this.el.select('tbody > tr', true).elements;
9823         
9824         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9825         
9826     },
9827     getCellDom : function(rowIndex, colIndex)
9828     {
9829         var row = this.getRowDom(rowIndex);
9830         if (row === false) {
9831             return false;
9832         }
9833         var cols = row.select('td', true).elements;
9834         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9835         
9836     },
9837     
9838     // returns the object tree for a tr..
9839   
9840     
9841     renderRow : function(cm, ds, rowIndex) 
9842     {
9843         var d = ds.getAt(rowIndex);
9844         
9845         var row = {
9846             tag : 'tr',
9847             cls : 'x-row-' + rowIndex,
9848             cn : []
9849         };
9850             
9851         var cellObjects = [];
9852         
9853         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9854             var config = cm.config[i];
9855             
9856             var renderer = cm.getRenderer(i);
9857             var value = '';
9858             var id = false;
9859             
9860             if(typeof(renderer) !== 'undefined'){
9861                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9862             }
9863             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9864             // and are rendered into the cells after the row is rendered - using the id for the element.
9865             
9866             if(typeof(value) === 'object'){
9867                 id = Roo.id();
9868                 cellObjects.push({
9869                     container : id,
9870                     cfg : value 
9871                 })
9872             }
9873             
9874             var rowcfg = {
9875                 record: d,
9876                 rowIndex : rowIndex,
9877                 colIndex : i,
9878                 rowClass : ''
9879             };
9880
9881             this.fireEvent('rowclass', this, rowcfg);
9882             
9883             var td = {
9884                 tag: 'td',
9885                 // this might end up displaying HTML?
9886                 // this is too messy... - better to only do it on columsn you know are going to be too long
9887                 //tooltip : (typeof(value) === 'object') ? '' : value,
9888                 cls : rowcfg.rowClass + ' x-col-' + i,
9889                 style: '',
9890                 html: (typeof(value) === 'object') ? '' : value
9891             };
9892             
9893             if (id) {
9894                 td.id = id;
9895             }
9896             
9897             if(typeof(config.colspan) != 'undefined'){
9898                 td.colspan = config.colspan;
9899             }
9900             
9901             
9902             
9903             if(typeof(config.align) != 'undefined' && config.align.length){
9904                 td.style += ' text-align:' + config.align + ';';
9905             }
9906             if(typeof(config.valign) != 'undefined' && config.valign.length){
9907                 td.style += ' vertical-align:' + config.valign + ';';
9908             }
9909             /*
9910             if(typeof(config.width) != 'undefined'){
9911                 td.style += ' width:' +  config.width + 'px;';
9912             }
9913             */
9914             
9915             if(typeof(config.cursor) != 'undefined'){
9916                 td.style += ' cursor:' +  config.cursor + ';';
9917             }
9918             
9919             if(typeof(config.cls) != 'undefined'){
9920                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9921             }
9922             if (this.responsive) {
9923                 ['xs','sm','md','lg'].map(function(size){
9924                     
9925                     if(typeof(config[size]) == 'undefined'){
9926                         return;
9927                     }
9928                     
9929                     
9930                       
9931                     if (!config[size]) { // 0 = hidden
9932                         // BS 4 '0' is treated as hide that column and below.
9933                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9934                         return;
9935                     }
9936                     
9937                     td.cls += ' col-' + size + '-' + config[size] + (
9938                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9939                     );
9940                      
9941     
9942                 });
9943             }
9944             row.cn.push(td);
9945            
9946         }
9947         
9948         row.cellObjects = cellObjects;
9949         
9950         return row;
9951           
9952     },
9953     
9954     
9955     
9956     onBeforeLoad : function()
9957     {
9958         
9959     },
9960      /**
9961      * Remove all rows
9962      */
9963     clear : function()
9964     {
9965         this.el.select('tbody', true).first().dom.innerHTML = '';
9966     },
9967     /**
9968      * Show or hide a row.
9969      * @param {Number} rowIndex to show or hide
9970      * @param {Boolean} state hide
9971      */
9972     setRowVisibility : function(rowIndex, state)
9973     {
9974         var bt = this.bodyEl.dom;
9975         
9976         var rows = this.el.select('tbody > tr', true).elements;
9977         
9978         if(typeof(rows[rowIndex]) == 'undefined'){
9979             return;
9980         }
9981         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9982         
9983     },
9984     
9985     
9986     getSelectionModel : function(){
9987         if(!this.selModel){
9988             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9989         }
9990         return this.selModel;
9991     },
9992     /*
9993      * Render the Roo.bootstrap object from renderder
9994      */
9995     renderCellObject : function(r)
9996     {
9997         var _this = this;
9998         
9999         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10000         
10001         var t = r.cfg.render(r.container);
10002         
10003         if(r.cfg.cn){
10004             Roo.each(r.cfg.cn, function(c){
10005                 var child = {
10006                     container: t.getChildContainer(),
10007                     cfg: c
10008                 };
10009                 _this.renderCellObject(child);
10010             })
10011         }
10012     },
10013     /**
10014      * get the Row Index from a dom element.
10015      * @param {Roo.Element} row The row to look for
10016      * @returns {Number} the row
10017      */
10018     getRowIndex : function(row)
10019     {
10020         var rowIndex = -1;
10021         
10022         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10023             if(el != row){
10024                 return;
10025             }
10026             
10027             rowIndex = index;
10028         });
10029         
10030         return rowIndex;
10031     },
10032     /**
10033      * get the header TH element for columnIndex
10034      * @param {Number} columnIndex
10035      * @returns {Roo.Element}
10036      */
10037     getHeaderIndex: function(colIndex)
10038     {
10039         var cols = this.headEl.select('th', true).elements;
10040         return cols[colIndex]; 
10041     },
10042     /**
10043      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10044      * @param {domElement} cell to look for
10045      * @returns {Number} the column
10046      */
10047     getCellIndex : function(cell)
10048     {
10049         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10050         if(id){
10051             return parseInt(id[1], 10);
10052         }
10053         return 0;
10054     },
10055      /**
10056      * Returns the grid's underlying element = used by panel.Grid
10057      * @return {Element} The element
10058      */
10059     getGridEl : function(){
10060         return this.el;
10061     },
10062      /**
10063      * Forces a resize - used by panel.Grid
10064      * @return {Element} The element
10065      */
10066     autoSize : function()
10067     {
10068         //var ctr = Roo.get(this.container.dom.parentElement);
10069         var ctr = Roo.get(this.el.dom);
10070         
10071         var thd = this.getGridEl().select('thead',true).first();
10072         var tbd = this.getGridEl().select('tbody', true).first();
10073         var tfd = this.getGridEl().select('tfoot', true).first();
10074         
10075         var cw = ctr.getWidth();
10076         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10077         
10078         if (tbd) {
10079             
10080             tbd.setWidth(ctr.getWidth());
10081             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10082             // this needs fixing for various usage - currently only hydra job advers I think..
10083             //tdb.setHeight(
10084             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10085             //); 
10086             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10087             cw -= barsize;
10088         }
10089         cw = Math.max(cw, this.totalWidth);
10090         this.getGridEl().select('tbody tr',true).setWidth(cw);
10091         this.initCSS();
10092         
10093         // resize 'expandable coloumn?
10094         
10095         return; // we doe not have a view in this design..
10096         
10097     },
10098     onBodyScroll: function()
10099     {
10100         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10101         if(this.headEl){
10102             this.headEl.setStyle({
10103                 'position' : 'relative',
10104                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10105             });
10106         }
10107         
10108         if(this.lazyLoad){
10109             
10110             var scrollHeight = this.bodyEl.dom.scrollHeight;
10111             
10112             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10113             
10114             var height = this.bodyEl.getHeight();
10115             
10116             if(scrollHeight - height == scrollTop) {
10117                 
10118                 var total = this.ds.getTotalCount();
10119                 
10120                 if(this.footer.cursor + this.footer.pageSize < total){
10121                     
10122                     this.footer.ds.load({
10123                         params : {
10124                             start : this.footer.cursor + this.footer.pageSize,
10125                             limit : this.footer.pageSize
10126                         },
10127                         add : true
10128                     });
10129                 }
10130             }
10131             
10132         }
10133     },
10134     onColumnSplitterMoved : function(i, diff)
10135     {
10136         this.userResized = true;
10137         
10138         var cm = this.colModel;
10139         
10140         var w = this.getHeaderIndex(i).getWidth() + diff;
10141         
10142         
10143         cm.setColumnWidth(i, w, true);
10144         this.initCSS();
10145         //var cid = cm.getColumnId(i); << not used in this version?
10146        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10147         
10148         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10149         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10150         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10151 */
10152         //this.updateSplitters();
10153         //this.layout(); << ??
10154         this.fireEvent("columnresize", i, w);
10155     },
10156     onHeaderChange : function()
10157     {
10158         var header = this.renderHeader();
10159         var table = this.el.select('table', true).first();
10160         
10161         this.headEl.remove();
10162         this.headEl = table.createChild(header, this.bodyEl, false);
10163         
10164         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10165             e.on('click', this.sort, this);
10166         }, this);
10167         
10168         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10169             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10170         }
10171         
10172     },
10173     
10174     onHiddenChange : function(colModel, colIndex, hidden)
10175     {
10176         /*
10177         this.cm.setHidden()
10178         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10179         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10180         
10181         this.CSS.updateRule(thSelector, "display", "");
10182         this.CSS.updateRule(tdSelector, "display", "");
10183         
10184         if(hidden){
10185             this.CSS.updateRule(thSelector, "display", "none");
10186             this.CSS.updateRule(tdSelector, "display", "none");
10187         }
10188         */
10189         // onload calls initCSS()
10190         this.onHeaderChange();
10191         this.onLoad();
10192     },
10193     
10194     setColumnWidth: function(col_index, width)
10195     {
10196         // width = "md-2 xs-2..."
10197         if(!this.colModel.config[col_index]) {
10198             return;
10199         }
10200         
10201         var w = width.split(" ");
10202         
10203         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10204         
10205         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10206         
10207         
10208         for(var j = 0; j < w.length; j++) {
10209             
10210             if(!w[j]) {
10211                 continue;
10212             }
10213             
10214             var size_cls = w[j].split("-");
10215             
10216             if(!Number.isInteger(size_cls[1] * 1)) {
10217                 continue;
10218             }
10219             
10220             if(!this.colModel.config[col_index][size_cls[0]]) {
10221                 continue;
10222             }
10223             
10224             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10225                 continue;
10226             }
10227             
10228             h_row[0].classList.replace(
10229                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10230                 "col-"+size_cls[0]+"-"+size_cls[1]
10231             );
10232             
10233             for(var i = 0; i < rows.length; i++) {
10234                 
10235                 var size_cls = w[j].split("-");
10236                 
10237                 if(!Number.isInteger(size_cls[1] * 1)) {
10238                     continue;
10239                 }
10240                 
10241                 if(!this.colModel.config[col_index][size_cls[0]]) {
10242                     continue;
10243                 }
10244                 
10245                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10246                     continue;
10247                 }
10248                 
10249                 rows[i].classList.replace(
10250                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10251                     "col-"+size_cls[0]+"-"+size_cls[1]
10252                 );
10253             }
10254             
10255             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10256         }
10257     }
10258 });
10259
10260 // currently only used to find the split on drag.. 
10261 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10262
10263 /**
10264  * @depricated
10265 */
10266 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10267 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10268 /*
10269  * - LGPL
10270  *
10271  * table cell
10272  * 
10273  */
10274
10275 /**
10276  * @class Roo.bootstrap.TableCell
10277  * @extends Roo.bootstrap.Component
10278  * Bootstrap TableCell class
10279  * @cfg {String} html cell contain text
10280  * @cfg {String} cls cell class
10281  * @cfg {String} tag cell tag (td|th) default td
10282  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10283  * @cfg {String} align Aligns the content in a cell
10284  * @cfg {String} axis Categorizes cells
10285  * @cfg {String} bgcolor Specifies the background color of a cell
10286  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10287  * @cfg {Number} colspan Specifies the number of columns a cell should span
10288  * @cfg {String} headers Specifies one or more header cells a cell is related to
10289  * @cfg {Number} height Sets the height of a cell
10290  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10291  * @cfg {Number} rowspan Sets the number of rows a cell should span
10292  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10293  * @cfg {String} valign Vertical aligns the content in a cell
10294  * @cfg {Number} width Specifies the width of a cell
10295  * 
10296  * @constructor
10297  * Create a new TableCell
10298  * @param {Object} config The config object
10299  */
10300
10301 Roo.bootstrap.TableCell = function(config){
10302     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10303 };
10304
10305 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10306     
10307     html: false,
10308     cls: false,
10309     tag: false,
10310     abbr: false,
10311     align: false,
10312     axis: false,
10313     bgcolor: false,
10314     charoff: false,
10315     colspan: false,
10316     headers: false,
10317     height: false,
10318     nowrap: false,
10319     rowspan: false,
10320     scope: false,
10321     valign: false,
10322     width: false,
10323     
10324     
10325     getAutoCreate : function(){
10326         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10327         
10328         cfg = {
10329             tag: 'td'
10330         };
10331         
10332         if(this.tag){
10333             cfg.tag = this.tag;
10334         }
10335         
10336         if (this.html) {
10337             cfg.html=this.html
10338         }
10339         if (this.cls) {
10340             cfg.cls=this.cls
10341         }
10342         if (this.abbr) {
10343             cfg.abbr=this.abbr
10344         }
10345         if (this.align) {
10346             cfg.align=this.align
10347         }
10348         if (this.axis) {
10349             cfg.axis=this.axis
10350         }
10351         if (this.bgcolor) {
10352             cfg.bgcolor=this.bgcolor
10353         }
10354         if (this.charoff) {
10355             cfg.charoff=this.charoff
10356         }
10357         if (this.colspan) {
10358             cfg.colspan=this.colspan
10359         }
10360         if (this.headers) {
10361             cfg.headers=this.headers
10362         }
10363         if (this.height) {
10364             cfg.height=this.height
10365         }
10366         if (this.nowrap) {
10367             cfg.nowrap=this.nowrap
10368         }
10369         if (this.rowspan) {
10370             cfg.rowspan=this.rowspan
10371         }
10372         if (this.scope) {
10373             cfg.scope=this.scope
10374         }
10375         if (this.valign) {
10376             cfg.valign=this.valign
10377         }
10378         if (this.width) {
10379             cfg.width=this.width
10380         }
10381         
10382         
10383         return cfg;
10384     }
10385    
10386 });
10387
10388  
10389
10390  /*
10391  * - LGPL
10392  *
10393  * table row
10394  * 
10395  */
10396
10397 /**
10398  * @class Roo.bootstrap.TableRow
10399  * @extends Roo.bootstrap.Component
10400  * Bootstrap TableRow class
10401  * @cfg {String} cls row class
10402  * @cfg {String} align Aligns the content in a table row
10403  * @cfg {String} bgcolor Specifies a background color for a table row
10404  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10405  * @cfg {String} valign Vertical aligns the content in a table row
10406  * 
10407  * @constructor
10408  * Create a new TableRow
10409  * @param {Object} config The config object
10410  */
10411
10412 Roo.bootstrap.TableRow = function(config){
10413     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10414 };
10415
10416 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10417     
10418     cls: false,
10419     align: false,
10420     bgcolor: false,
10421     charoff: false,
10422     valign: false,
10423     
10424     getAutoCreate : function(){
10425         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10426         
10427         cfg = {
10428             tag: 'tr'
10429         };
10430             
10431         if(this.cls){
10432             cfg.cls = this.cls;
10433         }
10434         if(this.align){
10435             cfg.align = this.align;
10436         }
10437         if(this.bgcolor){
10438             cfg.bgcolor = this.bgcolor;
10439         }
10440         if(this.charoff){
10441             cfg.charoff = this.charoff;
10442         }
10443         if(this.valign){
10444             cfg.valign = this.valign;
10445         }
10446         
10447         return cfg;
10448     }
10449    
10450 });
10451
10452  
10453
10454  /*
10455  * - LGPL
10456  *
10457  * table body
10458  * 
10459  */
10460
10461 /**
10462  * @class Roo.bootstrap.TableBody
10463  * @extends Roo.bootstrap.Component
10464  * Bootstrap TableBody class
10465  * @cfg {String} cls element class
10466  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10467  * @cfg {String} align Aligns the content inside the element
10468  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10469  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10470  * 
10471  * @constructor
10472  * Create a new TableBody
10473  * @param {Object} config The config object
10474  */
10475
10476 Roo.bootstrap.TableBody = function(config){
10477     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10478 };
10479
10480 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10481     
10482     cls: false,
10483     tag: false,
10484     align: false,
10485     charoff: false,
10486     valign: false,
10487     
10488     getAutoCreate : function(){
10489         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10490         
10491         cfg = {
10492             tag: 'tbody'
10493         };
10494             
10495         if (this.cls) {
10496             cfg.cls=this.cls
10497         }
10498         if(this.tag){
10499             cfg.tag = this.tag;
10500         }
10501         
10502         if(this.align){
10503             cfg.align = this.align;
10504         }
10505         if(this.charoff){
10506             cfg.charoff = this.charoff;
10507         }
10508         if(this.valign){
10509             cfg.valign = this.valign;
10510         }
10511         
10512         return cfg;
10513     }
10514     
10515     
10516 //    initEvents : function()
10517 //    {
10518 //        
10519 //        if(!this.store){
10520 //            return;
10521 //        }
10522 //        
10523 //        this.store = Roo.factory(this.store, Roo.data);
10524 //        this.store.on('load', this.onLoad, this);
10525 //        
10526 //        this.store.load();
10527 //        
10528 //    },
10529 //    
10530 //    onLoad: function () 
10531 //    {   
10532 //        this.fireEvent('load', this);
10533 //    }
10534 //    
10535 //   
10536 });
10537
10538  
10539
10540  /*
10541  * Based on:
10542  * Ext JS Library 1.1.1
10543  * Copyright(c) 2006-2007, Ext JS, LLC.
10544  *
10545  * Originally Released Under LGPL - original licence link has changed is not relivant.
10546  *
10547  * Fork - LGPL
10548  * <script type="text/javascript">
10549  */
10550
10551 // as we use this in bootstrap.
10552 Roo.namespace('Roo.form');
10553  /**
10554  * @class Roo.form.Action
10555  * Internal Class used to handle form actions
10556  * @constructor
10557  * @param {Roo.form.BasicForm} el The form element or its id
10558  * @param {Object} config Configuration options
10559  */
10560
10561  
10562  
10563 // define the action interface
10564 Roo.form.Action = function(form, options){
10565     this.form = form;
10566     this.options = options || {};
10567 };
10568 /**
10569  * Client Validation Failed
10570  * @const 
10571  */
10572 Roo.form.Action.CLIENT_INVALID = 'client';
10573 /**
10574  * Server Validation Failed
10575  * @const 
10576  */
10577 Roo.form.Action.SERVER_INVALID = 'server';
10578  /**
10579  * Connect to Server Failed
10580  * @const 
10581  */
10582 Roo.form.Action.CONNECT_FAILURE = 'connect';
10583 /**
10584  * Reading Data from Server Failed
10585  * @const 
10586  */
10587 Roo.form.Action.LOAD_FAILURE = 'load';
10588
10589 Roo.form.Action.prototype = {
10590     type : 'default',
10591     failureType : undefined,
10592     response : undefined,
10593     result : undefined,
10594
10595     // interface method
10596     run : function(options){
10597
10598     },
10599
10600     // interface method
10601     success : function(response){
10602
10603     },
10604
10605     // interface method
10606     handleResponse : function(response){
10607
10608     },
10609
10610     // default connection failure
10611     failure : function(response){
10612         
10613         this.response = response;
10614         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10615         this.form.afterAction(this, false);
10616     },
10617
10618     processResponse : function(response){
10619         this.response = response;
10620         if(!response.responseText){
10621             return true;
10622         }
10623         this.result = this.handleResponse(response);
10624         return this.result;
10625     },
10626
10627     // utility functions used internally
10628     getUrl : function(appendParams){
10629         var url = this.options.url || this.form.url || this.form.el.dom.action;
10630         if(appendParams){
10631             var p = this.getParams();
10632             if(p){
10633                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10634             }
10635         }
10636         return url;
10637     },
10638
10639     getMethod : function(){
10640         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10641     },
10642
10643     getParams : function(){
10644         var bp = this.form.baseParams;
10645         var p = this.options.params;
10646         if(p){
10647             if(typeof p == "object"){
10648                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10649             }else if(typeof p == 'string' && bp){
10650                 p += '&' + Roo.urlEncode(bp);
10651             }
10652         }else if(bp){
10653             p = Roo.urlEncode(bp);
10654         }
10655         return p;
10656     },
10657
10658     createCallback : function(){
10659         return {
10660             success: this.success,
10661             failure: this.failure,
10662             scope: this,
10663             timeout: (this.form.timeout*1000),
10664             upload: this.form.fileUpload ? this.success : undefined
10665         };
10666     }
10667 };
10668
10669 Roo.form.Action.Submit = function(form, options){
10670     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10671 };
10672
10673 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10674     type : 'submit',
10675
10676     haveProgress : false,
10677     uploadComplete : false,
10678     
10679     // uploadProgress indicator.
10680     uploadProgress : function()
10681     {
10682         if (!this.form.progressUrl) {
10683             return;
10684         }
10685         
10686         if (!this.haveProgress) {
10687             Roo.MessageBox.progress("Uploading", "Uploading");
10688         }
10689         if (this.uploadComplete) {
10690            Roo.MessageBox.hide();
10691            return;
10692         }
10693         
10694         this.haveProgress = true;
10695    
10696         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10697         
10698         var c = new Roo.data.Connection();
10699         c.request({
10700             url : this.form.progressUrl,
10701             params: {
10702                 id : uid
10703             },
10704             method: 'GET',
10705             success : function(req){
10706                //console.log(data);
10707                 var rdata = false;
10708                 var edata;
10709                 try  {
10710                    rdata = Roo.decode(req.responseText)
10711                 } catch (e) {
10712                     Roo.log("Invalid data from server..");
10713                     Roo.log(edata);
10714                     return;
10715                 }
10716                 if (!rdata || !rdata.success) {
10717                     Roo.log(rdata);
10718                     Roo.MessageBox.alert(Roo.encode(rdata));
10719                     return;
10720                 }
10721                 var data = rdata.data;
10722                 
10723                 if (this.uploadComplete) {
10724                    Roo.MessageBox.hide();
10725                    return;
10726                 }
10727                    
10728                 if (data){
10729                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10730                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10731                     );
10732                 }
10733                 this.uploadProgress.defer(2000,this);
10734             },
10735        
10736             failure: function(data) {
10737                 Roo.log('progress url failed ');
10738                 Roo.log(data);
10739             },
10740             scope : this
10741         });
10742            
10743     },
10744     
10745     
10746     run : function()
10747     {
10748         // run get Values on the form, so it syncs any secondary forms.
10749         this.form.getValues();
10750         
10751         var o = this.options;
10752         var method = this.getMethod();
10753         var isPost = method == 'POST';
10754         if(o.clientValidation === false || this.form.isValid()){
10755             
10756             if (this.form.progressUrl) {
10757                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10758                     (new Date() * 1) + '' + Math.random());
10759                     
10760             } 
10761             
10762             
10763             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10764                 form:this.form.el.dom,
10765                 url:this.getUrl(!isPost),
10766                 method: method,
10767                 params:isPost ? this.getParams() : null,
10768                 isUpload: this.form.fileUpload,
10769                 formData : this.form.formData
10770             }));
10771             
10772             this.uploadProgress();
10773
10774         }else if (o.clientValidation !== false){ // client validation failed
10775             this.failureType = Roo.form.Action.CLIENT_INVALID;
10776             this.form.afterAction(this, false);
10777         }
10778     },
10779
10780     success : function(response)
10781     {
10782         this.uploadComplete= true;
10783         if (this.haveProgress) {
10784             Roo.MessageBox.hide();
10785         }
10786         
10787         
10788         var result = this.processResponse(response);
10789         if(result === true || result.success){
10790             this.form.afterAction(this, true);
10791             return;
10792         }
10793         if(result.errors){
10794             this.form.markInvalid(result.errors);
10795             this.failureType = Roo.form.Action.SERVER_INVALID;
10796         }
10797         this.form.afterAction(this, false);
10798     },
10799     failure : function(response)
10800     {
10801         this.uploadComplete= true;
10802         if (this.haveProgress) {
10803             Roo.MessageBox.hide();
10804         }
10805         
10806         this.response = response;
10807         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10808         this.form.afterAction(this, false);
10809     },
10810     
10811     handleResponse : function(response){
10812         if(this.form.errorReader){
10813             var rs = this.form.errorReader.read(response);
10814             var errors = [];
10815             if(rs.records){
10816                 for(var i = 0, len = rs.records.length; i < len; i++) {
10817                     var r = rs.records[i];
10818                     errors[i] = r.data;
10819                 }
10820             }
10821             if(errors.length < 1){
10822                 errors = null;
10823             }
10824             return {
10825                 success : rs.success,
10826                 errors : errors
10827             };
10828         }
10829         var ret = false;
10830         try {
10831             ret = Roo.decode(response.responseText);
10832         } catch (e) {
10833             ret = {
10834                 success: false,
10835                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10836                 errors : []
10837             };
10838         }
10839         return ret;
10840         
10841     }
10842 });
10843
10844
10845 Roo.form.Action.Load = function(form, options){
10846     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10847     this.reader = this.form.reader;
10848 };
10849
10850 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10851     type : 'load',
10852
10853     run : function(){
10854         
10855         Roo.Ajax.request(Roo.apply(
10856                 this.createCallback(), {
10857                     method:this.getMethod(),
10858                     url:this.getUrl(false),
10859                     params:this.getParams()
10860         }));
10861     },
10862
10863     success : function(response){
10864         
10865         var result = this.processResponse(response);
10866         if(result === true || !result.success || !result.data){
10867             this.failureType = Roo.form.Action.LOAD_FAILURE;
10868             this.form.afterAction(this, false);
10869             return;
10870         }
10871         this.form.clearInvalid();
10872         this.form.setValues(result.data);
10873         this.form.afterAction(this, true);
10874     },
10875
10876     handleResponse : function(response){
10877         if(this.form.reader){
10878             var rs = this.form.reader.read(response);
10879             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10880             return {
10881                 success : rs.success,
10882                 data : data
10883             };
10884         }
10885         return Roo.decode(response.responseText);
10886     }
10887 });
10888
10889 Roo.form.Action.ACTION_TYPES = {
10890     'load' : Roo.form.Action.Load,
10891     'submit' : Roo.form.Action.Submit
10892 };/*
10893  * - LGPL
10894  *
10895  * form
10896  *
10897  */
10898
10899 /**
10900  * @class Roo.bootstrap.Form
10901  * @extends Roo.bootstrap.Component
10902  * Bootstrap Form class
10903  * @cfg {String} method  GET | POST (default POST)
10904  * @cfg {String} labelAlign top | left (default top)
10905  * @cfg {String} align left  | right - for navbars
10906  * @cfg {Boolean} loadMask load mask when submit (default true)
10907
10908  *
10909  * @constructor
10910  * Create a new Form
10911  * @param {Object} config The config object
10912  */
10913
10914
10915 Roo.bootstrap.Form = function(config){
10916     
10917     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10918     
10919     Roo.bootstrap.Form.popover.apply();
10920     
10921     this.addEvents({
10922         /**
10923          * @event clientvalidation
10924          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10925          * @param {Form} this
10926          * @param {Boolean} valid true if the form has passed client-side validation
10927          */
10928         clientvalidation: true,
10929         /**
10930          * @event beforeaction
10931          * Fires before any action is performed. Return false to cancel the action.
10932          * @param {Form} this
10933          * @param {Action} action The action to be performed
10934          */
10935         beforeaction: true,
10936         /**
10937          * @event actionfailed
10938          * Fires when an action fails.
10939          * @param {Form} this
10940          * @param {Action} action The action that failed
10941          */
10942         actionfailed : true,
10943         /**
10944          * @event actioncomplete
10945          * Fires when an action is completed.
10946          * @param {Form} this
10947          * @param {Action} action The action that completed
10948          */
10949         actioncomplete : true
10950     });
10951 };
10952
10953 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10954
10955      /**
10956      * @cfg {String} method
10957      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10958      */
10959     method : 'POST',
10960     /**
10961      * @cfg {String} url
10962      * The URL to use for form actions if one isn't supplied in the action options.
10963      */
10964     /**
10965      * @cfg {Boolean} fileUpload
10966      * Set to true if this form is a file upload.
10967      */
10968
10969     /**
10970      * @cfg {Object} baseParams
10971      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10972      */
10973
10974     /**
10975      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10976      */
10977     timeout: 30,
10978     /**
10979      * @cfg {Sting} align (left|right) for navbar forms
10980      */
10981     align : 'left',
10982
10983     // private
10984     activeAction : null,
10985
10986     /**
10987      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10988      * element by passing it or its id or mask the form itself by passing in true.
10989      * @type Mixed
10990      */
10991     waitMsgTarget : false,
10992
10993     loadMask : true,
10994     
10995     /**
10996      * @cfg {Boolean} errorMask (true|false) default false
10997      */
10998     errorMask : false,
10999     
11000     /**
11001      * @cfg {Number} maskOffset Default 100
11002      */
11003     maskOffset : 100,
11004     
11005     /**
11006      * @cfg {Boolean} maskBody
11007      */
11008     maskBody : false,
11009
11010     getAutoCreate : function(){
11011
11012         var cfg = {
11013             tag: 'form',
11014             method : this.method || 'POST',
11015             id : this.id || Roo.id(),
11016             cls : ''
11017         };
11018         if (this.parent().xtype.match(/^Nav/)) {
11019             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11020
11021         }
11022
11023         if (this.labelAlign == 'left' ) {
11024             cfg.cls += ' form-horizontal';
11025         }
11026
11027
11028         return cfg;
11029     },
11030     initEvents : function()
11031     {
11032         this.el.on('submit', this.onSubmit, this);
11033         // this was added as random key presses on the form where triggering form submit.
11034         this.el.on('keypress', function(e) {
11035             if (e.getCharCode() != 13) {
11036                 return true;
11037             }
11038             // we might need to allow it for textareas.. and some other items.
11039             // check e.getTarget().
11040
11041             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11042                 return true;
11043             }
11044
11045             Roo.log("keypress blocked");
11046
11047             e.preventDefault();
11048             return false;
11049         });
11050         
11051     },
11052     // private
11053     onSubmit : function(e){
11054         e.stopEvent();
11055     },
11056
11057      /**
11058      * Returns true if client-side validation on the form is successful.
11059      * @return Boolean
11060      */
11061     isValid : function(){
11062         var items = this.getItems();
11063         var valid = true;
11064         var target = false;
11065         
11066         items.each(function(f){
11067             
11068             if(f.validate()){
11069                 return;
11070             }
11071             
11072             Roo.log('invalid field: ' + f.name);
11073             
11074             valid = false;
11075
11076             if(!target && f.el.isVisible(true)){
11077                 target = f;
11078             }
11079            
11080         });
11081         
11082         if(this.errorMask && !valid){
11083             Roo.bootstrap.Form.popover.mask(this, target);
11084         }
11085         
11086         return valid;
11087     },
11088     
11089     /**
11090      * Returns true if any fields in this form have changed since their original load.
11091      * @return Boolean
11092      */
11093     isDirty : function(){
11094         var dirty = false;
11095         var items = this.getItems();
11096         items.each(function(f){
11097            if(f.isDirty()){
11098                dirty = true;
11099                return false;
11100            }
11101            return true;
11102         });
11103         return dirty;
11104     },
11105      /**
11106      * Performs a predefined action (submit or load) or custom actions you define on this form.
11107      * @param {String} actionName The name of the action type
11108      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11109      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11110      * accept other config options):
11111      * <pre>
11112 Property          Type             Description
11113 ----------------  ---------------  ----------------------------------------------------------------------------------
11114 url               String           The url for the action (defaults to the form's url)
11115 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11116 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11117 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11118                                    validate the form on the client (defaults to false)
11119      * </pre>
11120      * @return {BasicForm} this
11121      */
11122     doAction : function(action, options){
11123         if(typeof action == 'string'){
11124             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11125         }
11126         if(this.fireEvent('beforeaction', this, action) !== false){
11127             this.beforeAction(action);
11128             action.run.defer(100, action);
11129         }
11130         return this;
11131     },
11132
11133     // private
11134     beforeAction : function(action){
11135         var o = action.options;
11136         
11137         if(this.loadMask){
11138             
11139             if(this.maskBody){
11140                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11141             } else {
11142                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11143             }
11144         }
11145         // not really supported yet.. ??
11146
11147         //if(this.waitMsgTarget === true){
11148         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11149         //}else if(this.waitMsgTarget){
11150         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11151         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11152         //}else {
11153         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11154        // }
11155
11156     },
11157
11158     // private
11159     afterAction : function(action, success){
11160         this.activeAction = null;
11161         var o = action.options;
11162
11163         if(this.loadMask){
11164             
11165             if(this.maskBody){
11166                 Roo.get(document.body).unmask();
11167             } else {
11168                 this.el.unmask();
11169             }
11170         }
11171         
11172         //if(this.waitMsgTarget === true){
11173 //            this.el.unmask();
11174         //}else if(this.waitMsgTarget){
11175         //    this.waitMsgTarget.unmask();
11176         //}else{
11177         //    Roo.MessageBox.updateProgress(1);
11178         //    Roo.MessageBox.hide();
11179        // }
11180         //
11181         if(success){
11182             if(o.reset){
11183                 this.reset();
11184             }
11185             Roo.callback(o.success, o.scope, [this, action]);
11186             this.fireEvent('actioncomplete', this, action);
11187
11188         }else{
11189
11190             // failure condition..
11191             // we have a scenario where updates need confirming.
11192             // eg. if a locking scenario exists..
11193             // we look for { errors : { needs_confirm : true }} in the response.
11194             if (
11195                 (typeof(action.result) != 'undefined')  &&
11196                 (typeof(action.result.errors) != 'undefined')  &&
11197                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11198            ){
11199                 var _t = this;
11200                 Roo.log("not supported yet");
11201                  /*
11202
11203                 Roo.MessageBox.confirm(
11204                     "Change requires confirmation",
11205                     action.result.errorMsg,
11206                     function(r) {
11207                         if (r != 'yes') {
11208                             return;
11209                         }
11210                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11211                     }
11212
11213                 );
11214                 */
11215
11216
11217                 return;
11218             }
11219
11220             Roo.callback(o.failure, o.scope, [this, action]);
11221             // show an error message if no failed handler is set..
11222             if (!this.hasListener('actionfailed')) {
11223                 Roo.log("need to add dialog support");
11224                 /*
11225                 Roo.MessageBox.alert("Error",
11226                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11227                         action.result.errorMsg :
11228                         "Saving Failed, please check your entries or try again"
11229                 );
11230                 */
11231             }
11232
11233             this.fireEvent('actionfailed', this, action);
11234         }
11235
11236     },
11237     /**
11238      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11239      * @param {String} id The value to search for
11240      * @return Field
11241      */
11242     findField : function(id){
11243         var items = this.getItems();
11244         var field = items.get(id);
11245         if(!field){
11246              items.each(function(f){
11247                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11248                     field = f;
11249                     return false;
11250                 }
11251                 return true;
11252             });
11253         }
11254         return field || null;
11255     },
11256      /**
11257      * Mark fields in this form invalid in bulk.
11258      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11259      * @return {BasicForm} this
11260      */
11261     markInvalid : function(errors){
11262         if(errors instanceof Array){
11263             for(var i = 0, len = errors.length; i < len; i++){
11264                 var fieldError = errors[i];
11265                 var f = this.findField(fieldError.id);
11266                 if(f){
11267                     f.markInvalid(fieldError.msg);
11268                 }
11269             }
11270         }else{
11271             var field, id;
11272             for(id in errors){
11273                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11274                     field.markInvalid(errors[id]);
11275                 }
11276             }
11277         }
11278         //Roo.each(this.childForms || [], function (f) {
11279         //    f.markInvalid(errors);
11280         //});
11281
11282         return this;
11283     },
11284
11285     /**
11286      * Set values for fields in this form in bulk.
11287      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11288      * @return {BasicForm} this
11289      */
11290     setValues : function(values){
11291         if(values instanceof Array){ // array of objects
11292             for(var i = 0, len = values.length; i < len; i++){
11293                 var v = values[i];
11294                 var f = this.findField(v.id);
11295                 if(f){
11296                     f.setValue(v.value);
11297                     if(this.trackResetOnLoad){
11298                         f.originalValue = f.getValue();
11299                     }
11300                 }
11301             }
11302         }else{ // object hash
11303             var field, id;
11304             for(id in values){
11305                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11306
11307                     if (field.setFromData &&
11308                         field.valueField &&
11309                         field.displayField &&
11310                         // combos' with local stores can
11311                         // be queried via setValue()
11312                         // to set their value..
11313                         (field.store && !field.store.isLocal)
11314                         ) {
11315                         // it's a combo
11316                         var sd = { };
11317                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11318                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11319                         field.setFromData(sd);
11320
11321                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11322                         
11323                         field.setFromData(values);
11324                         
11325                     } else {
11326                         field.setValue(values[id]);
11327                     }
11328
11329
11330                     if(this.trackResetOnLoad){
11331                         field.originalValue = field.getValue();
11332                     }
11333                 }
11334             }
11335         }
11336
11337         //Roo.each(this.childForms || [], function (f) {
11338         //    f.setValues(values);
11339         //});
11340
11341         return this;
11342     },
11343
11344     /**
11345      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11346      * they are returned as an array.
11347      * @param {Boolean} asString
11348      * @return {Object}
11349      */
11350     getValues : function(asString){
11351         //if (this.childForms) {
11352             // copy values from the child forms
11353         //    Roo.each(this.childForms, function (f) {
11354         //        this.setValues(f.getValues());
11355         //    }, this);
11356         //}
11357
11358
11359
11360         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11361         if(asString === true){
11362             return fs;
11363         }
11364         return Roo.urlDecode(fs);
11365     },
11366
11367     /**
11368      * Returns the fields in this form as an object with key/value pairs.
11369      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11370      * @return {Object}
11371      */
11372     getFieldValues : function(with_hidden)
11373     {
11374         var items = this.getItems();
11375         var ret = {};
11376         items.each(function(f){
11377             
11378             if (!f.getName()) {
11379                 return;
11380             }
11381             
11382             var v = f.getValue();
11383             
11384             if (f.inputType =='radio') {
11385                 if (typeof(ret[f.getName()]) == 'undefined') {
11386                     ret[f.getName()] = ''; // empty..
11387                 }
11388
11389                 if (!f.el.dom.checked) {
11390                     return;
11391
11392                 }
11393                 v = f.el.dom.value;
11394
11395             }
11396             
11397             if(f.xtype == 'MoneyField'){
11398                 ret[f.currencyName] = f.getCurrency();
11399             }
11400
11401             // not sure if this supported any more..
11402             if ((typeof(v) == 'object') && f.getRawValue) {
11403                 v = f.getRawValue() ; // dates..
11404             }
11405             // combo boxes where name != hiddenName...
11406             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11407                 ret[f.name] = f.getRawValue();
11408             }
11409             ret[f.getName()] = v;
11410         });
11411
11412         return ret;
11413     },
11414
11415     /**
11416      * Clears all invalid messages in this form.
11417      * @return {BasicForm} this
11418      */
11419     clearInvalid : function(){
11420         var items = this.getItems();
11421
11422         items.each(function(f){
11423            f.clearInvalid();
11424         });
11425
11426         return this;
11427     },
11428
11429     /**
11430      * Resets this form.
11431      * @return {BasicForm} this
11432      */
11433     reset : function(){
11434         var items = this.getItems();
11435         items.each(function(f){
11436             f.reset();
11437         });
11438
11439         Roo.each(this.childForms || [], function (f) {
11440             f.reset();
11441         });
11442
11443
11444         return this;
11445     },
11446     
11447     getItems : function()
11448     {
11449         var r=new Roo.util.MixedCollection(false, function(o){
11450             return o.id || (o.id = Roo.id());
11451         });
11452         var iter = function(el) {
11453             if (el.inputEl) {
11454                 r.add(el);
11455             }
11456             if (!el.items) {
11457                 return;
11458             }
11459             Roo.each(el.items,function(e) {
11460                 iter(e);
11461             });
11462         };
11463
11464         iter(this);
11465         return r;
11466     },
11467     
11468     hideFields : function(items)
11469     {
11470         Roo.each(items, function(i){
11471             
11472             var f = this.findField(i);
11473             
11474             if(!f){
11475                 return;
11476             }
11477             
11478             f.hide();
11479             
11480         }, this);
11481     },
11482     
11483     showFields : function(items)
11484     {
11485         Roo.each(items, function(i){
11486             
11487             var f = this.findField(i);
11488             
11489             if(!f){
11490                 return;
11491             }
11492             
11493             f.show();
11494             
11495         }, this);
11496     }
11497
11498 });
11499
11500 Roo.apply(Roo.bootstrap.Form, {
11501     
11502     popover : {
11503         
11504         padding : 5,
11505         
11506         isApplied : false,
11507         
11508         isMasked : false,
11509         
11510         form : false,
11511         
11512         target : false,
11513         
11514         toolTip : false,
11515         
11516         intervalID : false,
11517         
11518         maskEl : false,
11519         
11520         apply : function()
11521         {
11522             if(this.isApplied){
11523                 return;
11524             }
11525             
11526             this.maskEl = {
11527                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11528                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11529                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11530                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11531             };
11532             
11533             this.maskEl.top.enableDisplayMode("block");
11534             this.maskEl.left.enableDisplayMode("block");
11535             this.maskEl.bottom.enableDisplayMode("block");
11536             this.maskEl.right.enableDisplayMode("block");
11537             
11538             this.toolTip = new Roo.bootstrap.Tooltip({
11539                 cls : 'roo-form-error-popover',
11540                 alignment : {
11541                     'left' : ['r-l', [-2,0], 'right'],
11542                     'right' : ['l-r', [2,0], 'left'],
11543                     'bottom' : ['tl-bl', [0,2], 'top'],
11544                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11545                 }
11546             });
11547             
11548             this.toolTip.render(Roo.get(document.body));
11549
11550             this.toolTip.el.enableDisplayMode("block");
11551             
11552             Roo.get(document.body).on('click', function(){
11553                 this.unmask();
11554             }, this);
11555             
11556             Roo.get(document.body).on('touchstart', function(){
11557                 this.unmask();
11558             }, this);
11559             
11560             this.isApplied = true
11561         },
11562         
11563         mask : function(form, target)
11564         {
11565             this.form = form;
11566             
11567             this.target = target;
11568             
11569             if(!this.form.errorMask || !target.el){
11570                 return;
11571             }
11572             
11573             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11574             
11575             Roo.log(scrollable);
11576             
11577             var ot = this.target.el.calcOffsetsTo(scrollable);
11578             
11579             var scrollTo = ot[1] - this.form.maskOffset;
11580             
11581             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11582             
11583             scrollable.scrollTo('top', scrollTo);
11584             
11585             var box = this.target.el.getBox();
11586             Roo.log(box);
11587             var zIndex = Roo.bootstrap.Modal.zIndex++;
11588
11589             
11590             this.maskEl.top.setStyle('position', 'absolute');
11591             this.maskEl.top.setStyle('z-index', zIndex);
11592             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11593             this.maskEl.top.setLeft(0);
11594             this.maskEl.top.setTop(0);
11595             this.maskEl.top.show();
11596             
11597             this.maskEl.left.setStyle('position', 'absolute');
11598             this.maskEl.left.setStyle('z-index', zIndex);
11599             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11600             this.maskEl.left.setLeft(0);
11601             this.maskEl.left.setTop(box.y - this.padding);
11602             this.maskEl.left.show();
11603
11604             this.maskEl.bottom.setStyle('position', 'absolute');
11605             this.maskEl.bottom.setStyle('z-index', zIndex);
11606             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11607             this.maskEl.bottom.setLeft(0);
11608             this.maskEl.bottom.setTop(box.bottom + this.padding);
11609             this.maskEl.bottom.show();
11610
11611             this.maskEl.right.setStyle('position', 'absolute');
11612             this.maskEl.right.setStyle('z-index', zIndex);
11613             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11614             this.maskEl.right.setLeft(box.right + this.padding);
11615             this.maskEl.right.setTop(box.y - this.padding);
11616             this.maskEl.right.show();
11617
11618             this.toolTip.bindEl = this.target.el;
11619
11620             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11621
11622             var tip = this.target.blankText;
11623
11624             if(this.target.getValue() !== '' ) {
11625                 
11626                 if (this.target.invalidText.length) {
11627                     tip = this.target.invalidText;
11628                 } else if (this.target.regexText.length){
11629                     tip = this.target.regexText;
11630                 }
11631             }
11632
11633             this.toolTip.show(tip);
11634
11635             this.intervalID = window.setInterval(function() {
11636                 Roo.bootstrap.Form.popover.unmask();
11637             }, 10000);
11638
11639             window.onwheel = function(){ return false;};
11640             
11641             (function(){ this.isMasked = true; }).defer(500, this);
11642             
11643         },
11644         
11645         unmask : function()
11646         {
11647             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11648                 return;
11649             }
11650             
11651             this.maskEl.top.setStyle('position', 'absolute');
11652             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11653             this.maskEl.top.hide();
11654
11655             this.maskEl.left.setStyle('position', 'absolute');
11656             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11657             this.maskEl.left.hide();
11658
11659             this.maskEl.bottom.setStyle('position', 'absolute');
11660             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11661             this.maskEl.bottom.hide();
11662
11663             this.maskEl.right.setStyle('position', 'absolute');
11664             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11665             this.maskEl.right.hide();
11666             
11667             this.toolTip.hide();
11668             
11669             this.toolTip.el.hide();
11670             
11671             window.onwheel = function(){ return true;};
11672             
11673             if(this.intervalID){
11674                 window.clearInterval(this.intervalID);
11675                 this.intervalID = false;
11676             }
11677             
11678             this.isMasked = false;
11679             
11680         }
11681         
11682     }
11683     
11684 });
11685
11686 /*
11687  * Based on:
11688  * Ext JS Library 1.1.1
11689  * Copyright(c) 2006-2007, Ext JS, LLC.
11690  *
11691  * Originally Released Under LGPL - original licence link has changed is not relivant.
11692  *
11693  * Fork - LGPL
11694  * <script type="text/javascript">
11695  */
11696 /**
11697  * @class Roo.form.VTypes
11698  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11699  * @singleton
11700  */
11701 Roo.form.VTypes = function(){
11702     // closure these in so they are only created once.
11703     var alpha = /^[a-zA-Z_]+$/;
11704     var alphanum = /^[a-zA-Z0-9_]+$/;
11705     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11706     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11707
11708     // All these messages and functions are configurable
11709     return {
11710         /**
11711          * The function used to validate email addresses
11712          * @param {String} value The email address
11713          */
11714         'email' : function(v){
11715             return email.test(v);
11716         },
11717         /**
11718          * The error text to display when the email validation function returns false
11719          * @type String
11720          */
11721         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11722         /**
11723          * The keystroke filter mask to be applied on email input
11724          * @type RegExp
11725          */
11726         'emailMask' : /[a-z0-9_\.\-@]/i,
11727
11728         /**
11729          * The function used to validate URLs
11730          * @param {String} value The URL
11731          */
11732         'url' : function(v){
11733             return url.test(v);
11734         },
11735         /**
11736          * The error text to display when the url validation function returns false
11737          * @type String
11738          */
11739         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11740         
11741         /**
11742          * The function used to validate alpha values
11743          * @param {String} value The value
11744          */
11745         'alpha' : function(v){
11746             return alpha.test(v);
11747         },
11748         /**
11749          * The error text to display when the alpha validation function returns false
11750          * @type String
11751          */
11752         'alphaText' : 'This field should only contain letters and _',
11753         /**
11754          * The keystroke filter mask to be applied on alpha input
11755          * @type RegExp
11756          */
11757         'alphaMask' : /[a-z_]/i,
11758
11759         /**
11760          * The function used to validate alphanumeric values
11761          * @param {String} value The value
11762          */
11763         'alphanum' : function(v){
11764             return alphanum.test(v);
11765         },
11766         /**
11767          * The error text to display when the alphanumeric validation function returns false
11768          * @type String
11769          */
11770         'alphanumText' : 'This field should only contain letters, numbers and _',
11771         /**
11772          * The keystroke filter mask to be applied on alphanumeric input
11773          * @type RegExp
11774          */
11775         'alphanumMask' : /[a-z0-9_]/i
11776     };
11777 }();/*
11778  * - LGPL
11779  *
11780  * Input
11781  * 
11782  */
11783
11784 /**
11785  * @class Roo.bootstrap.Input
11786  * @extends Roo.bootstrap.Component
11787  * Bootstrap Input class
11788  * @cfg {Boolean} disabled is it disabled
11789  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11790  * @cfg {String} name name of the input
11791  * @cfg {string} fieldLabel - the label associated
11792  * @cfg {string} placeholder - placeholder to put in text.
11793  * @cfg {string}  before - input group add on before
11794  * @cfg {string} after - input group add on after
11795  * @cfg {string} size - (lg|sm) or leave empty..
11796  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11797  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11798  * @cfg {Number} md colspan out of 12 for computer-sized screens
11799  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11800  * @cfg {string} value default value of the input
11801  * @cfg {Number} labelWidth set the width of label 
11802  * @cfg {Number} labellg set the width of label (1-12)
11803  * @cfg {Number} labelmd set the width of label (1-12)
11804  * @cfg {Number} labelsm set the width of label (1-12)
11805  * @cfg {Number} labelxs set the width of label (1-12)
11806  * @cfg {String} labelAlign (top|left)
11807  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11808  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11809  * @cfg {String} indicatorpos (left|right) default left
11810  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11811  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11812  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11813
11814  * @cfg {String} align (left|center|right) Default left
11815  * @cfg {Boolean} forceFeedback (true|false) Default false
11816  * 
11817  * @constructor
11818  * Create a new Input
11819  * @param {Object} config The config object
11820  */
11821
11822 Roo.bootstrap.Input = function(config){
11823     
11824     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11825     
11826     this.addEvents({
11827         /**
11828          * @event focus
11829          * Fires when this field receives input focus.
11830          * @param {Roo.form.Field} this
11831          */
11832         focus : true,
11833         /**
11834          * @event blur
11835          * Fires when this field loses input focus.
11836          * @param {Roo.form.Field} this
11837          */
11838         blur : true,
11839         /**
11840          * @event specialkey
11841          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11842          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11843          * @param {Roo.form.Field} this
11844          * @param {Roo.EventObject} e The event object
11845          */
11846         specialkey : true,
11847         /**
11848          * @event change
11849          * Fires just before the field blurs if the field value has changed.
11850          * @param {Roo.form.Field} this
11851          * @param {Mixed} newValue The new value
11852          * @param {Mixed} oldValue The original value
11853          */
11854         change : true,
11855         /**
11856          * @event invalid
11857          * Fires after the field has been marked as invalid.
11858          * @param {Roo.form.Field} this
11859          * @param {String} msg The validation message
11860          */
11861         invalid : true,
11862         /**
11863          * @event valid
11864          * Fires after the field has been validated with no errors.
11865          * @param {Roo.form.Field} this
11866          */
11867         valid : true,
11868          /**
11869          * @event keyup
11870          * Fires after the key up
11871          * @param {Roo.form.Field} this
11872          * @param {Roo.EventObject}  e The event Object
11873          */
11874         keyup : true,
11875         /**
11876          * @event paste
11877          * Fires after the user pastes into input
11878          * @param {Roo.form.Field} this
11879          * @param {Roo.EventObject}  e The event Object
11880          */
11881         paste : true
11882     });
11883 };
11884
11885 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11886      /**
11887      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11888       automatic validation (defaults to "keyup").
11889      */
11890     validationEvent : "keyup",
11891      /**
11892      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11893      */
11894     validateOnBlur : true,
11895     /**
11896      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11897      */
11898     validationDelay : 250,
11899      /**
11900      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11901      */
11902     focusClass : "x-form-focus",  // not needed???
11903     
11904        
11905     /**
11906      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11907      */
11908     invalidClass : "has-warning",
11909     
11910     /**
11911      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11912      */
11913     validClass : "has-success",
11914     
11915     /**
11916      * @cfg {Boolean} hasFeedback (true|false) default true
11917      */
11918     hasFeedback : true,
11919     
11920     /**
11921      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11922      */
11923     invalidFeedbackClass : "glyphicon-warning-sign",
11924     
11925     /**
11926      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11927      */
11928     validFeedbackClass : "glyphicon-ok",
11929     
11930     /**
11931      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11932      */
11933     selectOnFocus : false,
11934     
11935      /**
11936      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11937      */
11938     maskRe : null,
11939        /**
11940      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11941      */
11942     vtype : null,
11943     
11944       /**
11945      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11946      */
11947     disableKeyFilter : false,
11948     
11949        /**
11950      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11951      */
11952     disabled : false,
11953      /**
11954      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11955      */
11956     allowBlank : true,
11957     /**
11958      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11959      */
11960     blankText : "Please complete this mandatory field",
11961     
11962      /**
11963      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11964      */
11965     minLength : 0,
11966     /**
11967      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11968      */
11969     maxLength : Number.MAX_VALUE,
11970     /**
11971      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11972      */
11973     minLengthText : "The minimum length for this field is {0}",
11974     /**
11975      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11976      */
11977     maxLengthText : "The maximum length for this field is {0}",
11978   
11979     
11980     /**
11981      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11982      * If available, this function will be called only after the basic validators all return true, and will be passed the
11983      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11984      */
11985     validator : null,
11986     /**
11987      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11988      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11989      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11990      */
11991     regex : null,
11992     /**
11993      * @cfg {String} regexText -- Depricated - use Invalid Text
11994      */
11995     regexText : "",
11996     
11997     /**
11998      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11999      */
12000     invalidText : "",
12001     
12002     
12003     
12004     autocomplete: false,
12005     
12006     
12007     fieldLabel : '',
12008     inputType : 'text',
12009     
12010     name : false,
12011     placeholder: false,
12012     before : false,
12013     after : false,
12014     size : false,
12015     hasFocus : false,
12016     preventMark: false,
12017     isFormField : true,
12018     value : '',
12019     labelWidth : 2,
12020     labelAlign : false,
12021     readOnly : false,
12022     align : false,
12023     formatedValue : false,
12024     forceFeedback : false,
12025     
12026     indicatorpos : 'left',
12027     
12028     labellg : 0,
12029     labelmd : 0,
12030     labelsm : 0,
12031     labelxs : 0,
12032     
12033     capture : '',
12034     accept : '',
12035     
12036     parentLabelAlign : function()
12037     {
12038         var parent = this;
12039         while (parent.parent()) {
12040             parent = parent.parent();
12041             if (typeof(parent.labelAlign) !='undefined') {
12042                 return parent.labelAlign;
12043             }
12044         }
12045         return 'left';
12046         
12047     },
12048     
12049     getAutoCreate : function()
12050     {
12051         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12052         
12053         var id = Roo.id();
12054         
12055         var cfg = {};
12056         
12057         if(this.inputType != 'hidden'){
12058             cfg.cls = 'form-group' //input-group
12059         }
12060         
12061         var input =  {
12062             tag: 'input',
12063             id : id,
12064             type : this.inputType,
12065             value : this.value,
12066             cls : 'form-control',
12067             placeholder : this.placeholder || '',
12068             autocomplete : this.autocomplete || 'new-password'
12069         };
12070         if (this.inputType == 'file') {
12071             input.style = 'overflow:hidden'; // why not in CSS?
12072         }
12073         
12074         if(this.capture.length){
12075             input.capture = this.capture;
12076         }
12077         
12078         if(this.accept.length){
12079             input.accept = this.accept + "/*";
12080         }
12081         
12082         if(this.align){
12083             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12084         }
12085         
12086         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12087             input.maxLength = this.maxLength;
12088         }
12089         
12090         if (this.disabled) {
12091             input.disabled=true;
12092         }
12093         
12094         if (this.readOnly) {
12095             input.readonly=true;
12096         }
12097         
12098         if (this.name) {
12099             input.name = this.name;
12100         }
12101         
12102         if (this.size) {
12103             input.cls += ' input-' + this.size;
12104         }
12105         
12106         var settings=this;
12107         ['xs','sm','md','lg'].map(function(size){
12108             if (settings[size]) {
12109                 cfg.cls += ' col-' + size + '-' + settings[size];
12110             }
12111         });
12112         
12113         var inputblock = input;
12114         
12115         var feedback = {
12116             tag: 'span',
12117             cls: 'glyphicon form-control-feedback'
12118         };
12119             
12120         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12121             
12122             inputblock = {
12123                 cls : 'has-feedback',
12124                 cn :  [
12125                     input,
12126                     feedback
12127                 ] 
12128             };  
12129         }
12130         
12131         if (this.before || this.after) {
12132             
12133             inputblock = {
12134                 cls : 'input-group',
12135                 cn :  [] 
12136             };
12137             
12138             if (this.before && typeof(this.before) == 'string') {
12139                 
12140                 inputblock.cn.push({
12141                     tag :'span',
12142                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12143                     html : this.before
12144                 });
12145             }
12146             if (this.before && typeof(this.before) == 'object') {
12147                 this.before = Roo.factory(this.before);
12148                 
12149                 inputblock.cn.push({
12150                     tag :'span',
12151                     cls : 'roo-input-before input-group-prepend   input-group-' +
12152                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12153                 });
12154             }
12155             
12156             inputblock.cn.push(input);
12157             
12158             if (this.after && typeof(this.after) == 'string') {
12159                 inputblock.cn.push({
12160                     tag :'span',
12161                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12162                     html : this.after
12163                 });
12164             }
12165             if (this.after && typeof(this.after) == 'object') {
12166                 this.after = Roo.factory(this.after);
12167                 
12168                 inputblock.cn.push({
12169                     tag :'span',
12170                     cls : 'roo-input-after input-group-append  input-group-' +
12171                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12172                 });
12173             }
12174             
12175             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12176                 inputblock.cls += ' has-feedback';
12177                 inputblock.cn.push(feedback);
12178             }
12179         };
12180         var indicator = {
12181             tag : 'i',
12182             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12183             tooltip : 'This field is required'
12184         };
12185         if (this.allowBlank ) {
12186             indicator.style = this.allowBlank ? ' display:none' : '';
12187         }
12188         if (align ==='left' && this.fieldLabel.length) {
12189             
12190             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12191             
12192             cfg.cn = [
12193                 indicator,
12194                 {
12195                     tag: 'label',
12196                     'for' :  id,
12197                     cls : 'control-label col-form-label',
12198                     html : this.fieldLabel
12199
12200                 },
12201                 {
12202                     cls : "", 
12203                     cn: [
12204                         inputblock
12205                     ]
12206                 }
12207             ];
12208             
12209             var labelCfg = cfg.cn[1];
12210             var contentCfg = cfg.cn[2];
12211             
12212             if(this.indicatorpos == 'right'){
12213                 cfg.cn = [
12214                     {
12215                         tag: 'label',
12216                         'for' :  id,
12217                         cls : 'control-label col-form-label',
12218                         cn : [
12219                             {
12220                                 tag : 'span',
12221                                 html : this.fieldLabel
12222                             },
12223                             indicator
12224                         ]
12225                     },
12226                     {
12227                         cls : "",
12228                         cn: [
12229                             inputblock
12230                         ]
12231                     }
12232
12233                 ];
12234                 
12235                 labelCfg = cfg.cn[0];
12236                 contentCfg = cfg.cn[1];
12237             
12238             }
12239             
12240             if(this.labelWidth > 12){
12241                 labelCfg.style = "width: " + this.labelWidth + 'px';
12242             }
12243             
12244             if(this.labelWidth < 13 && this.labelmd == 0){
12245                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12246             }
12247             
12248             if(this.labellg > 0){
12249                 labelCfg.cls += ' col-lg-' + this.labellg;
12250                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12251             }
12252             
12253             if(this.labelmd > 0){
12254                 labelCfg.cls += ' col-md-' + this.labelmd;
12255                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12256             }
12257             
12258             if(this.labelsm > 0){
12259                 labelCfg.cls += ' col-sm-' + this.labelsm;
12260                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12261             }
12262             
12263             if(this.labelxs > 0){
12264                 labelCfg.cls += ' col-xs-' + this.labelxs;
12265                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12266             }
12267             
12268             
12269         } else if ( this.fieldLabel.length) {
12270                 
12271             
12272             
12273             cfg.cn = [
12274                 {
12275                     tag : 'i',
12276                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12277                     tooltip : 'This field is required',
12278                     style : this.allowBlank ? ' display:none' : '' 
12279                 },
12280                 {
12281                     tag: 'label',
12282                    //cls : 'input-group-addon',
12283                     html : this.fieldLabel
12284
12285                 },
12286
12287                inputblock
12288
12289            ];
12290            
12291            if(this.indicatorpos == 'right'){
12292        
12293                 cfg.cn = [
12294                     {
12295                         tag: 'label',
12296                        //cls : 'input-group-addon',
12297                         html : this.fieldLabel
12298
12299                     },
12300                     {
12301                         tag : 'i',
12302                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12303                         tooltip : 'This field is required',
12304                         style : this.allowBlank ? ' display:none' : '' 
12305                     },
12306
12307                    inputblock
12308
12309                ];
12310
12311             }
12312
12313         } else {
12314             
12315             cfg.cn = [
12316
12317                     inputblock
12318
12319             ];
12320                 
12321                 
12322         };
12323         
12324         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12325            cfg.cls += ' navbar-form';
12326         }
12327         
12328         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12329             // on BS4 we do this only if not form 
12330             cfg.cls += ' navbar-form';
12331             cfg.tag = 'li';
12332         }
12333         
12334         return cfg;
12335         
12336     },
12337     /**
12338      * return the real input element.
12339      */
12340     inputEl: function ()
12341     {
12342         return this.el.select('input.form-control',true).first();
12343     },
12344     
12345     tooltipEl : function()
12346     {
12347         return this.inputEl();
12348     },
12349     
12350     indicatorEl : function()
12351     {
12352         if (Roo.bootstrap.version == 4) {
12353             return false; // not enabled in v4 yet.
12354         }
12355         
12356         var indicator = this.el.select('i.roo-required-indicator',true).first();
12357         
12358         if(!indicator){
12359             return false;
12360         }
12361         
12362         return indicator;
12363         
12364     },
12365     
12366     setDisabled : function(v)
12367     {
12368         var i  = this.inputEl().dom;
12369         if (!v) {
12370             i.removeAttribute('disabled');
12371             return;
12372             
12373         }
12374         i.setAttribute('disabled','true');
12375     },
12376     initEvents : function()
12377     {
12378           
12379         this.inputEl().on("keydown" , this.fireKey,  this);
12380         this.inputEl().on("focus", this.onFocus,  this);
12381         this.inputEl().on("blur", this.onBlur,  this);
12382         
12383         this.inputEl().relayEvent('keyup', this);
12384         this.inputEl().relayEvent('paste', this);
12385         
12386         this.indicator = this.indicatorEl();
12387         
12388         if(this.indicator){
12389             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12390         }
12391  
12392         // reference to original value for reset
12393         this.originalValue = this.getValue();
12394         //Roo.form.TextField.superclass.initEvents.call(this);
12395         if(this.validationEvent == 'keyup'){
12396             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12397             this.inputEl().on('keyup', this.filterValidation, this);
12398         }
12399         else if(this.validationEvent !== false){
12400             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12401         }
12402         
12403         if(this.selectOnFocus){
12404             this.on("focus", this.preFocus, this);
12405             
12406         }
12407         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12408             this.inputEl().on("keypress", this.filterKeys, this);
12409         } else {
12410             this.inputEl().relayEvent('keypress', this);
12411         }
12412        /* if(this.grow){
12413             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12414             this.el.on("click", this.autoSize,  this);
12415         }
12416         */
12417         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12418             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12419         }
12420         
12421         if (typeof(this.before) == 'object') {
12422             this.before.render(this.el.select('.roo-input-before',true).first());
12423         }
12424         if (typeof(this.after) == 'object') {
12425             this.after.render(this.el.select('.roo-input-after',true).first());
12426         }
12427         
12428         this.inputEl().on('change', this.onChange, this);
12429         
12430     },
12431     filterValidation : function(e){
12432         if(!e.isNavKeyPress()){
12433             this.validationTask.delay(this.validationDelay);
12434         }
12435     },
12436      /**
12437      * Validates the field value
12438      * @return {Boolean} True if the value is valid, else false
12439      */
12440     validate : function(){
12441         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12442         if(this.disabled || this.validateValue(this.getRawValue())){
12443             this.markValid();
12444             return true;
12445         }
12446         
12447         this.markInvalid();
12448         return false;
12449     },
12450     
12451     
12452     /**
12453      * Validates a value according to the field's validation rules and marks the field as invalid
12454      * if the validation fails
12455      * @param {Mixed} value The value to validate
12456      * @return {Boolean} True if the value is valid, else false
12457      */
12458     validateValue : function(value)
12459     {
12460         if(this.getVisibilityEl().hasClass('hidden')){
12461             return true;
12462         }
12463         
12464         if(value.length < 1)  { // if it's blank
12465             if(this.allowBlank){
12466                 return true;
12467             }
12468             return false;
12469         }
12470         
12471         if(value.length < this.minLength){
12472             return false;
12473         }
12474         if(value.length > this.maxLength){
12475             return false;
12476         }
12477         if(this.vtype){
12478             var vt = Roo.form.VTypes;
12479             if(!vt[this.vtype](value, this)){
12480                 return false;
12481             }
12482         }
12483         if(typeof this.validator == "function"){
12484             var msg = this.validator(value);
12485             if(msg !== true){
12486                 return false;
12487             }
12488             if (typeof(msg) == 'string') {
12489                 this.invalidText = msg;
12490             }
12491         }
12492         
12493         if(this.regex && !this.regex.test(value)){
12494             return false;
12495         }
12496         
12497         return true;
12498     },
12499     
12500      // private
12501     fireKey : function(e){
12502         //Roo.log('field ' + e.getKey());
12503         if(e.isNavKeyPress()){
12504             this.fireEvent("specialkey", this, e);
12505         }
12506     },
12507     focus : function (selectText){
12508         if(this.rendered){
12509             this.inputEl().focus();
12510             if(selectText === true){
12511                 this.inputEl().dom.select();
12512             }
12513         }
12514         return this;
12515     } ,
12516     
12517     onFocus : function(){
12518         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12519            // this.el.addClass(this.focusClass);
12520         }
12521         if(!this.hasFocus){
12522             this.hasFocus = true;
12523             this.startValue = this.getValue();
12524             this.fireEvent("focus", this);
12525         }
12526     },
12527     
12528     beforeBlur : Roo.emptyFn,
12529
12530     
12531     // private
12532     onBlur : function(){
12533         this.beforeBlur();
12534         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12535             //this.el.removeClass(this.focusClass);
12536         }
12537         this.hasFocus = false;
12538         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12539             this.validate();
12540         }
12541         var v = this.getValue();
12542         if(String(v) !== String(this.startValue)){
12543             this.fireEvent('change', this, v, this.startValue);
12544         }
12545         this.fireEvent("blur", this);
12546     },
12547     
12548     onChange : function(e)
12549     {
12550         var v = this.getValue();
12551         if(String(v) !== String(this.startValue)){
12552             this.fireEvent('change', this, v, this.startValue);
12553         }
12554         
12555     },
12556     
12557     /**
12558      * Resets the current field value to the originally loaded value and clears any validation messages
12559      */
12560     reset : function(){
12561         this.setValue(this.originalValue);
12562         this.validate();
12563     },
12564      /**
12565      * Returns the name of the field
12566      * @return {Mixed} name The name field
12567      */
12568     getName: function(){
12569         return this.name;
12570     },
12571      /**
12572      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12573      * @return {Mixed} value The field value
12574      */
12575     getValue : function(){
12576         
12577         var v = this.inputEl().getValue();
12578         
12579         return v;
12580     },
12581     /**
12582      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12583      * @return {Mixed} value The field value
12584      */
12585     getRawValue : function(){
12586         var v = this.inputEl().getValue();
12587         
12588         return v;
12589     },
12590     
12591     /**
12592      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12593      * @param {Mixed} value The value to set
12594      */
12595     setRawValue : function(v){
12596         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12597     },
12598     
12599     selectText : function(start, end){
12600         var v = this.getRawValue();
12601         if(v.length > 0){
12602             start = start === undefined ? 0 : start;
12603             end = end === undefined ? v.length : end;
12604             var d = this.inputEl().dom;
12605             if(d.setSelectionRange){
12606                 d.setSelectionRange(start, end);
12607             }else if(d.createTextRange){
12608                 var range = d.createTextRange();
12609                 range.moveStart("character", start);
12610                 range.moveEnd("character", v.length-end);
12611                 range.select();
12612             }
12613         }
12614     },
12615     
12616     /**
12617      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12618      * @param {Mixed} value The value to set
12619      */
12620     setValue : function(v){
12621         this.value = v;
12622         if(this.rendered){
12623             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12624             this.validate();
12625         }
12626     },
12627     
12628     /*
12629     processValue : function(value){
12630         if(this.stripCharsRe){
12631             var newValue = value.replace(this.stripCharsRe, '');
12632             if(newValue !== value){
12633                 this.setRawValue(newValue);
12634                 return newValue;
12635             }
12636         }
12637         return value;
12638     },
12639   */
12640     preFocus : function(){
12641         
12642         if(this.selectOnFocus){
12643             this.inputEl().dom.select();
12644         }
12645     },
12646     filterKeys : function(e){
12647         var k = e.getKey();
12648         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12649             return;
12650         }
12651         var c = e.getCharCode(), cc = String.fromCharCode(c);
12652         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12653             return;
12654         }
12655         if(!this.maskRe.test(cc)){
12656             e.stopEvent();
12657         }
12658     },
12659      /**
12660      * Clear any invalid styles/messages for this field
12661      */
12662     clearInvalid : function(){
12663         
12664         if(!this.el || this.preventMark){ // not rendered
12665             return;
12666         }
12667         
12668         
12669         this.el.removeClass([this.invalidClass, 'is-invalid']);
12670         
12671         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12672             
12673             var feedback = this.el.select('.form-control-feedback', true).first();
12674             
12675             if(feedback){
12676                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12677             }
12678             
12679         }
12680         
12681         if(this.indicator){
12682             this.indicator.removeClass('visible');
12683             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12684         }
12685         
12686         this.fireEvent('valid', this);
12687     },
12688     
12689      /**
12690      * Mark this field as valid
12691      */
12692     markValid : function()
12693     {
12694         if(!this.el  || this.preventMark){ // not rendered...
12695             return;
12696         }
12697         
12698         this.el.removeClass([this.invalidClass, this.validClass]);
12699         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12700
12701         var feedback = this.el.select('.form-control-feedback', true).first();
12702             
12703         if(feedback){
12704             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12705         }
12706         
12707         if(this.indicator){
12708             this.indicator.removeClass('visible');
12709             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12710         }
12711         
12712         if(this.disabled){
12713             return;
12714         }
12715         
12716            
12717         if(this.allowBlank && !this.getRawValue().length){
12718             return;
12719         }
12720         if (Roo.bootstrap.version == 3) {
12721             this.el.addClass(this.validClass);
12722         } else {
12723             this.inputEl().addClass('is-valid');
12724         }
12725
12726         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12727             
12728             var feedback = this.el.select('.form-control-feedback', true).first();
12729             
12730             if(feedback){
12731                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12732                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12733             }
12734             
12735         }
12736         
12737         this.fireEvent('valid', this);
12738     },
12739     
12740      /**
12741      * Mark this field as invalid
12742      * @param {String} msg The validation message
12743      */
12744     markInvalid : function(msg)
12745     {
12746         if(!this.el  || this.preventMark){ // not rendered
12747             return;
12748         }
12749         
12750         this.el.removeClass([this.invalidClass, this.validClass]);
12751         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12752         
12753         var feedback = this.el.select('.form-control-feedback', true).first();
12754             
12755         if(feedback){
12756             this.el.select('.form-control-feedback', true).first().removeClass(
12757                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12758         }
12759
12760         if(this.disabled){
12761             return;
12762         }
12763         
12764         if(this.allowBlank && !this.getRawValue().length){
12765             return;
12766         }
12767         
12768         if(this.indicator){
12769             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12770             this.indicator.addClass('visible');
12771         }
12772         if (Roo.bootstrap.version == 3) {
12773             this.el.addClass(this.invalidClass);
12774         } else {
12775             this.inputEl().addClass('is-invalid');
12776         }
12777         
12778         
12779         
12780         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12781             
12782             var feedback = this.el.select('.form-control-feedback', true).first();
12783             
12784             if(feedback){
12785                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12786                 
12787                 if(this.getValue().length || this.forceFeedback){
12788                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12789                 }
12790                 
12791             }
12792             
12793         }
12794         
12795         this.fireEvent('invalid', this, msg);
12796     },
12797     // private
12798     SafariOnKeyDown : function(event)
12799     {
12800         // this is a workaround for a password hang bug on chrome/ webkit.
12801         if (this.inputEl().dom.type != 'password') {
12802             return;
12803         }
12804         
12805         var isSelectAll = false;
12806         
12807         if(this.inputEl().dom.selectionEnd > 0){
12808             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12809         }
12810         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12811             event.preventDefault();
12812             this.setValue('');
12813             return;
12814         }
12815         
12816         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12817             
12818             event.preventDefault();
12819             // this is very hacky as keydown always get's upper case.
12820             //
12821             var cc = String.fromCharCode(event.getCharCode());
12822             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12823             
12824         }
12825     },
12826     adjustWidth : function(tag, w){
12827         tag = tag.toLowerCase();
12828         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12829             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12830                 if(tag == 'input'){
12831                     return w + 2;
12832                 }
12833                 if(tag == 'textarea'){
12834                     return w-2;
12835                 }
12836             }else if(Roo.isOpera){
12837                 if(tag == 'input'){
12838                     return w + 2;
12839                 }
12840                 if(tag == 'textarea'){
12841                     return w-2;
12842                 }
12843             }
12844         }
12845         return w;
12846     },
12847     
12848     setFieldLabel : function(v)
12849     {
12850         if(!this.rendered){
12851             return;
12852         }
12853         
12854         if(this.indicatorEl()){
12855             var ar = this.el.select('label > span',true);
12856             
12857             if (ar.elements.length) {
12858                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12859                 this.fieldLabel = v;
12860                 return;
12861             }
12862             
12863             var br = this.el.select('label',true);
12864             
12865             if(br.elements.length) {
12866                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12867                 this.fieldLabel = v;
12868                 return;
12869             }
12870             
12871             Roo.log('Cannot Found any of label > span || label in input');
12872             return;
12873         }
12874         
12875         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12876         this.fieldLabel = v;
12877         
12878         
12879     }
12880 });
12881
12882  
12883 /*
12884  * - LGPL
12885  *
12886  * Input
12887  * 
12888  */
12889
12890 /**
12891  * @class Roo.bootstrap.TextArea
12892  * @extends Roo.bootstrap.Input
12893  * Bootstrap TextArea class
12894  * @cfg {Number} cols Specifies the visible width of a text area
12895  * @cfg {Number} rows Specifies the visible number of lines in a text area
12896  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12897  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12898  * @cfg {string} html text
12899  * 
12900  * @constructor
12901  * Create a new TextArea
12902  * @param {Object} config The config object
12903  */
12904
12905 Roo.bootstrap.TextArea = function(config){
12906     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12907    
12908 };
12909
12910 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12911      
12912     cols : false,
12913     rows : 5,
12914     readOnly : false,
12915     warp : 'soft',
12916     resize : false,
12917     value: false,
12918     html: false,
12919     
12920     getAutoCreate : function(){
12921         
12922         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12923         
12924         var id = Roo.id();
12925         
12926         var cfg = {};
12927         
12928         if(this.inputType != 'hidden'){
12929             cfg.cls = 'form-group' //input-group
12930         }
12931         
12932         var input =  {
12933             tag: 'textarea',
12934             id : id,
12935             warp : this.warp,
12936             rows : this.rows,
12937             value : this.value || '',
12938             html: this.html || '',
12939             cls : 'form-control',
12940             placeholder : this.placeholder || '' 
12941             
12942         };
12943         
12944         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12945             input.maxLength = this.maxLength;
12946         }
12947         
12948         if(this.resize){
12949             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12950         }
12951         
12952         if(this.cols){
12953             input.cols = this.cols;
12954         }
12955         
12956         if (this.readOnly) {
12957             input.readonly = true;
12958         }
12959         
12960         if (this.name) {
12961             input.name = this.name;
12962         }
12963         
12964         if (this.size) {
12965             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12966         }
12967         
12968         var settings=this;
12969         ['xs','sm','md','lg'].map(function(size){
12970             if (settings[size]) {
12971                 cfg.cls += ' col-' + size + '-' + settings[size];
12972             }
12973         });
12974         
12975         var inputblock = input;
12976         
12977         if(this.hasFeedback && !this.allowBlank){
12978             
12979             var feedback = {
12980                 tag: 'span',
12981                 cls: 'glyphicon form-control-feedback'
12982             };
12983
12984             inputblock = {
12985                 cls : 'has-feedback',
12986                 cn :  [
12987                     input,
12988                     feedback
12989                 ] 
12990             };  
12991         }
12992         
12993         
12994         if (this.before || this.after) {
12995             
12996             inputblock = {
12997                 cls : 'input-group',
12998                 cn :  [] 
12999             };
13000             if (this.before) {
13001                 inputblock.cn.push({
13002                     tag :'span',
13003                     cls : 'input-group-addon',
13004                     html : this.before
13005                 });
13006             }
13007             
13008             inputblock.cn.push(input);
13009             
13010             if(this.hasFeedback && !this.allowBlank){
13011                 inputblock.cls += ' has-feedback';
13012                 inputblock.cn.push(feedback);
13013             }
13014             
13015             if (this.after) {
13016                 inputblock.cn.push({
13017                     tag :'span',
13018                     cls : 'input-group-addon',
13019                     html : this.after
13020                 });
13021             }
13022             
13023         }
13024         
13025         if (align ==='left' && this.fieldLabel.length) {
13026             cfg.cn = [
13027                 {
13028                     tag: 'label',
13029                     'for' :  id,
13030                     cls : 'control-label',
13031                     html : this.fieldLabel
13032                 },
13033                 {
13034                     cls : "",
13035                     cn: [
13036                         inputblock
13037                     ]
13038                 }
13039
13040             ];
13041             
13042             if(this.labelWidth > 12){
13043                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13044             }
13045
13046             if(this.labelWidth < 13 && this.labelmd == 0){
13047                 this.labelmd = this.labelWidth;
13048             }
13049
13050             if(this.labellg > 0){
13051                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13052                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13053             }
13054
13055             if(this.labelmd > 0){
13056                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13057                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13058             }
13059
13060             if(this.labelsm > 0){
13061                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13062                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13063             }
13064
13065             if(this.labelxs > 0){
13066                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13067                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13068             }
13069             
13070         } else if ( this.fieldLabel.length) {
13071             cfg.cn = [
13072
13073                {
13074                    tag: 'label',
13075                    //cls : 'input-group-addon',
13076                    html : this.fieldLabel
13077
13078                },
13079
13080                inputblock
13081
13082            ];
13083
13084         } else {
13085
13086             cfg.cn = [
13087
13088                 inputblock
13089
13090             ];
13091                 
13092         }
13093         
13094         if (this.disabled) {
13095             input.disabled=true;
13096         }
13097         
13098         return cfg;
13099         
13100     },
13101     /**
13102      * return the real textarea element.
13103      */
13104     inputEl: function ()
13105     {
13106         return this.el.select('textarea.form-control',true).first();
13107     },
13108     
13109     /**
13110      * Clear any invalid styles/messages for this field
13111      */
13112     clearInvalid : function()
13113     {
13114         
13115         if(!this.el || this.preventMark){ // not rendered
13116             return;
13117         }
13118         
13119         var label = this.el.select('label', true).first();
13120         var icon = this.el.select('i.fa-star', true).first();
13121         
13122         if(label && icon){
13123             icon.remove();
13124         }
13125         this.el.removeClass( this.validClass);
13126         this.inputEl().removeClass('is-invalid');
13127          
13128         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13129             
13130             var feedback = this.el.select('.form-control-feedback', true).first();
13131             
13132             if(feedback){
13133                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13134             }
13135             
13136         }
13137         
13138         this.fireEvent('valid', this);
13139     },
13140     
13141      /**
13142      * Mark this field as valid
13143      */
13144     markValid : function()
13145     {
13146         if(!this.el  || this.preventMark){ // not rendered
13147             return;
13148         }
13149         
13150         this.el.removeClass([this.invalidClass, this.validClass]);
13151         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13152         
13153         var feedback = this.el.select('.form-control-feedback', true).first();
13154             
13155         if(feedback){
13156             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13157         }
13158
13159         if(this.disabled || this.allowBlank){
13160             return;
13161         }
13162         
13163         var label = this.el.select('label', true).first();
13164         var icon = this.el.select('i.fa-star', true).first();
13165         
13166         if(label && icon){
13167             icon.remove();
13168         }
13169         if (Roo.bootstrap.version == 3) {
13170             this.el.addClass(this.validClass);
13171         } else {
13172             this.inputEl().addClass('is-valid');
13173         }
13174         
13175         
13176         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13177             
13178             var feedback = this.el.select('.form-control-feedback', true).first();
13179             
13180             if(feedback){
13181                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13182                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13183             }
13184             
13185         }
13186         
13187         this.fireEvent('valid', this);
13188     },
13189     
13190      /**
13191      * Mark this field as invalid
13192      * @param {String} msg The validation message
13193      */
13194     markInvalid : function(msg)
13195     {
13196         if(!this.el  || this.preventMark){ // not rendered
13197             return;
13198         }
13199         
13200         this.el.removeClass([this.invalidClass, this.validClass]);
13201         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13202         
13203         var feedback = this.el.select('.form-control-feedback', true).first();
13204             
13205         if(feedback){
13206             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13207         }
13208
13209         if(this.disabled || this.allowBlank){
13210             return;
13211         }
13212         
13213         var label = this.el.select('label', true).first();
13214         var icon = this.el.select('i.fa-star', true).first();
13215         
13216         if(!this.getValue().length && label && !icon){
13217             this.el.createChild({
13218                 tag : 'i',
13219                 cls : 'text-danger fa fa-lg fa-star',
13220                 tooltip : 'This field is required',
13221                 style : 'margin-right:5px;'
13222             }, label, true);
13223         }
13224         
13225         if (Roo.bootstrap.version == 3) {
13226             this.el.addClass(this.invalidClass);
13227         } else {
13228             this.inputEl().addClass('is-invalid');
13229         }
13230         
13231         // fixme ... this may be depricated need to test..
13232         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13233             
13234             var feedback = this.el.select('.form-control-feedback', true).first();
13235             
13236             if(feedback){
13237                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13238                 
13239                 if(this.getValue().length || this.forceFeedback){
13240                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13241                 }
13242                 
13243             }
13244             
13245         }
13246         
13247         this.fireEvent('invalid', this, msg);
13248     }
13249 });
13250
13251  
13252 /*
13253  * - LGPL
13254  *
13255  * trigger field - base class for combo..
13256  * 
13257  */
13258  
13259 /**
13260  * @class Roo.bootstrap.TriggerField
13261  * @extends Roo.bootstrap.Input
13262  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13263  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13264  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13265  * for which you can provide a custom implementation.  For example:
13266  * <pre><code>
13267 var trigger = new Roo.bootstrap.TriggerField();
13268 trigger.onTriggerClick = myTriggerFn;
13269 trigger.applyTo('my-field');
13270 </code></pre>
13271  *
13272  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13273  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13274  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13275  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13276  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13277
13278  * @constructor
13279  * Create a new TriggerField.
13280  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13281  * to the base TextField)
13282  */
13283 Roo.bootstrap.TriggerField = function(config){
13284     this.mimicing = false;
13285     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13286 };
13287
13288 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13289     /**
13290      * @cfg {String} triggerClass A CSS class to apply to the trigger
13291      */
13292      /**
13293      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13294      */
13295     hideTrigger:false,
13296
13297     /**
13298      * @cfg {Boolean} removable (true|false) special filter default false
13299      */
13300     removable : false,
13301     
13302     /** @cfg {Boolean} grow @hide */
13303     /** @cfg {Number} growMin @hide */
13304     /** @cfg {Number} growMax @hide */
13305
13306     /**
13307      * @hide 
13308      * @method
13309      */
13310     autoSize: Roo.emptyFn,
13311     // private
13312     monitorTab : true,
13313     // private
13314     deferHeight : true,
13315
13316     
13317     actionMode : 'wrap',
13318     
13319     caret : false,
13320     
13321     
13322     getAutoCreate : function(){
13323        
13324         var align = this.labelAlign || this.parentLabelAlign();
13325         
13326         var id = Roo.id();
13327         
13328         var cfg = {
13329             cls: 'form-group' //input-group
13330         };
13331         
13332         
13333         var input =  {
13334             tag: 'input',
13335             id : id,
13336             type : this.inputType,
13337             cls : 'form-control',
13338             autocomplete: 'new-password',
13339             placeholder : this.placeholder || '' 
13340             
13341         };
13342         if (this.name) {
13343             input.name = this.name;
13344         }
13345         if (this.size) {
13346             input.cls += ' input-' + this.size;
13347         }
13348         
13349         if (this.disabled) {
13350             input.disabled=true;
13351         }
13352         
13353         var inputblock = input;
13354         
13355         if(this.hasFeedback && !this.allowBlank){
13356             
13357             var feedback = {
13358                 tag: 'span',
13359                 cls: 'glyphicon form-control-feedback'
13360             };
13361             
13362             if(this.removable && !this.editable  ){
13363                 inputblock = {
13364                     cls : 'has-feedback',
13365                     cn :  [
13366                         inputblock,
13367                         {
13368                             tag: 'button',
13369                             html : 'x',
13370                             cls : 'roo-combo-removable-btn close'
13371                         },
13372                         feedback
13373                     ] 
13374                 };
13375             } else {
13376                 inputblock = {
13377                     cls : 'has-feedback',
13378                     cn :  [
13379                         inputblock,
13380                         feedback
13381                     ] 
13382                 };
13383             }
13384
13385         } else {
13386             if(this.removable && !this.editable ){
13387                 inputblock = {
13388                     cls : 'roo-removable',
13389                     cn :  [
13390                         inputblock,
13391                         {
13392                             tag: 'button',
13393                             html : 'x',
13394                             cls : 'roo-combo-removable-btn close'
13395                         }
13396                     ] 
13397                 };
13398             }
13399         }
13400         
13401         if (this.before || this.after) {
13402             
13403             inputblock = {
13404                 cls : 'input-group',
13405                 cn :  [] 
13406             };
13407             if (this.before) {
13408                 inputblock.cn.push({
13409                     tag :'span',
13410                     cls : 'input-group-addon input-group-prepend input-group-text',
13411                     html : this.before
13412                 });
13413             }
13414             
13415             inputblock.cn.push(input);
13416             
13417             if(this.hasFeedback && !this.allowBlank){
13418                 inputblock.cls += ' has-feedback';
13419                 inputblock.cn.push(feedback);
13420             }
13421             
13422             if (this.after) {
13423                 inputblock.cn.push({
13424                     tag :'span',
13425                     cls : 'input-group-addon input-group-append input-group-text',
13426                     html : this.after
13427                 });
13428             }
13429             
13430         };
13431         
13432       
13433         
13434         var ibwrap = inputblock;
13435         
13436         if(this.multiple){
13437             ibwrap = {
13438                 tag: 'ul',
13439                 cls: 'roo-select2-choices',
13440                 cn:[
13441                     {
13442                         tag: 'li',
13443                         cls: 'roo-select2-search-field',
13444                         cn: [
13445
13446                             inputblock
13447                         ]
13448                     }
13449                 ]
13450             };
13451                 
13452         }
13453         
13454         var combobox = {
13455             cls: 'roo-select2-container input-group',
13456             cn: [
13457                  {
13458                     tag: 'input',
13459                     type : 'hidden',
13460                     cls: 'form-hidden-field'
13461                 },
13462                 ibwrap
13463             ]
13464         };
13465         
13466         if(!this.multiple && this.showToggleBtn){
13467             
13468             var caret = {
13469                         tag: 'span',
13470                         cls: 'caret'
13471              };
13472             if (this.caret != false) {
13473                 caret = {
13474                      tag: 'i',
13475                      cls: 'fa fa-' + this.caret
13476                 };
13477                 
13478             }
13479             
13480             combobox.cn.push({
13481                 tag :'span',
13482                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13483                 cn : [
13484                     Roo.bootstrap.version == 3 ? caret : '',
13485                     {
13486                         tag: 'span',
13487                         cls: 'combobox-clear',
13488                         cn  : [
13489                             {
13490                                 tag : 'i',
13491                                 cls: 'icon-remove'
13492                             }
13493                         ]
13494                     }
13495                 ]
13496
13497             })
13498         }
13499         
13500         if(this.multiple){
13501             combobox.cls += ' roo-select2-container-multi';
13502         }
13503          var indicator = {
13504             tag : 'i',
13505             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13506             tooltip : 'This field is required'
13507         };
13508         if (Roo.bootstrap.version == 4) {
13509             indicator = {
13510                 tag : 'i',
13511                 style : 'display:none'
13512             };
13513         }
13514         
13515         
13516         if (align ==='left' && this.fieldLabel.length) {
13517             
13518             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13519
13520             cfg.cn = [
13521                 indicator,
13522                 {
13523                     tag: 'label',
13524                     'for' :  id,
13525                     cls : 'control-label',
13526                     html : this.fieldLabel
13527
13528                 },
13529                 {
13530                     cls : "", 
13531                     cn: [
13532                         combobox
13533                     ]
13534                 }
13535
13536             ];
13537             
13538             var labelCfg = cfg.cn[1];
13539             var contentCfg = cfg.cn[2];
13540             
13541             if(this.indicatorpos == 'right'){
13542                 cfg.cn = [
13543                     {
13544                         tag: 'label',
13545                         'for' :  id,
13546                         cls : 'control-label',
13547                         cn : [
13548                             {
13549                                 tag : 'span',
13550                                 html : this.fieldLabel
13551                             },
13552                             indicator
13553                         ]
13554                     },
13555                     {
13556                         cls : "", 
13557                         cn: [
13558                             combobox
13559                         ]
13560                     }
13561
13562                 ];
13563                 
13564                 labelCfg = cfg.cn[0];
13565                 contentCfg = cfg.cn[1];
13566             }
13567             
13568             if(this.labelWidth > 12){
13569                 labelCfg.style = "width: " + this.labelWidth + 'px';
13570             }
13571             
13572             if(this.labelWidth < 13 && this.labelmd == 0){
13573                 this.labelmd = this.labelWidth;
13574             }
13575             
13576             if(this.labellg > 0){
13577                 labelCfg.cls += ' col-lg-' + this.labellg;
13578                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13579             }
13580             
13581             if(this.labelmd > 0){
13582                 labelCfg.cls += ' col-md-' + this.labelmd;
13583                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13584             }
13585             
13586             if(this.labelsm > 0){
13587                 labelCfg.cls += ' col-sm-' + this.labelsm;
13588                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13589             }
13590             
13591             if(this.labelxs > 0){
13592                 labelCfg.cls += ' col-xs-' + this.labelxs;
13593                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13594             }
13595             
13596         } else if ( this.fieldLabel.length) {
13597 //                Roo.log(" label");
13598             cfg.cn = [
13599                 indicator,
13600                {
13601                    tag: 'label',
13602                    //cls : 'input-group-addon',
13603                    html : this.fieldLabel
13604
13605                },
13606
13607                combobox
13608
13609             ];
13610             
13611             if(this.indicatorpos == 'right'){
13612                 
13613                 cfg.cn = [
13614                     {
13615                        tag: 'label',
13616                        cn : [
13617                            {
13618                                tag : 'span',
13619                                html : this.fieldLabel
13620                            },
13621                            indicator
13622                        ]
13623
13624                     },
13625                     combobox
13626
13627                 ];
13628
13629             }
13630
13631         } else {
13632             
13633 //                Roo.log(" no label && no align");
13634                 cfg = combobox
13635                      
13636                 
13637         }
13638         
13639         var settings=this;
13640         ['xs','sm','md','lg'].map(function(size){
13641             if (settings[size]) {
13642                 cfg.cls += ' col-' + size + '-' + settings[size];
13643             }
13644         });
13645         
13646         return cfg;
13647         
13648     },
13649     
13650     
13651     
13652     // private
13653     onResize : function(w, h){
13654 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13655 //        if(typeof w == 'number'){
13656 //            var x = w - this.trigger.getWidth();
13657 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13658 //            this.trigger.setStyle('left', x+'px');
13659 //        }
13660     },
13661
13662     // private
13663     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13664
13665     // private
13666     getResizeEl : function(){
13667         return this.inputEl();
13668     },
13669
13670     // private
13671     getPositionEl : function(){
13672         return this.inputEl();
13673     },
13674
13675     // private
13676     alignErrorIcon : function(){
13677         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13678     },
13679
13680     // private
13681     initEvents : function(){
13682         
13683         this.createList();
13684         
13685         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13686         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13687         if(!this.multiple && this.showToggleBtn){
13688             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13689             if(this.hideTrigger){
13690                 this.trigger.setDisplayed(false);
13691             }
13692             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13693         }
13694         
13695         if(this.multiple){
13696             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13697         }
13698         
13699         if(this.removable && !this.editable && !this.tickable){
13700             var close = this.closeTriggerEl();
13701             
13702             if(close){
13703                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13704                 close.on('click', this.removeBtnClick, this, close);
13705             }
13706         }
13707         
13708         //this.trigger.addClassOnOver('x-form-trigger-over');
13709         //this.trigger.addClassOnClick('x-form-trigger-click');
13710         
13711         //if(!this.width){
13712         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13713         //}
13714     },
13715     
13716     closeTriggerEl : function()
13717     {
13718         var close = this.el.select('.roo-combo-removable-btn', true).first();
13719         return close ? close : false;
13720     },
13721     
13722     removeBtnClick : function(e, h, el)
13723     {
13724         e.preventDefault();
13725         
13726         if(this.fireEvent("remove", this) !== false){
13727             this.reset();
13728             this.fireEvent("afterremove", this)
13729         }
13730     },
13731     
13732     createList : function()
13733     {
13734         this.list = Roo.get(document.body).createChild({
13735             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13736             cls: 'typeahead typeahead-long dropdown-menu shadow',
13737             style: 'display:none'
13738         });
13739         
13740         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13741         
13742     },
13743
13744     // private
13745     initTrigger : function(){
13746        
13747     },
13748
13749     // private
13750     onDestroy : function(){
13751         if(this.trigger){
13752             this.trigger.removeAllListeners();
13753           //  this.trigger.remove();
13754         }
13755         //if(this.wrap){
13756         //    this.wrap.remove();
13757         //}
13758         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13759     },
13760
13761     // private
13762     onFocus : function(){
13763         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13764         /*
13765         if(!this.mimicing){
13766             this.wrap.addClass('x-trigger-wrap-focus');
13767             this.mimicing = true;
13768             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13769             if(this.monitorTab){
13770                 this.el.on("keydown", this.checkTab, this);
13771             }
13772         }
13773         */
13774     },
13775
13776     // private
13777     checkTab : function(e){
13778         if(e.getKey() == e.TAB){
13779             this.triggerBlur();
13780         }
13781     },
13782
13783     // private
13784     onBlur : function(){
13785         // do nothing
13786     },
13787
13788     // private
13789     mimicBlur : function(e, t){
13790         /*
13791         if(!this.wrap.contains(t) && this.validateBlur()){
13792             this.triggerBlur();
13793         }
13794         */
13795     },
13796
13797     // private
13798     triggerBlur : function(){
13799         this.mimicing = false;
13800         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13801         if(this.monitorTab){
13802             this.el.un("keydown", this.checkTab, this);
13803         }
13804         //this.wrap.removeClass('x-trigger-wrap-focus');
13805         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13806     },
13807
13808     // private
13809     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13810     validateBlur : function(e, t){
13811         return true;
13812     },
13813
13814     // private
13815     onDisable : function(){
13816         this.inputEl().dom.disabled = true;
13817         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13818         //if(this.wrap){
13819         //    this.wrap.addClass('x-item-disabled');
13820         //}
13821     },
13822
13823     // private
13824     onEnable : function(){
13825         this.inputEl().dom.disabled = false;
13826         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13827         //if(this.wrap){
13828         //    this.el.removeClass('x-item-disabled');
13829         //}
13830     },
13831
13832     // private
13833     onShow : function(){
13834         var ae = this.getActionEl();
13835         
13836         if(ae){
13837             ae.dom.style.display = '';
13838             ae.dom.style.visibility = 'visible';
13839         }
13840     },
13841
13842     // private
13843     
13844     onHide : function(){
13845         var ae = this.getActionEl();
13846         ae.dom.style.display = 'none';
13847     },
13848
13849     /**
13850      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13851      * by an implementing function.
13852      * @method
13853      * @param {EventObject} e
13854      */
13855     onTriggerClick : Roo.emptyFn
13856 });
13857  
13858 /*
13859 * Licence: LGPL
13860 */
13861
13862 /**
13863  * @class Roo.bootstrap.CardUploader
13864  * @extends Roo.bootstrap.Button
13865  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13866  * @cfg {Number} errorTimeout default 3000
13867  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13868  * @cfg {Array}  html The button text.
13869
13870  *
13871  * @constructor
13872  * Create a new CardUploader
13873  * @param {Object} config The config object
13874  */
13875
13876 Roo.bootstrap.CardUploader = function(config){
13877     
13878  
13879     
13880     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13881     
13882     
13883     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13884         return r.data.id
13885      });
13886     
13887      this.addEvents({
13888          // raw events
13889         /**
13890          * @event preview
13891          * When a image is clicked on - and needs to display a slideshow or similar..
13892          * @param {Roo.bootstrap.Card} this
13893          * @param {Object} The image information data 
13894          *
13895          */
13896         'preview' : true,
13897          /**
13898          * @event download
13899          * When a the download link is clicked
13900          * @param {Roo.bootstrap.Card} this
13901          * @param {Object} The image information data  contains 
13902          */
13903         'download' : true
13904         
13905     });
13906 };
13907  
13908 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13909     
13910      
13911     errorTimeout : 3000,
13912      
13913     images : false,
13914    
13915     fileCollection : false,
13916     allowBlank : true,
13917     
13918     getAutoCreate : function()
13919     {
13920         
13921         var cfg =  {
13922             cls :'form-group' ,
13923             cn : [
13924                
13925                 {
13926                     tag: 'label',
13927                    //cls : 'input-group-addon',
13928                     html : this.fieldLabel
13929
13930                 },
13931
13932                 {
13933                     tag: 'input',
13934                     type : 'hidden',
13935                     name : this.name,
13936                     value : this.value,
13937                     cls : 'd-none  form-control'
13938                 },
13939                 
13940                 {
13941                     tag: 'input',
13942                     multiple : 'multiple',
13943                     type : 'file',
13944                     cls : 'd-none  roo-card-upload-selector'
13945                 },
13946                 
13947                 {
13948                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13949                 },
13950                 {
13951                     cls : 'card-columns roo-card-uploader-container'
13952                 }
13953
13954             ]
13955         };
13956            
13957          
13958         return cfg;
13959     },
13960     
13961     getChildContainer : function() /// what children are added to.
13962     {
13963         return this.containerEl;
13964     },
13965    
13966     getButtonContainer : function() /// what children are added to.
13967     {
13968         return this.el.select(".roo-card-uploader-button-container").first();
13969     },
13970    
13971     initEvents : function()
13972     {
13973         
13974         Roo.bootstrap.Input.prototype.initEvents.call(this);
13975         
13976         var t = this;
13977         this.addxtype({
13978             xns: Roo.bootstrap,
13979
13980             xtype : 'Button',
13981             container_method : 'getButtonContainer' ,            
13982             html :  this.html, // fix changable?
13983             cls : 'w-100 ',
13984             listeners : {
13985                 'click' : function(btn, e) {
13986                     t.onClick(e);
13987                 }
13988             }
13989         });
13990         
13991         
13992         
13993         
13994         this.urlAPI = (window.createObjectURL && window) || 
13995                                 (window.URL && URL.revokeObjectURL && URL) || 
13996                                 (window.webkitURL && webkitURL);
13997                         
13998          
13999          
14000          
14001         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14002         
14003         this.selectorEl.on('change', this.onFileSelected, this);
14004         if (this.images) {
14005             var t = this;
14006             this.images.forEach(function(img) {
14007                 t.addCard(img)
14008             });
14009             this.images = false;
14010         }
14011         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14012          
14013        
14014     },
14015     
14016    
14017     onClick : function(e)
14018     {
14019         e.preventDefault();
14020          
14021         this.selectorEl.dom.click();
14022          
14023     },
14024     
14025     onFileSelected : function(e)
14026     {
14027         e.preventDefault();
14028         
14029         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14030             return;
14031         }
14032         
14033         Roo.each(this.selectorEl.dom.files, function(file){    
14034             this.addFile(file);
14035         }, this);
14036          
14037     },
14038     
14039       
14040     
14041       
14042     
14043     addFile : function(file)
14044     {
14045            
14046         if(typeof(file) === 'string'){
14047             throw "Add file by name?"; // should not happen
14048             return;
14049         }
14050         
14051         if(!file || !this.urlAPI){
14052             return;
14053         }
14054         
14055         // file;
14056         // file.type;
14057         
14058         var _this = this;
14059         
14060         
14061         var url = _this.urlAPI.createObjectURL( file);
14062            
14063         this.addCard({
14064             id : Roo.bootstrap.CardUploader.ID--,
14065             is_uploaded : false,
14066             src : url,
14067             srcfile : file,
14068             title : file.name,
14069             mimetype : file.type,
14070             preview : false,
14071             is_deleted : 0
14072         });
14073         
14074     },
14075     
14076     /**
14077      * addCard - add an Attachment to the uploader
14078      * @param data - the data about the image to upload
14079      *
14080      * {
14081           id : 123
14082           title : "Title of file",
14083           is_uploaded : false,
14084           src : "http://.....",
14085           srcfile : { the File upload object },
14086           mimetype : file.type,
14087           preview : false,
14088           is_deleted : 0
14089           .. any other data...
14090         }
14091      *
14092      * 
14093     */
14094     
14095     addCard : function (data)
14096     {
14097         // hidden input element?
14098         // if the file is not an image...
14099         //then we need to use something other that and header_image
14100         var t = this;
14101         //   remove.....
14102         var footer = [
14103             {
14104                 xns : Roo.bootstrap,
14105                 xtype : 'CardFooter',
14106                  items: [
14107                     {
14108                         xns : Roo.bootstrap,
14109                         xtype : 'Element',
14110                         cls : 'd-flex',
14111                         items : [
14112                             
14113                             {
14114                                 xns : Roo.bootstrap,
14115                                 xtype : 'Button',
14116                                 html : String.format("<small>{0}</small>", data.title),
14117                                 cls : 'col-10 text-left',
14118                                 size: 'sm',
14119                                 weight: 'link',
14120                                 fa : 'download',
14121                                 listeners : {
14122                                     click : function() {
14123                                      
14124                                         t.fireEvent( "download", t, data );
14125                                     }
14126                                 }
14127                             },
14128                           
14129                             {
14130                                 xns : Roo.bootstrap,
14131                                 xtype : 'Button',
14132                                 style: 'max-height: 28px; ',
14133                                 size : 'sm',
14134                                 weight: 'danger',
14135                                 cls : 'col-2',
14136                                 fa : 'times',
14137                                 listeners : {
14138                                     click : function() {
14139                                         t.removeCard(data.id)
14140                                     }
14141                                 }
14142                             }
14143                         ]
14144                     }
14145                     
14146                 ] 
14147             }
14148             
14149         ];
14150         
14151         var cn = this.addxtype(
14152             {
14153                  
14154                 xns : Roo.bootstrap,
14155                 xtype : 'Card',
14156                 closeable : true,
14157                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14158                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14159                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14160                 data : data,
14161                 html : false,
14162                  
14163                 items : footer,
14164                 initEvents : function() {
14165                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14166                     var card = this;
14167                     this.imgEl = this.el.select('.card-img-top').first();
14168                     if (this.imgEl) {
14169                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14170                         this.imgEl.set({ 'pointer' : 'cursor' });
14171                                   
14172                     }
14173                     this.getCardFooter().addClass('p-1');
14174                     
14175                   
14176                 }
14177                 
14178             }
14179         );
14180         // dont' really need ot update items.
14181         // this.items.push(cn);
14182         this.fileCollection.add(cn);
14183         
14184         if (!data.srcfile) {
14185             this.updateInput();
14186             return;
14187         }
14188             
14189         var _t = this;
14190         var reader = new FileReader();
14191         reader.addEventListener("load", function() {  
14192             data.srcdata =  reader.result;
14193             _t.updateInput();
14194         });
14195         reader.readAsDataURL(data.srcfile);
14196         
14197         
14198         
14199     },
14200     removeCard : function(id)
14201     {
14202         
14203         var card  = this.fileCollection.get(id);
14204         card.data.is_deleted = 1;
14205         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14206         //this.fileCollection.remove(card);
14207         //this.items = this.items.filter(function(e) { return e != card });
14208         // dont' really need ot update items.
14209         card.el.dom.parentNode.removeChild(card.el.dom);
14210         this.updateInput();
14211
14212         
14213     },
14214     reset: function()
14215     {
14216         this.fileCollection.each(function(card) {
14217             if (card.el.dom && card.el.dom.parentNode) {
14218                 card.el.dom.parentNode.removeChild(card.el.dom);
14219             }
14220         });
14221         this.fileCollection.clear();
14222         this.updateInput();
14223     },
14224     
14225     updateInput : function()
14226     {
14227          var data = [];
14228         this.fileCollection.each(function(e) {
14229             data.push(e.data);
14230             
14231         });
14232         this.inputEl().dom.value = JSON.stringify(data);
14233         
14234         
14235         
14236     }
14237     
14238     
14239 });
14240
14241
14242 Roo.bootstrap.CardUploader.ID = -1;/*
14243  * Based on:
14244  * Ext JS Library 1.1.1
14245  * Copyright(c) 2006-2007, Ext JS, LLC.
14246  *
14247  * Originally Released Under LGPL - original licence link has changed is not relivant.
14248  *
14249  * Fork - LGPL
14250  * <script type="text/javascript">
14251  */
14252
14253
14254 /**
14255  * @class Roo.data.SortTypes
14256  * @singleton
14257  * Defines the default sorting (casting?) comparison functions used when sorting data.
14258  */
14259 Roo.data.SortTypes = {
14260     /**
14261      * Default sort that does nothing
14262      * @param {Mixed} s The value being converted
14263      * @return {Mixed} The comparison value
14264      */
14265     none : function(s){
14266         return s;
14267     },
14268     
14269     /**
14270      * The regular expression used to strip tags
14271      * @type {RegExp}
14272      * @property
14273      */
14274     stripTagsRE : /<\/?[^>]+>/gi,
14275     
14276     /**
14277      * Strips all HTML tags to sort on text only
14278      * @param {Mixed} s The value being converted
14279      * @return {String} The comparison value
14280      */
14281     asText : function(s){
14282         return String(s).replace(this.stripTagsRE, "");
14283     },
14284     
14285     /**
14286      * Strips all HTML tags to sort on text only - Case insensitive
14287      * @param {Mixed} s The value being converted
14288      * @return {String} The comparison value
14289      */
14290     asUCText : function(s){
14291         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14292     },
14293     
14294     /**
14295      * Case insensitive string
14296      * @param {Mixed} s The value being converted
14297      * @return {String} The comparison value
14298      */
14299     asUCString : function(s) {
14300         return String(s).toUpperCase();
14301     },
14302     
14303     /**
14304      * Date sorting
14305      * @param {Mixed} s The value being converted
14306      * @return {Number} The comparison value
14307      */
14308     asDate : function(s) {
14309         if(!s){
14310             return 0;
14311         }
14312         if(s instanceof Date){
14313             return s.getTime();
14314         }
14315         return Date.parse(String(s));
14316     },
14317     
14318     /**
14319      * Float sorting
14320      * @param {Mixed} s The value being converted
14321      * @return {Float} The comparison value
14322      */
14323     asFloat : function(s) {
14324         var val = parseFloat(String(s).replace(/,/g, ""));
14325         if(isNaN(val)) {
14326             val = 0;
14327         }
14328         return val;
14329     },
14330     
14331     /**
14332      * Integer sorting
14333      * @param {Mixed} s The value being converted
14334      * @return {Number} The comparison value
14335      */
14336     asInt : function(s) {
14337         var val = parseInt(String(s).replace(/,/g, ""));
14338         if(isNaN(val)) {
14339             val = 0;
14340         }
14341         return val;
14342     }
14343 };/*
14344  * Based on:
14345  * Ext JS Library 1.1.1
14346  * Copyright(c) 2006-2007, Ext JS, LLC.
14347  *
14348  * Originally Released Under LGPL - original licence link has changed is not relivant.
14349  *
14350  * Fork - LGPL
14351  * <script type="text/javascript">
14352  */
14353
14354 /**
14355 * @class Roo.data.Record
14356  * Instances of this class encapsulate both record <em>definition</em> information, and record
14357  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14358  * to access Records cached in an {@link Roo.data.Store} object.<br>
14359  * <p>
14360  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14361  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14362  * objects.<br>
14363  * <p>
14364  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14365  * @constructor
14366  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14367  * {@link #create}. The parameters are the same.
14368  * @param {Array} data An associative Array of data values keyed by the field name.
14369  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14370  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14371  * not specified an integer id is generated.
14372  */
14373 Roo.data.Record = function(data, id){
14374     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14375     this.data = data;
14376 };
14377
14378 /**
14379  * Generate a constructor for a specific record layout.
14380  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14381  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14382  * Each field definition object may contain the following properties: <ul>
14383  * <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,
14384  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14385  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14386  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14387  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14388  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14389  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14390  * this may be omitted.</p></li>
14391  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14392  * <ul><li>auto (Default, implies no conversion)</li>
14393  * <li>string</li>
14394  * <li>int</li>
14395  * <li>float</li>
14396  * <li>boolean</li>
14397  * <li>date</li></ul></p></li>
14398  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14399  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14400  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14401  * by the Reader into an object that will be stored in the Record. It is passed the
14402  * following parameters:<ul>
14403  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14404  * </ul></p></li>
14405  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14406  * </ul>
14407  * <br>usage:<br><pre><code>
14408 var TopicRecord = Roo.data.Record.create(
14409     {name: 'title', mapping: 'topic_title'},
14410     {name: 'author', mapping: 'username'},
14411     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14412     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14413     {name: 'lastPoster', mapping: 'user2'},
14414     {name: 'excerpt', mapping: 'post_text'}
14415 );
14416
14417 var myNewRecord = new TopicRecord({
14418     title: 'Do my job please',
14419     author: 'noobie',
14420     totalPosts: 1,
14421     lastPost: new Date(),
14422     lastPoster: 'Animal',
14423     excerpt: 'No way dude!'
14424 });
14425 myStore.add(myNewRecord);
14426 </code></pre>
14427  * @method create
14428  * @static
14429  */
14430 Roo.data.Record.create = function(o){
14431     var f = function(){
14432         f.superclass.constructor.apply(this, arguments);
14433     };
14434     Roo.extend(f, Roo.data.Record);
14435     var p = f.prototype;
14436     p.fields = new Roo.util.MixedCollection(false, function(field){
14437         return field.name;
14438     });
14439     for(var i = 0, len = o.length; i < len; i++){
14440         p.fields.add(new Roo.data.Field(o[i]));
14441     }
14442     f.getField = function(name){
14443         return p.fields.get(name);  
14444     };
14445     return f;
14446 };
14447
14448 Roo.data.Record.AUTO_ID = 1000;
14449 Roo.data.Record.EDIT = 'edit';
14450 Roo.data.Record.REJECT = 'reject';
14451 Roo.data.Record.COMMIT = 'commit';
14452
14453 Roo.data.Record.prototype = {
14454     /**
14455      * Readonly flag - true if this record has been modified.
14456      * @type Boolean
14457      */
14458     dirty : false,
14459     editing : false,
14460     error: null,
14461     modified: null,
14462
14463     // private
14464     join : function(store){
14465         this.store = store;
14466     },
14467
14468     /**
14469      * Set the named field to the specified value.
14470      * @param {String} name The name of the field to set.
14471      * @param {Object} value The value to set the field to.
14472      */
14473     set : function(name, value){
14474         if(this.data[name] == value){
14475             return;
14476         }
14477         this.dirty = true;
14478         if(!this.modified){
14479             this.modified = {};
14480         }
14481         if(typeof this.modified[name] == 'undefined'){
14482             this.modified[name] = this.data[name];
14483         }
14484         this.data[name] = value;
14485         if(!this.editing && this.store){
14486             this.store.afterEdit(this);
14487         }       
14488     },
14489
14490     /**
14491      * Get the value of the named field.
14492      * @param {String} name The name of the field to get the value of.
14493      * @return {Object} The value of the field.
14494      */
14495     get : function(name){
14496         return this.data[name]; 
14497     },
14498
14499     // private
14500     beginEdit : function(){
14501         this.editing = true;
14502         this.modified = {}; 
14503     },
14504
14505     // private
14506     cancelEdit : function(){
14507         this.editing = false;
14508         delete this.modified;
14509     },
14510
14511     // private
14512     endEdit : function(){
14513         this.editing = false;
14514         if(this.dirty && this.store){
14515             this.store.afterEdit(this);
14516         }
14517     },
14518
14519     /**
14520      * Usually called by the {@link Roo.data.Store} which owns the Record.
14521      * Rejects all changes made to the Record since either creation, or the last commit operation.
14522      * Modified fields are reverted to their original values.
14523      * <p>
14524      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14525      * of reject operations.
14526      */
14527     reject : function(){
14528         var m = this.modified;
14529         for(var n in m){
14530             if(typeof m[n] != "function"){
14531                 this.data[n] = m[n];
14532             }
14533         }
14534         this.dirty = false;
14535         delete this.modified;
14536         this.editing = false;
14537         if(this.store){
14538             this.store.afterReject(this);
14539         }
14540     },
14541
14542     /**
14543      * Usually called by the {@link Roo.data.Store} which owns the Record.
14544      * Commits all changes made to the Record since either creation, or the last commit operation.
14545      * <p>
14546      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14547      * of commit operations.
14548      */
14549     commit : function(){
14550         this.dirty = false;
14551         delete this.modified;
14552         this.editing = false;
14553         if(this.store){
14554             this.store.afterCommit(this);
14555         }
14556     },
14557
14558     // private
14559     hasError : function(){
14560         return this.error != null;
14561     },
14562
14563     // private
14564     clearError : function(){
14565         this.error = null;
14566     },
14567
14568     /**
14569      * Creates a copy of this record.
14570      * @param {String} id (optional) A new record id if you don't want to use this record's id
14571      * @return {Record}
14572      */
14573     copy : function(newId) {
14574         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14575     }
14576 };/*
14577  * Based on:
14578  * Ext JS Library 1.1.1
14579  * Copyright(c) 2006-2007, Ext JS, LLC.
14580  *
14581  * Originally Released Under LGPL - original licence link has changed is not relivant.
14582  *
14583  * Fork - LGPL
14584  * <script type="text/javascript">
14585  */
14586
14587
14588
14589 /**
14590  * @class Roo.data.Store
14591  * @extends Roo.util.Observable
14592  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14593  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14594  * <p>
14595  * 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
14596  * has no knowledge of the format of the data returned by the Proxy.<br>
14597  * <p>
14598  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14599  * instances from the data object. These records are cached and made available through accessor functions.
14600  * @constructor
14601  * Creates a new Store.
14602  * @param {Object} config A config object containing the objects needed for the Store to access data,
14603  * and read the data into Records.
14604  */
14605 Roo.data.Store = function(config){
14606     this.data = new Roo.util.MixedCollection(false);
14607     this.data.getKey = function(o){
14608         return o.id;
14609     };
14610     this.baseParams = {};
14611     // private
14612     this.paramNames = {
14613         "start" : "start",
14614         "limit" : "limit",
14615         "sort" : "sort",
14616         "dir" : "dir",
14617         "multisort" : "_multisort"
14618     };
14619
14620     if(config && config.data){
14621         this.inlineData = config.data;
14622         delete config.data;
14623     }
14624
14625     Roo.apply(this, config);
14626     
14627     if(this.reader){ // reader passed
14628         this.reader = Roo.factory(this.reader, Roo.data);
14629         this.reader.xmodule = this.xmodule || false;
14630         if(!this.recordType){
14631             this.recordType = this.reader.recordType;
14632         }
14633         if(this.reader.onMetaChange){
14634             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14635         }
14636     }
14637
14638     if(this.recordType){
14639         this.fields = this.recordType.prototype.fields;
14640     }
14641     this.modified = [];
14642
14643     this.addEvents({
14644         /**
14645          * @event datachanged
14646          * Fires when the data cache has changed, and a widget which is using this Store
14647          * as a Record cache should refresh its view.
14648          * @param {Store} this
14649          */
14650         datachanged : true,
14651         /**
14652          * @event metachange
14653          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14654          * @param {Store} this
14655          * @param {Object} meta The JSON metadata
14656          */
14657         metachange : true,
14658         /**
14659          * @event add
14660          * Fires when Records have been added to the Store
14661          * @param {Store} this
14662          * @param {Roo.data.Record[]} records The array of Records added
14663          * @param {Number} index The index at which the record(s) were added
14664          */
14665         add : true,
14666         /**
14667          * @event remove
14668          * Fires when a Record has been removed from the Store
14669          * @param {Store} this
14670          * @param {Roo.data.Record} record The Record that was removed
14671          * @param {Number} index The index at which the record was removed
14672          */
14673         remove : true,
14674         /**
14675          * @event update
14676          * Fires when a Record has been updated
14677          * @param {Store} this
14678          * @param {Roo.data.Record} record The Record that was updated
14679          * @param {String} operation The update operation being performed.  Value may be one of:
14680          * <pre><code>
14681  Roo.data.Record.EDIT
14682  Roo.data.Record.REJECT
14683  Roo.data.Record.COMMIT
14684          * </code></pre>
14685          */
14686         update : true,
14687         /**
14688          * @event clear
14689          * Fires when the data cache has been cleared.
14690          * @param {Store} this
14691          */
14692         clear : true,
14693         /**
14694          * @event beforeload
14695          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14696          * the load action will be canceled.
14697          * @param {Store} this
14698          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14699          */
14700         beforeload : true,
14701         /**
14702          * @event beforeloadadd
14703          * Fires after a new set of Records has been loaded.
14704          * @param {Store} this
14705          * @param {Roo.data.Record[]} records The Records that were loaded
14706          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14707          */
14708         beforeloadadd : true,
14709         /**
14710          * @event load
14711          * Fires after a new set of Records has been loaded, before they are added to the store.
14712          * @param {Store} this
14713          * @param {Roo.data.Record[]} records The Records that were loaded
14714          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14715          * @params {Object} return from reader
14716          */
14717         load : true,
14718         /**
14719          * @event loadexception
14720          * Fires if an exception occurs in the Proxy during loading.
14721          * Called with the signature of the Proxy's "loadexception" event.
14722          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14723          * 
14724          * @param {Proxy} 
14725          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14726          * @param {Object} load options 
14727          * @param {Object} jsonData from your request (normally this contains the Exception)
14728          */
14729         loadexception : true
14730     });
14731     
14732     if(this.proxy){
14733         this.proxy = Roo.factory(this.proxy, Roo.data);
14734         this.proxy.xmodule = this.xmodule || false;
14735         this.relayEvents(this.proxy,  ["loadexception"]);
14736     }
14737     this.sortToggle = {};
14738     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14739
14740     Roo.data.Store.superclass.constructor.call(this);
14741
14742     if(this.inlineData){
14743         this.loadData(this.inlineData);
14744         delete this.inlineData;
14745     }
14746 };
14747
14748 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14749      /**
14750     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14751     * without a remote query - used by combo/forms at present.
14752     */
14753     
14754     /**
14755     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14756     */
14757     /**
14758     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14759     */
14760     /**
14761     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14762     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14763     */
14764     /**
14765     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14766     * on any HTTP request
14767     */
14768     /**
14769     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14770     */
14771     /**
14772     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14773     */
14774     multiSort: false,
14775     /**
14776     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14777     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14778     */
14779     remoteSort : false,
14780
14781     /**
14782     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14783      * loaded or when a record is removed. (defaults to false).
14784     */
14785     pruneModifiedRecords : false,
14786
14787     // private
14788     lastOptions : null,
14789
14790     /**
14791      * Add Records to the Store and fires the add event.
14792      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14793      */
14794     add : function(records){
14795         records = [].concat(records);
14796         for(var i = 0, len = records.length; i < len; i++){
14797             records[i].join(this);
14798         }
14799         var index = this.data.length;
14800         this.data.addAll(records);
14801         this.fireEvent("add", this, records, index);
14802     },
14803
14804     /**
14805      * Remove a Record from the Store and fires the remove event.
14806      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14807      */
14808     remove : function(record){
14809         var index = this.data.indexOf(record);
14810         this.data.removeAt(index);
14811  
14812         if(this.pruneModifiedRecords){
14813             this.modified.remove(record);
14814         }
14815         this.fireEvent("remove", this, record, index);
14816     },
14817
14818     /**
14819      * Remove all Records from the Store and fires the clear event.
14820      */
14821     removeAll : function(){
14822         this.data.clear();
14823         if(this.pruneModifiedRecords){
14824             this.modified = [];
14825         }
14826         this.fireEvent("clear", this);
14827     },
14828
14829     /**
14830      * Inserts Records to the Store at the given index and fires the add event.
14831      * @param {Number} index The start index at which to insert the passed Records.
14832      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14833      */
14834     insert : function(index, records){
14835         records = [].concat(records);
14836         for(var i = 0, len = records.length; i < len; i++){
14837             this.data.insert(index, records[i]);
14838             records[i].join(this);
14839         }
14840         this.fireEvent("add", this, records, index);
14841     },
14842
14843     /**
14844      * Get the index within the cache of the passed Record.
14845      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14846      * @return {Number} The index of the passed Record. Returns -1 if not found.
14847      */
14848     indexOf : function(record){
14849         return this.data.indexOf(record);
14850     },
14851
14852     /**
14853      * Get the index within the cache of the Record with the passed id.
14854      * @param {String} id The id of the Record to find.
14855      * @return {Number} The index of the Record. Returns -1 if not found.
14856      */
14857     indexOfId : function(id){
14858         return this.data.indexOfKey(id);
14859     },
14860
14861     /**
14862      * Get the Record with the specified id.
14863      * @param {String} id The id of the Record to find.
14864      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14865      */
14866     getById : function(id){
14867         return this.data.key(id);
14868     },
14869
14870     /**
14871      * Get the Record at the specified index.
14872      * @param {Number} index The index of the Record to find.
14873      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14874      */
14875     getAt : function(index){
14876         return this.data.itemAt(index);
14877     },
14878
14879     /**
14880      * Returns a range of Records between specified indices.
14881      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14882      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14883      * @return {Roo.data.Record[]} An array of Records
14884      */
14885     getRange : function(start, end){
14886         return this.data.getRange(start, end);
14887     },
14888
14889     // private
14890     storeOptions : function(o){
14891         o = Roo.apply({}, o);
14892         delete o.callback;
14893         delete o.scope;
14894         this.lastOptions = o;
14895     },
14896
14897     /**
14898      * Loads the Record cache from the configured Proxy using the configured Reader.
14899      * <p>
14900      * If using remote paging, then the first load call must specify the <em>start</em>
14901      * and <em>limit</em> properties in the options.params property to establish the initial
14902      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14903      * <p>
14904      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14905      * and this call will return before the new data has been loaded. Perform any post-processing
14906      * in a callback function, or in a "load" event handler.</strong>
14907      * <p>
14908      * @param {Object} options An object containing properties which control loading options:<ul>
14909      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14910      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14911      * passed the following arguments:<ul>
14912      * <li>r : Roo.data.Record[]</li>
14913      * <li>options: Options object from the load call</li>
14914      * <li>success: Boolean success indicator</li></ul></li>
14915      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14916      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14917      * </ul>
14918      */
14919     load : function(options){
14920         options = options || {};
14921         if(this.fireEvent("beforeload", this, options) !== false){
14922             this.storeOptions(options);
14923             var p = Roo.apply(options.params || {}, this.baseParams);
14924             // if meta was not loaded from remote source.. try requesting it.
14925             if (!this.reader.metaFromRemote) {
14926                 p._requestMeta = 1;
14927             }
14928             if(this.sortInfo && this.remoteSort){
14929                 var pn = this.paramNames;
14930                 p[pn["sort"]] = this.sortInfo.field;
14931                 p[pn["dir"]] = this.sortInfo.direction;
14932             }
14933             if (this.multiSort) {
14934                 var pn = this.paramNames;
14935                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14936             }
14937             
14938             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14939         }
14940     },
14941
14942     /**
14943      * Reloads the Record cache from the configured Proxy using the configured Reader and
14944      * the options from the last load operation performed.
14945      * @param {Object} options (optional) An object containing properties which may override the options
14946      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14947      * the most recently used options are reused).
14948      */
14949     reload : function(options){
14950         this.load(Roo.applyIf(options||{}, this.lastOptions));
14951     },
14952
14953     // private
14954     // Called as a callback by the Reader during a load operation.
14955     loadRecords : function(o, options, success){
14956         if(!o || success === false){
14957             if(success !== false){
14958                 this.fireEvent("load", this, [], options, o);
14959             }
14960             if(options.callback){
14961                 options.callback.call(options.scope || this, [], options, false);
14962             }
14963             return;
14964         }
14965         // if data returned failure - throw an exception.
14966         if (o.success === false) {
14967             // show a message if no listener is registered.
14968             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14969                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14970             }
14971             // loadmask wil be hooked into this..
14972             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14973             return;
14974         }
14975         var r = o.records, t = o.totalRecords || r.length;
14976         
14977         this.fireEvent("beforeloadadd", this, r, options, o);
14978         
14979         if(!options || options.add !== true){
14980             if(this.pruneModifiedRecords){
14981                 this.modified = [];
14982             }
14983             for(var i = 0, len = r.length; i < len; i++){
14984                 r[i].join(this);
14985             }
14986             if(this.snapshot){
14987                 this.data = this.snapshot;
14988                 delete this.snapshot;
14989             }
14990             this.data.clear();
14991             this.data.addAll(r);
14992             this.totalLength = t;
14993             this.applySort();
14994             this.fireEvent("datachanged", this);
14995         }else{
14996             this.totalLength = Math.max(t, this.data.length+r.length);
14997             this.add(r);
14998         }
14999         
15000         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15001                 
15002             var e = new Roo.data.Record({});
15003
15004             e.set(this.parent.displayField, this.parent.emptyTitle);
15005             e.set(this.parent.valueField, '');
15006
15007             this.insert(0, e);
15008         }
15009             
15010         this.fireEvent("load", this, r, options, o);
15011         if(options.callback){
15012             options.callback.call(options.scope || this, r, options, true);
15013         }
15014     },
15015
15016
15017     /**
15018      * Loads data from a passed data block. A Reader which understands the format of the data
15019      * must have been configured in the constructor.
15020      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15021      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15022      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15023      */
15024     loadData : function(o, append){
15025         var r = this.reader.readRecords(o);
15026         this.loadRecords(r, {add: append}, true);
15027     },
15028     
15029      /**
15030      * using 'cn' the nested child reader read the child array into it's child stores.
15031      * @param {Object} rec The record with a 'children array
15032      */
15033     loadDataFromChildren : function(rec)
15034     {
15035         this.loadData(this.reader.toLoadData(rec));
15036     },
15037     
15038
15039     /**
15040      * Gets the number of cached records.
15041      * <p>
15042      * <em>If using paging, this may not be the total size of the dataset. If the data object
15043      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15044      * the data set size</em>
15045      */
15046     getCount : function(){
15047         return this.data.length || 0;
15048     },
15049
15050     /**
15051      * Gets the total number of records in the dataset as returned by the server.
15052      * <p>
15053      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15054      * the dataset size</em>
15055      */
15056     getTotalCount : function(){
15057         return this.totalLength || 0;
15058     },
15059
15060     /**
15061      * Returns the sort state of the Store as an object with two properties:
15062      * <pre><code>
15063  field {String} The name of the field by which the Records are sorted
15064  direction {String} The sort order, "ASC" or "DESC"
15065      * </code></pre>
15066      */
15067     getSortState : function(){
15068         return this.sortInfo;
15069     },
15070
15071     // private
15072     applySort : function(){
15073         if(this.sortInfo && !this.remoteSort){
15074             var s = this.sortInfo, f = s.field;
15075             var st = this.fields.get(f).sortType;
15076             var fn = function(r1, r2){
15077                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15078                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15079             };
15080             this.data.sort(s.direction, fn);
15081             if(this.snapshot && this.snapshot != this.data){
15082                 this.snapshot.sort(s.direction, fn);
15083             }
15084         }
15085     },
15086
15087     /**
15088      * Sets the default sort column and order to be used by the next load operation.
15089      * @param {String} fieldName The name of the field to sort by.
15090      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15091      */
15092     setDefaultSort : function(field, dir){
15093         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15094     },
15095
15096     /**
15097      * Sort the Records.
15098      * If remote sorting is used, the sort is performed on the server, and the cache is
15099      * reloaded. If local sorting is used, the cache is sorted internally.
15100      * @param {String} fieldName The name of the field to sort by.
15101      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15102      */
15103     sort : function(fieldName, dir){
15104         var f = this.fields.get(fieldName);
15105         if(!dir){
15106             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15107             
15108             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15109                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15110             }else{
15111                 dir = f.sortDir;
15112             }
15113         }
15114         this.sortToggle[f.name] = dir;
15115         this.sortInfo = {field: f.name, direction: dir};
15116         if(!this.remoteSort){
15117             this.applySort();
15118             this.fireEvent("datachanged", this);
15119         }else{
15120             this.load(this.lastOptions);
15121         }
15122     },
15123
15124     /**
15125      * Calls the specified function for each of the Records in the cache.
15126      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15127      * Returning <em>false</em> aborts and exits the iteration.
15128      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15129      */
15130     each : function(fn, scope){
15131         this.data.each(fn, scope);
15132     },
15133
15134     /**
15135      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15136      * (e.g., during paging).
15137      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15138      */
15139     getModifiedRecords : function(){
15140         return this.modified;
15141     },
15142
15143     // private
15144     createFilterFn : function(property, value, anyMatch){
15145         if(!value.exec){ // not a regex
15146             value = String(value);
15147             if(value.length == 0){
15148                 return false;
15149             }
15150             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15151         }
15152         return function(r){
15153             return value.test(r.data[property]);
15154         };
15155     },
15156
15157     /**
15158      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15159      * @param {String} property A field on your records
15160      * @param {Number} start The record index to start at (defaults to 0)
15161      * @param {Number} end The last record index to include (defaults to length - 1)
15162      * @return {Number} The sum
15163      */
15164     sum : function(property, start, end){
15165         var rs = this.data.items, v = 0;
15166         start = start || 0;
15167         end = (end || end === 0) ? end : rs.length-1;
15168
15169         for(var i = start; i <= end; i++){
15170             v += (rs[i].data[property] || 0);
15171         }
15172         return v;
15173     },
15174
15175     /**
15176      * Filter the records by a specified property.
15177      * @param {String} field A field on your records
15178      * @param {String/RegExp} value Either a string that the field
15179      * should start with or a RegExp to test against the field
15180      * @param {Boolean} anyMatch True to match any part not just the beginning
15181      */
15182     filter : function(property, value, anyMatch){
15183         var fn = this.createFilterFn(property, value, anyMatch);
15184         return fn ? this.filterBy(fn) : this.clearFilter();
15185     },
15186
15187     /**
15188      * Filter by a function. The specified function will be called with each
15189      * record in this data source. If the function returns true the record is included,
15190      * otherwise it is filtered.
15191      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15192      * @param {Object} scope (optional) The scope of the function (defaults to this)
15193      */
15194     filterBy : function(fn, scope){
15195         this.snapshot = this.snapshot || this.data;
15196         this.data = this.queryBy(fn, scope||this);
15197         this.fireEvent("datachanged", this);
15198     },
15199
15200     /**
15201      * Query the records by a specified property.
15202      * @param {String} field A field on your records
15203      * @param {String/RegExp} value Either a string that the field
15204      * should start with or a RegExp to test against the field
15205      * @param {Boolean} anyMatch True to match any part not just the beginning
15206      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15207      */
15208     query : function(property, value, anyMatch){
15209         var fn = this.createFilterFn(property, value, anyMatch);
15210         return fn ? this.queryBy(fn) : this.data.clone();
15211     },
15212
15213     /**
15214      * Query by a function. The specified function will be called with each
15215      * record in this data source. If the function returns true the record is included
15216      * in the results.
15217      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15218      * @param {Object} scope (optional) The scope of the function (defaults to this)
15219       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15220      **/
15221     queryBy : function(fn, scope){
15222         var data = this.snapshot || this.data;
15223         return data.filterBy(fn, scope||this);
15224     },
15225
15226     /**
15227      * Collects unique values for a particular dataIndex from this store.
15228      * @param {String} dataIndex The property to collect
15229      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15230      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15231      * @return {Array} An array of the unique values
15232      **/
15233     collect : function(dataIndex, allowNull, bypassFilter){
15234         var d = (bypassFilter === true && this.snapshot) ?
15235                 this.snapshot.items : this.data.items;
15236         var v, sv, r = [], l = {};
15237         for(var i = 0, len = d.length; i < len; i++){
15238             v = d[i].data[dataIndex];
15239             sv = String(v);
15240             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15241                 l[sv] = true;
15242                 r[r.length] = v;
15243             }
15244         }
15245         return r;
15246     },
15247
15248     /**
15249      * Revert to a view of the Record cache with no filtering applied.
15250      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15251      */
15252     clearFilter : function(suppressEvent){
15253         if(this.snapshot && this.snapshot != this.data){
15254             this.data = this.snapshot;
15255             delete this.snapshot;
15256             if(suppressEvent !== true){
15257                 this.fireEvent("datachanged", this);
15258             }
15259         }
15260     },
15261
15262     // private
15263     afterEdit : function(record){
15264         if(this.modified.indexOf(record) == -1){
15265             this.modified.push(record);
15266         }
15267         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15268     },
15269     
15270     // private
15271     afterReject : function(record){
15272         this.modified.remove(record);
15273         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15274     },
15275
15276     // private
15277     afterCommit : function(record){
15278         this.modified.remove(record);
15279         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15280     },
15281
15282     /**
15283      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15284      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15285      */
15286     commitChanges : function(){
15287         var m = this.modified.slice(0);
15288         this.modified = [];
15289         for(var i = 0, len = m.length; i < len; i++){
15290             m[i].commit();
15291         }
15292     },
15293
15294     /**
15295      * Cancel outstanding changes on all changed records.
15296      */
15297     rejectChanges : function(){
15298         var m = this.modified.slice(0);
15299         this.modified = [];
15300         for(var i = 0, len = m.length; i < len; i++){
15301             m[i].reject();
15302         }
15303     },
15304
15305     onMetaChange : function(meta, rtype, o){
15306         this.recordType = rtype;
15307         this.fields = rtype.prototype.fields;
15308         delete this.snapshot;
15309         this.sortInfo = meta.sortInfo || this.sortInfo;
15310         this.modified = [];
15311         this.fireEvent('metachange', this, this.reader.meta);
15312     },
15313     
15314     moveIndex : function(data, type)
15315     {
15316         var index = this.indexOf(data);
15317         
15318         var newIndex = index + type;
15319         
15320         this.remove(data);
15321         
15322         this.insert(newIndex, data);
15323         
15324     }
15325 });/*
15326  * Based on:
15327  * Ext JS Library 1.1.1
15328  * Copyright(c) 2006-2007, Ext JS, LLC.
15329  *
15330  * Originally Released Under LGPL - original licence link has changed is not relivant.
15331  *
15332  * Fork - LGPL
15333  * <script type="text/javascript">
15334  */
15335
15336 /**
15337  * @class Roo.data.SimpleStore
15338  * @extends Roo.data.Store
15339  * Small helper class to make creating Stores from Array data easier.
15340  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15341  * @cfg {Array} fields An array of field definition objects, or field name strings.
15342  * @cfg {Object} an existing reader (eg. copied from another store)
15343  * @cfg {Array} data The multi-dimensional array of data
15344  * @constructor
15345  * @param {Object} config
15346  */
15347 Roo.data.SimpleStore = function(config)
15348 {
15349     Roo.data.SimpleStore.superclass.constructor.call(this, {
15350         isLocal : true,
15351         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15352                 id: config.id
15353             },
15354             Roo.data.Record.create(config.fields)
15355         ),
15356         proxy : new Roo.data.MemoryProxy(config.data)
15357     });
15358     this.load();
15359 };
15360 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15361  * Based on:
15362  * Ext JS Library 1.1.1
15363  * Copyright(c) 2006-2007, Ext JS, LLC.
15364  *
15365  * Originally Released Under LGPL - original licence link has changed is not relivant.
15366  *
15367  * Fork - LGPL
15368  * <script type="text/javascript">
15369  */
15370
15371 /**
15372 /**
15373  * @extends Roo.data.Store
15374  * @class Roo.data.JsonStore
15375  * Small helper class to make creating Stores for JSON data easier. <br/>
15376 <pre><code>
15377 var store = new Roo.data.JsonStore({
15378     url: 'get-images.php',
15379     root: 'images',
15380     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15381 });
15382 </code></pre>
15383  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15384  * JsonReader and HttpProxy (unless inline data is provided).</b>
15385  * @cfg {Array} fields An array of field definition objects, or field name strings.
15386  * @constructor
15387  * @param {Object} config
15388  */
15389 Roo.data.JsonStore = function(c){
15390     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15391         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15392         reader: new Roo.data.JsonReader(c, c.fields)
15393     }));
15394 };
15395 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15396  * Based on:
15397  * Ext JS Library 1.1.1
15398  * Copyright(c) 2006-2007, Ext JS, LLC.
15399  *
15400  * Originally Released Under LGPL - original licence link has changed is not relivant.
15401  *
15402  * Fork - LGPL
15403  * <script type="text/javascript">
15404  */
15405
15406  
15407 Roo.data.Field = function(config){
15408     if(typeof config == "string"){
15409         config = {name: config};
15410     }
15411     Roo.apply(this, config);
15412     
15413     if(!this.type){
15414         this.type = "auto";
15415     }
15416     
15417     var st = Roo.data.SortTypes;
15418     // named sortTypes are supported, here we look them up
15419     if(typeof this.sortType == "string"){
15420         this.sortType = st[this.sortType];
15421     }
15422     
15423     // set default sortType for strings and dates
15424     if(!this.sortType){
15425         switch(this.type){
15426             case "string":
15427                 this.sortType = st.asUCString;
15428                 break;
15429             case "date":
15430                 this.sortType = st.asDate;
15431                 break;
15432             default:
15433                 this.sortType = st.none;
15434         }
15435     }
15436
15437     // define once
15438     var stripRe = /[\$,%]/g;
15439
15440     // prebuilt conversion function for this field, instead of
15441     // switching every time we're reading a value
15442     if(!this.convert){
15443         var cv, dateFormat = this.dateFormat;
15444         switch(this.type){
15445             case "":
15446             case "auto":
15447             case undefined:
15448                 cv = function(v){ return v; };
15449                 break;
15450             case "string":
15451                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15452                 break;
15453             case "int":
15454                 cv = function(v){
15455                     return v !== undefined && v !== null && v !== '' ?
15456                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15457                     };
15458                 break;
15459             case "float":
15460                 cv = function(v){
15461                     return v !== undefined && v !== null && v !== '' ?
15462                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15463                     };
15464                 break;
15465             case "bool":
15466             case "boolean":
15467                 cv = function(v){ return v === true || v === "true" || v == 1; };
15468                 break;
15469             case "date":
15470                 cv = function(v){
15471                     if(!v){
15472                         return '';
15473                     }
15474                     if(v instanceof Date){
15475                         return v;
15476                     }
15477                     if(dateFormat){
15478                         if(dateFormat == "timestamp"){
15479                             return new Date(v*1000);
15480                         }
15481                         return Date.parseDate(v, dateFormat);
15482                     }
15483                     var parsed = Date.parse(v);
15484                     return parsed ? new Date(parsed) : null;
15485                 };
15486              break;
15487             
15488         }
15489         this.convert = cv;
15490     }
15491 };
15492
15493 Roo.data.Field.prototype = {
15494     dateFormat: null,
15495     defaultValue: "",
15496     mapping: null,
15497     sortType : null,
15498     sortDir : "ASC"
15499 };/*
15500  * Based on:
15501  * Ext JS Library 1.1.1
15502  * Copyright(c) 2006-2007, Ext JS, LLC.
15503  *
15504  * Originally Released Under LGPL - original licence link has changed is not relivant.
15505  *
15506  * Fork - LGPL
15507  * <script type="text/javascript">
15508  */
15509  
15510 // Base class for reading structured data from a data source.  This class is intended to be
15511 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15512
15513 /**
15514  * @class Roo.data.DataReader
15515  * Base class for reading structured data from a data source.  This class is intended to be
15516  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15517  */
15518
15519 Roo.data.DataReader = function(meta, recordType){
15520     
15521     this.meta = meta;
15522     
15523     this.recordType = recordType instanceof Array ? 
15524         Roo.data.Record.create(recordType) : recordType;
15525 };
15526
15527 Roo.data.DataReader.prototype = {
15528     
15529     
15530     readerType : 'Data',
15531      /**
15532      * Create an empty record
15533      * @param {Object} data (optional) - overlay some values
15534      * @return {Roo.data.Record} record created.
15535      */
15536     newRow :  function(d) {
15537         var da =  {};
15538         this.recordType.prototype.fields.each(function(c) {
15539             switch( c.type) {
15540                 case 'int' : da[c.name] = 0; break;
15541                 case 'date' : da[c.name] = new Date(); break;
15542                 case 'float' : da[c.name] = 0.0; break;
15543                 case 'boolean' : da[c.name] = false; break;
15544                 default : da[c.name] = ""; break;
15545             }
15546             
15547         });
15548         return new this.recordType(Roo.apply(da, d));
15549     }
15550     
15551     
15552 };/*
15553  * Based on:
15554  * Ext JS Library 1.1.1
15555  * Copyright(c) 2006-2007, Ext JS, LLC.
15556  *
15557  * Originally Released Under LGPL - original licence link has changed is not relivant.
15558  *
15559  * Fork - LGPL
15560  * <script type="text/javascript">
15561  */
15562
15563 /**
15564  * @class Roo.data.DataProxy
15565  * @extends Roo.data.Observable
15566  * This class is an abstract base class for implementations which provide retrieval of
15567  * unformatted data objects.<br>
15568  * <p>
15569  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15570  * (of the appropriate type which knows how to parse the data object) to provide a block of
15571  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15572  * <p>
15573  * Custom implementations must implement the load method as described in
15574  * {@link Roo.data.HttpProxy#load}.
15575  */
15576 Roo.data.DataProxy = function(){
15577     this.addEvents({
15578         /**
15579          * @event beforeload
15580          * Fires before a network request is made to retrieve a data object.
15581          * @param {Object} This DataProxy object.
15582          * @param {Object} params The params parameter to the load function.
15583          */
15584         beforeload : true,
15585         /**
15586          * @event load
15587          * Fires before the load method's callback is called.
15588          * @param {Object} This DataProxy object.
15589          * @param {Object} o The data object.
15590          * @param {Object} arg The callback argument object passed to the load function.
15591          */
15592         load : true,
15593         /**
15594          * @event loadexception
15595          * Fires if an Exception occurs during data retrieval.
15596          * @param {Object} This DataProxy object.
15597          * @param {Object} o The data object.
15598          * @param {Object} arg The callback argument object passed to the load function.
15599          * @param {Object} e The Exception.
15600          */
15601         loadexception : true
15602     });
15603     Roo.data.DataProxy.superclass.constructor.call(this);
15604 };
15605
15606 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15607
15608     /**
15609      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15610      */
15611 /*
15612  * Based on:
15613  * Ext JS Library 1.1.1
15614  * Copyright(c) 2006-2007, Ext JS, LLC.
15615  *
15616  * Originally Released Under LGPL - original licence link has changed is not relivant.
15617  *
15618  * Fork - LGPL
15619  * <script type="text/javascript">
15620  */
15621 /**
15622  * @class Roo.data.MemoryProxy
15623  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15624  * to the Reader when its load method is called.
15625  * @constructor
15626  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15627  */
15628 Roo.data.MemoryProxy = function(data){
15629     if (data.data) {
15630         data = data.data;
15631     }
15632     Roo.data.MemoryProxy.superclass.constructor.call(this);
15633     this.data = data;
15634 };
15635
15636 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15637     
15638     /**
15639      * Load data from the requested source (in this case an in-memory
15640      * data object passed to the constructor), read the data object into
15641      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15642      * process that block using the passed callback.
15643      * @param {Object} params This parameter is not used by the MemoryProxy class.
15644      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15645      * object into a block of Roo.data.Records.
15646      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15647      * The function must be passed <ul>
15648      * <li>The Record block object</li>
15649      * <li>The "arg" argument from the load function</li>
15650      * <li>A boolean success indicator</li>
15651      * </ul>
15652      * @param {Object} scope The scope in which to call the callback
15653      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15654      */
15655     load : function(params, reader, callback, scope, arg){
15656         params = params || {};
15657         var result;
15658         try {
15659             result = reader.readRecords(params.data ? params.data :this.data);
15660         }catch(e){
15661             this.fireEvent("loadexception", this, arg, null, e);
15662             callback.call(scope, null, arg, false);
15663             return;
15664         }
15665         callback.call(scope, result, arg, true);
15666     },
15667     
15668     // private
15669     update : function(params, records){
15670         
15671     }
15672 });/*
15673  * Based on:
15674  * Ext JS Library 1.1.1
15675  * Copyright(c) 2006-2007, Ext JS, LLC.
15676  *
15677  * Originally Released Under LGPL - original licence link has changed is not relivant.
15678  *
15679  * Fork - LGPL
15680  * <script type="text/javascript">
15681  */
15682 /**
15683  * @class Roo.data.HttpProxy
15684  * @extends Roo.data.DataProxy
15685  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15686  * configured to reference a certain URL.<br><br>
15687  * <p>
15688  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15689  * from which the running page was served.<br><br>
15690  * <p>
15691  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15692  * <p>
15693  * Be aware that to enable the browser to parse an XML document, the server must set
15694  * the Content-Type header in the HTTP response to "text/xml".
15695  * @constructor
15696  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15697  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15698  * will be used to make the request.
15699  */
15700 Roo.data.HttpProxy = function(conn){
15701     Roo.data.HttpProxy.superclass.constructor.call(this);
15702     // is conn a conn config or a real conn?
15703     this.conn = conn;
15704     this.useAjax = !conn || !conn.events;
15705   
15706 };
15707
15708 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15709     // thse are take from connection...
15710     
15711     /**
15712      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15713      */
15714     /**
15715      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15716      * extra parameters to each request made by this object. (defaults to undefined)
15717      */
15718     /**
15719      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15720      *  to each request made by this object. (defaults to undefined)
15721      */
15722     /**
15723      * @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)
15724      */
15725     /**
15726      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15727      */
15728      /**
15729      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15730      * @type Boolean
15731      */
15732   
15733
15734     /**
15735      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15736      * @type Boolean
15737      */
15738     /**
15739      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15740      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15741      * a finer-grained basis than the DataProxy events.
15742      */
15743     getConnection : function(){
15744         return this.useAjax ? Roo.Ajax : this.conn;
15745     },
15746
15747     /**
15748      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15749      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15750      * process that block using the passed callback.
15751      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15752      * for the request to the remote server.
15753      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15754      * object into a block of Roo.data.Records.
15755      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15756      * The function must be passed <ul>
15757      * <li>The Record block object</li>
15758      * <li>The "arg" argument from the load function</li>
15759      * <li>A boolean success indicator</li>
15760      * </ul>
15761      * @param {Object} scope The scope in which to call the callback
15762      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15763      */
15764     load : function(params, reader, callback, scope, arg){
15765         if(this.fireEvent("beforeload", this, params) !== false){
15766             var  o = {
15767                 params : params || {},
15768                 request: {
15769                     callback : callback,
15770                     scope : scope,
15771                     arg : arg
15772                 },
15773                 reader: reader,
15774                 callback : this.loadResponse,
15775                 scope: this
15776             };
15777             if(this.useAjax){
15778                 Roo.applyIf(o, this.conn);
15779                 if(this.activeRequest){
15780                     Roo.Ajax.abort(this.activeRequest);
15781                 }
15782                 this.activeRequest = Roo.Ajax.request(o);
15783             }else{
15784                 this.conn.request(o);
15785             }
15786         }else{
15787             callback.call(scope||this, null, arg, false);
15788         }
15789     },
15790
15791     // private
15792     loadResponse : function(o, success, response){
15793         delete this.activeRequest;
15794         if(!success){
15795             this.fireEvent("loadexception", this, o, response);
15796             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15797             return;
15798         }
15799         var result;
15800         try {
15801             result = o.reader.read(response);
15802         }catch(e){
15803             this.fireEvent("loadexception", this, o, response, e);
15804             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15805             return;
15806         }
15807         
15808         this.fireEvent("load", this, o, o.request.arg);
15809         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15810     },
15811
15812     // private
15813     update : function(dataSet){
15814
15815     },
15816
15817     // private
15818     updateResponse : function(dataSet){
15819
15820     }
15821 });/*
15822  * Based on:
15823  * Ext JS Library 1.1.1
15824  * Copyright(c) 2006-2007, Ext JS, LLC.
15825  *
15826  * Originally Released Under LGPL - original licence link has changed is not relivant.
15827  *
15828  * Fork - LGPL
15829  * <script type="text/javascript">
15830  */
15831
15832 /**
15833  * @class Roo.data.ScriptTagProxy
15834  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15835  * other than the originating domain of the running page.<br><br>
15836  * <p>
15837  * <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
15838  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15839  * <p>
15840  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15841  * source code that is used as the source inside a &lt;script> tag.<br><br>
15842  * <p>
15843  * In order for the browser to process the returned data, the server must wrap the data object
15844  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15845  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15846  * depending on whether the callback name was passed:
15847  * <p>
15848  * <pre><code>
15849 boolean scriptTag = false;
15850 String cb = request.getParameter("callback");
15851 if (cb != null) {
15852     scriptTag = true;
15853     response.setContentType("text/javascript");
15854 } else {
15855     response.setContentType("application/x-json");
15856 }
15857 Writer out = response.getWriter();
15858 if (scriptTag) {
15859     out.write(cb + "(");
15860 }
15861 out.print(dataBlock.toJsonString());
15862 if (scriptTag) {
15863     out.write(");");
15864 }
15865 </pre></code>
15866  *
15867  * @constructor
15868  * @param {Object} config A configuration object.
15869  */
15870 Roo.data.ScriptTagProxy = function(config){
15871     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15872     Roo.apply(this, config);
15873     this.head = document.getElementsByTagName("head")[0];
15874 };
15875
15876 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15877
15878 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15879     /**
15880      * @cfg {String} url The URL from which to request the data object.
15881      */
15882     /**
15883      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15884      */
15885     timeout : 30000,
15886     /**
15887      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15888      * the server the name of the callback function set up by the load call to process the returned data object.
15889      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15890      * javascript output which calls this named function passing the data object as its only parameter.
15891      */
15892     callbackParam : "callback",
15893     /**
15894      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15895      * name to the request.
15896      */
15897     nocache : true,
15898
15899     /**
15900      * Load data from the configured URL, read the data object into
15901      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15902      * process that block using the passed callback.
15903      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15904      * for the request to the remote server.
15905      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15906      * object into a block of Roo.data.Records.
15907      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15908      * The function must be passed <ul>
15909      * <li>The Record block object</li>
15910      * <li>The "arg" argument from the load function</li>
15911      * <li>A boolean success indicator</li>
15912      * </ul>
15913      * @param {Object} scope The scope in which to call the callback
15914      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15915      */
15916     load : function(params, reader, callback, scope, arg){
15917         if(this.fireEvent("beforeload", this, params) !== false){
15918
15919             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15920
15921             var url = this.url;
15922             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15923             if(this.nocache){
15924                 url += "&_dc=" + (new Date().getTime());
15925             }
15926             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15927             var trans = {
15928                 id : transId,
15929                 cb : "stcCallback"+transId,
15930                 scriptId : "stcScript"+transId,
15931                 params : params,
15932                 arg : arg,
15933                 url : url,
15934                 callback : callback,
15935                 scope : scope,
15936                 reader : reader
15937             };
15938             var conn = this;
15939
15940             window[trans.cb] = function(o){
15941                 conn.handleResponse(o, trans);
15942             };
15943
15944             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15945
15946             if(this.autoAbort !== false){
15947                 this.abort();
15948             }
15949
15950             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15951
15952             var script = document.createElement("script");
15953             script.setAttribute("src", url);
15954             script.setAttribute("type", "text/javascript");
15955             script.setAttribute("id", trans.scriptId);
15956             this.head.appendChild(script);
15957
15958             this.trans = trans;
15959         }else{
15960             callback.call(scope||this, null, arg, false);
15961         }
15962     },
15963
15964     // private
15965     isLoading : function(){
15966         return this.trans ? true : false;
15967     },
15968
15969     /**
15970      * Abort the current server request.
15971      */
15972     abort : function(){
15973         if(this.isLoading()){
15974             this.destroyTrans(this.trans);
15975         }
15976     },
15977
15978     // private
15979     destroyTrans : function(trans, isLoaded){
15980         this.head.removeChild(document.getElementById(trans.scriptId));
15981         clearTimeout(trans.timeoutId);
15982         if(isLoaded){
15983             window[trans.cb] = undefined;
15984             try{
15985                 delete window[trans.cb];
15986             }catch(e){}
15987         }else{
15988             // if hasn't been loaded, wait for load to remove it to prevent script error
15989             window[trans.cb] = function(){
15990                 window[trans.cb] = undefined;
15991                 try{
15992                     delete window[trans.cb];
15993                 }catch(e){}
15994             };
15995         }
15996     },
15997
15998     // private
15999     handleResponse : function(o, trans){
16000         this.trans = false;
16001         this.destroyTrans(trans, true);
16002         var result;
16003         try {
16004             result = trans.reader.readRecords(o);
16005         }catch(e){
16006             this.fireEvent("loadexception", this, o, trans.arg, e);
16007             trans.callback.call(trans.scope||window, null, trans.arg, false);
16008             return;
16009         }
16010         this.fireEvent("load", this, o, trans.arg);
16011         trans.callback.call(trans.scope||window, result, trans.arg, true);
16012     },
16013
16014     // private
16015     handleFailure : function(trans){
16016         this.trans = false;
16017         this.destroyTrans(trans, false);
16018         this.fireEvent("loadexception", this, null, trans.arg);
16019         trans.callback.call(trans.scope||window, null, trans.arg, false);
16020     }
16021 });/*
16022  * Based on:
16023  * Ext JS Library 1.1.1
16024  * Copyright(c) 2006-2007, Ext JS, LLC.
16025  *
16026  * Originally Released Under LGPL - original licence link has changed is not relivant.
16027  *
16028  * Fork - LGPL
16029  * <script type="text/javascript">
16030  */
16031
16032 /**
16033  * @class Roo.data.JsonReader
16034  * @extends Roo.data.DataReader
16035  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16036  * based on mappings in a provided Roo.data.Record constructor.
16037  * 
16038  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16039  * in the reply previously. 
16040  * 
16041  * <p>
16042  * Example code:
16043  * <pre><code>
16044 var RecordDef = Roo.data.Record.create([
16045     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16046     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16047 ]);
16048 var myReader = new Roo.data.JsonReader({
16049     totalProperty: "results",    // The property which contains the total dataset size (optional)
16050     root: "rows",                // The property which contains an Array of row objects
16051     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16052 }, RecordDef);
16053 </code></pre>
16054  * <p>
16055  * This would consume a JSON file like this:
16056  * <pre><code>
16057 { 'results': 2, 'rows': [
16058     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16059     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16060 }
16061 </code></pre>
16062  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16063  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16064  * paged from the remote server.
16065  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16066  * @cfg {String} root name of the property which contains the Array of row objects.
16067  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16068  * @cfg {Array} fields Array of field definition objects
16069  * @constructor
16070  * Create a new JsonReader
16071  * @param {Object} meta Metadata configuration options
16072  * @param {Object} recordType Either an Array of field definition objects,
16073  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16074  */
16075 Roo.data.JsonReader = function(meta, recordType){
16076     
16077     meta = meta || {};
16078     // set some defaults:
16079     Roo.applyIf(meta, {
16080         totalProperty: 'total',
16081         successProperty : 'success',
16082         root : 'data',
16083         id : 'id'
16084     });
16085     
16086     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16087 };
16088 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16089     
16090     readerType : 'Json',
16091     
16092     /**
16093      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16094      * Used by Store query builder to append _requestMeta to params.
16095      * 
16096      */
16097     metaFromRemote : false,
16098     /**
16099      * This method is only used by a DataProxy which has retrieved data from a remote server.
16100      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16101      * @return {Object} data A data block which is used by an Roo.data.Store object as
16102      * a cache of Roo.data.Records.
16103      */
16104     read : function(response){
16105         var json = response.responseText;
16106        
16107         var o = /* eval:var:o */ eval("("+json+")");
16108         if(!o) {
16109             throw {message: "JsonReader.read: Json object not found"};
16110         }
16111         
16112         if(o.metaData){
16113             
16114             delete this.ef;
16115             this.metaFromRemote = true;
16116             this.meta = o.metaData;
16117             this.recordType = Roo.data.Record.create(o.metaData.fields);
16118             this.onMetaChange(this.meta, this.recordType, o);
16119         }
16120         return this.readRecords(o);
16121     },
16122
16123     // private function a store will implement
16124     onMetaChange : function(meta, recordType, o){
16125
16126     },
16127
16128     /**
16129          * @ignore
16130          */
16131     simpleAccess: function(obj, subsc) {
16132         return obj[subsc];
16133     },
16134
16135         /**
16136          * @ignore
16137          */
16138     getJsonAccessor: function(){
16139         var re = /[\[\.]/;
16140         return function(expr) {
16141             try {
16142                 return(re.test(expr))
16143                     ? new Function("obj", "return obj." + expr)
16144                     : function(obj){
16145                         return obj[expr];
16146                     };
16147             } catch(e){}
16148             return Roo.emptyFn;
16149         };
16150     }(),
16151
16152     /**
16153      * Create a data block containing Roo.data.Records from an XML document.
16154      * @param {Object} o An object which contains an Array of row objects in the property specified
16155      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16156      * which contains the total size of the dataset.
16157      * @return {Object} data A data block which is used by an Roo.data.Store object as
16158      * a cache of Roo.data.Records.
16159      */
16160     readRecords : function(o){
16161         /**
16162          * After any data loads, the raw JSON data is available for further custom processing.
16163          * @type Object
16164          */
16165         this.o = o;
16166         var s = this.meta, Record = this.recordType,
16167             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16168
16169 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16170         if (!this.ef) {
16171             if(s.totalProperty) {
16172                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16173                 }
16174                 if(s.successProperty) {
16175                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16176                 }
16177                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16178                 if (s.id) {
16179                         var g = this.getJsonAccessor(s.id);
16180                         this.getId = function(rec) {
16181                                 var r = g(rec);  
16182                                 return (r === undefined || r === "") ? null : r;
16183                         };
16184                 } else {
16185                         this.getId = function(){return null;};
16186                 }
16187             this.ef = [];
16188             for(var jj = 0; jj < fl; jj++){
16189                 f = fi[jj];
16190                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16191                 this.ef[jj] = this.getJsonAccessor(map);
16192             }
16193         }
16194
16195         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16196         if(s.totalProperty){
16197             var vt = parseInt(this.getTotal(o), 10);
16198             if(!isNaN(vt)){
16199                 totalRecords = vt;
16200             }
16201         }
16202         if(s.successProperty){
16203             var vs = this.getSuccess(o);
16204             if(vs === false || vs === 'false'){
16205                 success = false;
16206             }
16207         }
16208         var records = [];
16209         for(var i = 0; i < c; i++){
16210                 var n = root[i];
16211             var values = {};
16212             var id = this.getId(n);
16213             for(var j = 0; j < fl; j++){
16214                 f = fi[j];
16215             var v = this.ef[j](n);
16216             if (!f.convert) {
16217                 Roo.log('missing convert for ' + f.name);
16218                 Roo.log(f);
16219                 continue;
16220             }
16221             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16222             }
16223             var record = new Record(values, id);
16224             record.json = n;
16225             records[i] = record;
16226         }
16227         return {
16228             raw : o,
16229             success : success,
16230             records : records,
16231             totalRecords : totalRecords
16232         };
16233     },
16234     // used when loading children.. @see loadDataFromChildren
16235     toLoadData: function(rec)
16236     {
16237         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16238         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16239         return { data : data, total : data.length };
16240         
16241     }
16242 });/*
16243  * Based on:
16244  * Ext JS Library 1.1.1
16245  * Copyright(c) 2006-2007, Ext JS, LLC.
16246  *
16247  * Originally Released Under LGPL - original licence link has changed is not relivant.
16248  *
16249  * Fork - LGPL
16250  * <script type="text/javascript">
16251  */
16252
16253 /**
16254  * @class Roo.data.ArrayReader
16255  * @extends Roo.data.DataReader
16256  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16257  * Each element of that Array represents a row of data fields. The
16258  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16259  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16260  * <p>
16261  * Example code:.
16262  * <pre><code>
16263 var RecordDef = Roo.data.Record.create([
16264     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16265     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16266 ]);
16267 var myReader = new Roo.data.ArrayReader({
16268     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16269 }, RecordDef);
16270 </code></pre>
16271  * <p>
16272  * This would consume an Array like this:
16273  * <pre><code>
16274 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16275   </code></pre>
16276  
16277  * @constructor
16278  * Create a new JsonReader
16279  * @param {Object} meta Metadata configuration options.
16280  * @param {Object|Array} recordType Either an Array of field definition objects
16281  * 
16282  * @cfg {Array} fields Array of field definition objects
16283  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16284  * as specified to {@link Roo.data.Record#create},
16285  * or an {@link Roo.data.Record} object
16286  *
16287  * 
16288  * created using {@link Roo.data.Record#create}.
16289  */
16290 Roo.data.ArrayReader = function(meta, recordType)
16291 {    
16292     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16293 };
16294
16295 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16296     
16297       /**
16298      * Create a data block containing Roo.data.Records from an XML document.
16299      * @param {Object} o An Array of row objects which represents the dataset.
16300      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16301      * a cache of Roo.data.Records.
16302      */
16303     readRecords : function(o)
16304     {
16305         var sid = this.meta ? this.meta.id : null;
16306         var recordType = this.recordType, fields = recordType.prototype.fields;
16307         var records = [];
16308         var root = o;
16309         for(var i = 0; i < root.length; i++){
16310             var n = root[i];
16311             var values = {};
16312             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16313             for(var j = 0, jlen = fields.length; j < jlen; j++){
16314                 var f = fields.items[j];
16315                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16316                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16317                 v = f.convert(v);
16318                 values[f.name] = v;
16319             }
16320             var record = new recordType(values, id);
16321             record.json = n;
16322             records[records.length] = record;
16323         }
16324         return {
16325             records : records,
16326             totalRecords : records.length
16327         };
16328     },
16329     // used when loading children.. @see loadDataFromChildren
16330     toLoadData: function(rec)
16331     {
16332         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16333         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16334         
16335     }
16336     
16337     
16338 });/*
16339  * - LGPL
16340  * * 
16341  */
16342
16343 /**
16344  * @class Roo.bootstrap.ComboBox
16345  * @extends Roo.bootstrap.TriggerField
16346  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16347  * @cfg {Boolean} append (true|false) default false
16348  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16349  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16350  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16351  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16352  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16353  * @cfg {Boolean} animate default true
16354  * @cfg {Boolean} emptyResultText only for touch device
16355  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16356  * @cfg {String} emptyTitle default ''
16357  * @cfg {Number} width fixed with? experimental
16358  * @constructor
16359  * Create a new ComboBox.
16360  * @param {Object} config Configuration options
16361  */
16362 Roo.bootstrap.ComboBox = function(config){
16363     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16364     this.addEvents({
16365         /**
16366          * @event expand
16367          * Fires when the dropdown list is expanded
16368         * @param {Roo.bootstrap.ComboBox} combo This combo box
16369         */
16370         'expand' : true,
16371         /**
16372          * @event collapse
16373          * Fires when the dropdown list is collapsed
16374         * @param {Roo.bootstrap.ComboBox} combo This combo box
16375         */
16376         'collapse' : true,
16377         /**
16378          * @event beforeselect
16379          * Fires before a list item is selected. Return false to cancel the selection.
16380         * @param {Roo.bootstrap.ComboBox} combo This combo box
16381         * @param {Roo.data.Record} record The data record returned from the underlying store
16382         * @param {Number} index The index of the selected item in the dropdown list
16383         */
16384         'beforeselect' : true,
16385         /**
16386          * @event select
16387          * Fires when a list item is selected
16388         * @param {Roo.bootstrap.ComboBox} combo This combo box
16389         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16390         * @param {Number} index The index of the selected item in the dropdown list
16391         */
16392         'select' : true,
16393         /**
16394          * @event beforequery
16395          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16396          * The event object passed has these properties:
16397         * @param {Roo.bootstrap.ComboBox} combo This combo box
16398         * @param {String} query The query
16399         * @param {Boolean} forceAll true to force "all" query
16400         * @param {Boolean} cancel true to cancel the query
16401         * @param {Object} e The query event object
16402         */
16403         'beforequery': true,
16404          /**
16405          * @event add
16406          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16407         * @param {Roo.bootstrap.ComboBox} combo This combo box
16408         */
16409         'add' : true,
16410         /**
16411          * @event edit
16412          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16413         * @param {Roo.bootstrap.ComboBox} combo This combo box
16414         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16415         */
16416         'edit' : true,
16417         /**
16418          * @event remove
16419          * Fires when the remove value from the combobox array
16420         * @param {Roo.bootstrap.ComboBox} combo This combo box
16421         */
16422         'remove' : true,
16423         /**
16424          * @event afterremove
16425          * Fires when the remove value from the combobox array
16426         * @param {Roo.bootstrap.ComboBox} combo This combo box
16427         */
16428         'afterremove' : true,
16429         /**
16430          * @event specialfilter
16431          * Fires when specialfilter
16432             * @param {Roo.bootstrap.ComboBox} combo This combo box
16433             */
16434         'specialfilter' : true,
16435         /**
16436          * @event tick
16437          * Fires when tick the element
16438             * @param {Roo.bootstrap.ComboBox} combo This combo box
16439             */
16440         'tick' : true,
16441         /**
16442          * @event touchviewdisplay
16443          * Fires when touch view require special display (default is using displayField)
16444             * @param {Roo.bootstrap.ComboBox} combo This combo box
16445             * @param {Object} cfg set html .
16446             */
16447         'touchviewdisplay' : true
16448         
16449     });
16450     
16451     this.item = [];
16452     this.tickItems = [];
16453     
16454     this.selectedIndex = -1;
16455     if(this.mode == 'local'){
16456         if(config.queryDelay === undefined){
16457             this.queryDelay = 10;
16458         }
16459         if(config.minChars === undefined){
16460             this.minChars = 0;
16461         }
16462     }
16463 };
16464
16465 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16466      
16467     /**
16468      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16469      * rendering into an Roo.Editor, defaults to false)
16470      */
16471     /**
16472      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16473      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16474      */
16475     /**
16476      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16477      */
16478     /**
16479      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16480      * the dropdown list (defaults to undefined, with no header element)
16481      */
16482
16483      /**
16484      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16485      */
16486      
16487      /**
16488      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16489      */
16490     listWidth: undefined,
16491     /**
16492      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16493      * mode = 'remote' or 'text' if mode = 'local')
16494      */
16495     displayField: undefined,
16496     
16497     /**
16498      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16499      * mode = 'remote' or 'value' if mode = 'local'). 
16500      * Note: use of a valueField requires the user make a selection
16501      * in order for a value to be mapped.
16502      */
16503     valueField: undefined,
16504     /**
16505      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16506      */
16507     modalTitle : '',
16508     
16509     /**
16510      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16511      * field's data value (defaults to the underlying DOM element's name)
16512      */
16513     hiddenName: undefined,
16514     /**
16515      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16516      */
16517     listClass: '',
16518     /**
16519      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16520      */
16521     selectedClass: 'active',
16522     
16523     /**
16524      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16525      */
16526     shadow:'sides',
16527     /**
16528      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16529      * anchor positions (defaults to 'tl-bl')
16530      */
16531     listAlign: 'tl-bl?',
16532     /**
16533      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16534      */
16535     maxHeight: 300,
16536     /**
16537      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16538      * query specified by the allQuery config option (defaults to 'query')
16539      */
16540     triggerAction: 'query',
16541     /**
16542      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16543      * (defaults to 4, does not apply if editable = false)
16544      */
16545     minChars : 4,
16546     /**
16547      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16548      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16549      */
16550     typeAhead: false,
16551     /**
16552      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16553      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16554      */
16555     queryDelay: 500,
16556     /**
16557      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16558      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16559      */
16560     pageSize: 0,
16561     /**
16562      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16563      * when editable = true (defaults to false)
16564      */
16565     selectOnFocus:false,
16566     /**
16567      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16568      */
16569     queryParam: 'query',
16570     /**
16571      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16572      * when mode = 'remote' (defaults to 'Loading...')
16573      */
16574     loadingText: 'Loading...',
16575     /**
16576      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16577      */
16578     resizable: false,
16579     /**
16580      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16581      */
16582     handleHeight : 8,
16583     /**
16584      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16585      * traditional select (defaults to true)
16586      */
16587     editable: true,
16588     /**
16589      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16590      */
16591     allQuery: '',
16592     /**
16593      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16594      */
16595     mode: 'remote',
16596     /**
16597      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16598      * listWidth has a higher value)
16599      */
16600     minListWidth : 70,
16601     /**
16602      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16603      * allow the user to set arbitrary text into the field (defaults to false)
16604      */
16605     forceSelection:false,
16606     /**
16607      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16608      * if typeAhead = true (defaults to 250)
16609      */
16610     typeAheadDelay : 250,
16611     /**
16612      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16613      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16614      */
16615     valueNotFoundText : undefined,
16616     /**
16617      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16618      */
16619     blockFocus : false,
16620     
16621     /**
16622      * @cfg {Boolean} disableClear Disable showing of clear button.
16623      */
16624     disableClear : false,
16625     /**
16626      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16627      */
16628     alwaysQuery : false,
16629     
16630     /**
16631      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16632      */
16633     multiple : false,
16634     
16635     /**
16636      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16637      */
16638     invalidClass : "has-warning",
16639     
16640     /**
16641      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16642      */
16643     validClass : "has-success",
16644     
16645     /**
16646      * @cfg {Boolean} specialFilter (true|false) special filter default false
16647      */
16648     specialFilter : false,
16649     
16650     /**
16651      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16652      */
16653     mobileTouchView : true,
16654     
16655     /**
16656      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16657      */
16658     useNativeIOS : false,
16659     
16660     /**
16661      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16662      */
16663     mobile_restrict_height : false,
16664     
16665     ios_options : false,
16666     
16667     //private
16668     addicon : false,
16669     editicon: false,
16670     
16671     page: 0,
16672     hasQuery: false,
16673     append: false,
16674     loadNext: false,
16675     autoFocus : true,
16676     tickable : false,
16677     btnPosition : 'right',
16678     triggerList : true,
16679     showToggleBtn : true,
16680     animate : true,
16681     emptyResultText: 'Empty',
16682     triggerText : 'Select',
16683     emptyTitle : '',
16684     width : false,
16685     
16686     // element that contains real text value.. (when hidden is used..)
16687     
16688     getAutoCreate : function()
16689     {   
16690         var cfg = false;
16691         //render
16692         /*
16693          * Render classic select for iso
16694          */
16695         
16696         if(Roo.isIOS && this.useNativeIOS){
16697             cfg = this.getAutoCreateNativeIOS();
16698             return cfg;
16699         }
16700         
16701         /*
16702          * Touch Devices
16703          */
16704         
16705         if(Roo.isTouch && this.mobileTouchView){
16706             cfg = this.getAutoCreateTouchView();
16707             return cfg;;
16708         }
16709         
16710         /*
16711          *  Normal ComboBox
16712          */
16713         if(!this.tickable){
16714             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16715             return cfg;
16716         }
16717         
16718         /*
16719          *  ComboBox with tickable selections
16720          */
16721              
16722         var align = this.labelAlign || this.parentLabelAlign();
16723         
16724         cfg = {
16725             cls : 'form-group roo-combobox-tickable' //input-group
16726         };
16727         
16728         var btn_text_select = '';
16729         var btn_text_done = '';
16730         var btn_text_cancel = '';
16731         
16732         if (this.btn_text_show) {
16733             btn_text_select = 'Select';
16734             btn_text_done = 'Done';
16735             btn_text_cancel = 'Cancel'; 
16736         }
16737         
16738         var buttons = {
16739             tag : 'div',
16740             cls : 'tickable-buttons',
16741             cn : [
16742                 {
16743                     tag : 'button',
16744                     type : 'button',
16745                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16746                     //html : this.triggerText
16747                     html: btn_text_select
16748                 },
16749                 {
16750                     tag : 'button',
16751                     type : 'button',
16752                     name : 'ok',
16753                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16754                     //html : 'Done'
16755                     html: btn_text_done
16756                 },
16757                 {
16758                     tag : 'button',
16759                     type : 'button',
16760                     name : 'cancel',
16761                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16762                     //html : 'Cancel'
16763                     html: btn_text_cancel
16764                 }
16765             ]
16766         };
16767         
16768         if(this.editable){
16769             buttons.cn.unshift({
16770                 tag: 'input',
16771                 cls: 'roo-select2-search-field-input'
16772             });
16773         }
16774         
16775         var _this = this;
16776         
16777         Roo.each(buttons.cn, function(c){
16778             if (_this.size) {
16779                 c.cls += ' btn-' + _this.size;
16780             }
16781
16782             if (_this.disabled) {
16783                 c.disabled = true;
16784             }
16785         });
16786         
16787         var box = {
16788             tag: 'div',
16789             style : 'display: contents',
16790             cn: [
16791                 {
16792                     tag: 'input',
16793                     type : 'hidden',
16794                     cls: 'form-hidden-field'
16795                 },
16796                 {
16797                     tag: 'ul',
16798                     cls: 'roo-select2-choices',
16799                     cn:[
16800                         {
16801                             tag: 'li',
16802                             cls: 'roo-select2-search-field',
16803                             cn: [
16804                                 buttons
16805                             ]
16806                         }
16807                     ]
16808                 }
16809             ]
16810         };
16811         
16812         var combobox = {
16813             cls: 'roo-select2-container input-group roo-select2-container-multi',
16814             cn: [
16815                 
16816                 box
16817 //                {
16818 //                    tag: 'ul',
16819 //                    cls: 'typeahead typeahead-long dropdown-menu',
16820 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16821 //                }
16822             ]
16823         };
16824         
16825         if(this.hasFeedback && !this.allowBlank){
16826             
16827             var feedback = {
16828                 tag: 'span',
16829                 cls: 'glyphicon form-control-feedback'
16830             };
16831
16832             combobox.cn.push(feedback);
16833         }
16834         
16835         
16836         
16837         var indicator = {
16838             tag : 'i',
16839             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16840             tooltip : 'This field is required'
16841         };
16842         if (Roo.bootstrap.version == 4) {
16843             indicator = {
16844                 tag : 'i',
16845                 style : 'display:none'
16846             };
16847         }
16848         if (align ==='left' && this.fieldLabel.length) {
16849             
16850             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16851             
16852             cfg.cn = [
16853                 indicator,
16854                 {
16855                     tag: 'label',
16856                     'for' :  id,
16857                     cls : 'control-label col-form-label',
16858                     html : this.fieldLabel
16859
16860                 },
16861                 {
16862                     cls : "", 
16863                     cn: [
16864                         combobox
16865                     ]
16866                 }
16867
16868             ];
16869             
16870             var labelCfg = cfg.cn[1];
16871             var contentCfg = cfg.cn[2];
16872             
16873
16874             if(this.indicatorpos == 'right'){
16875                 
16876                 cfg.cn = [
16877                     {
16878                         tag: 'label',
16879                         'for' :  id,
16880                         cls : 'control-label col-form-label',
16881                         cn : [
16882                             {
16883                                 tag : 'span',
16884                                 html : this.fieldLabel
16885                             },
16886                             indicator
16887                         ]
16888                     },
16889                     {
16890                         cls : "",
16891                         cn: [
16892                             combobox
16893                         ]
16894                     }
16895
16896                 ];
16897                 
16898                 
16899                 
16900                 labelCfg = cfg.cn[0];
16901                 contentCfg = cfg.cn[1];
16902             
16903             }
16904             
16905             if(this.labelWidth > 12){
16906                 labelCfg.style = "width: " + this.labelWidth + 'px';
16907             }
16908             if(this.width * 1 > 0){
16909                 contentCfg.style = "width: " + this.width + 'px';
16910             }
16911             if(this.labelWidth < 13 && this.labelmd == 0){
16912                 this.labelmd = this.labelWidth;
16913             }
16914             
16915             if(this.labellg > 0){
16916                 labelCfg.cls += ' col-lg-' + this.labellg;
16917                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16918             }
16919             
16920             if(this.labelmd > 0){
16921                 labelCfg.cls += ' col-md-' + this.labelmd;
16922                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16923             }
16924             
16925             if(this.labelsm > 0){
16926                 labelCfg.cls += ' col-sm-' + this.labelsm;
16927                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16928             }
16929             
16930             if(this.labelxs > 0){
16931                 labelCfg.cls += ' col-xs-' + this.labelxs;
16932                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16933             }
16934                 
16935                 
16936         } else if ( this.fieldLabel.length) {
16937 //                Roo.log(" label");
16938                  cfg.cn = [
16939                    indicator,
16940                     {
16941                         tag: 'label',
16942                         //cls : 'input-group-addon',
16943                         html : this.fieldLabel
16944                     },
16945                     combobox
16946                 ];
16947                 
16948                 if(this.indicatorpos == 'right'){
16949                     cfg.cn = [
16950                         {
16951                             tag: 'label',
16952                             //cls : 'input-group-addon',
16953                             html : this.fieldLabel
16954                         },
16955                         indicator,
16956                         combobox
16957                     ];
16958                     
16959                 }
16960
16961         } else {
16962             
16963 //                Roo.log(" no label && no align");
16964                 cfg = combobox
16965                      
16966                 
16967         }
16968          
16969         var settings=this;
16970         ['xs','sm','md','lg'].map(function(size){
16971             if (settings[size]) {
16972                 cfg.cls += ' col-' + size + '-' + settings[size];
16973             }
16974         });
16975         
16976         return cfg;
16977         
16978     },
16979     
16980     _initEventsCalled : false,
16981     
16982     // private
16983     initEvents: function()
16984     {   
16985         if (this._initEventsCalled) { // as we call render... prevent looping...
16986             return;
16987         }
16988         this._initEventsCalled = true;
16989         
16990         if (!this.store) {
16991             throw "can not find store for combo";
16992         }
16993         
16994         this.indicator = this.indicatorEl();
16995         
16996         this.store = Roo.factory(this.store, Roo.data);
16997         this.store.parent = this;
16998         
16999         // if we are building from html. then this element is so complex, that we can not really
17000         // use the rendered HTML.
17001         // so we have to trash and replace the previous code.
17002         if (Roo.XComponent.build_from_html) {
17003             // remove this element....
17004             var e = this.el.dom, k=0;
17005             while (e ) { e = e.previousSibling;  ++k;}
17006
17007             this.el.remove();
17008             
17009             this.el=false;
17010             this.rendered = false;
17011             
17012             this.render(this.parent().getChildContainer(true), k);
17013         }
17014         
17015         if(Roo.isIOS && this.useNativeIOS){
17016             this.initIOSView();
17017             return;
17018         }
17019         
17020         /*
17021          * Touch Devices
17022          */
17023         
17024         if(Roo.isTouch && this.mobileTouchView){
17025             this.initTouchView();
17026             return;
17027         }
17028         
17029         if(this.tickable){
17030             this.initTickableEvents();
17031             return;
17032         }
17033         
17034         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17035         
17036         if(this.hiddenName){
17037             
17038             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17039             
17040             this.hiddenField.dom.value =
17041                 this.hiddenValue !== undefined ? this.hiddenValue :
17042                 this.value !== undefined ? this.value : '';
17043
17044             // prevent input submission
17045             this.el.dom.removeAttribute('name');
17046             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17047              
17048              
17049         }
17050         //if(Roo.isGecko){
17051         //    this.el.dom.setAttribute('autocomplete', 'off');
17052         //}
17053         
17054         var cls = 'x-combo-list';
17055         
17056         //this.list = new Roo.Layer({
17057         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17058         //});
17059         
17060         var _this = this;
17061         
17062         (function(){
17063             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17064             _this.list.setWidth(lw);
17065         }).defer(100);
17066         
17067         this.list.on('mouseover', this.onViewOver, this);
17068         this.list.on('mousemove', this.onViewMove, this);
17069         this.list.on('scroll', this.onViewScroll, this);
17070         
17071         /*
17072         this.list.swallowEvent('mousewheel');
17073         this.assetHeight = 0;
17074
17075         if(this.title){
17076             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17077             this.assetHeight += this.header.getHeight();
17078         }
17079
17080         this.innerList = this.list.createChild({cls:cls+'-inner'});
17081         this.innerList.on('mouseover', this.onViewOver, this);
17082         this.innerList.on('mousemove', this.onViewMove, this);
17083         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17084         
17085         if(this.allowBlank && !this.pageSize && !this.disableClear){
17086             this.footer = this.list.createChild({cls:cls+'-ft'});
17087             this.pageTb = new Roo.Toolbar(this.footer);
17088            
17089         }
17090         if(this.pageSize){
17091             this.footer = this.list.createChild({cls:cls+'-ft'});
17092             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17093                     {pageSize: this.pageSize});
17094             
17095         }
17096         
17097         if (this.pageTb && this.allowBlank && !this.disableClear) {
17098             var _this = this;
17099             this.pageTb.add(new Roo.Toolbar.Fill(), {
17100                 cls: 'x-btn-icon x-btn-clear',
17101                 text: '&#160;',
17102                 handler: function()
17103                 {
17104                     _this.collapse();
17105                     _this.clearValue();
17106                     _this.onSelect(false, -1);
17107                 }
17108             });
17109         }
17110         if (this.footer) {
17111             this.assetHeight += this.footer.getHeight();
17112         }
17113         */
17114             
17115         if(!this.tpl){
17116             this.tpl = Roo.bootstrap.version == 4 ?
17117                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17118                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17119         }
17120
17121         this.view = new Roo.View(this.list, this.tpl, {
17122             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17123         });
17124         //this.view.wrapEl.setDisplayed(false);
17125         this.view.on('click', this.onViewClick, this);
17126         
17127         
17128         this.store.on('beforeload', this.onBeforeLoad, this);
17129         this.store.on('load', this.onLoad, this);
17130         this.store.on('loadexception', this.onLoadException, this);
17131         /*
17132         if(this.resizable){
17133             this.resizer = new Roo.Resizable(this.list,  {
17134                pinned:true, handles:'se'
17135             });
17136             this.resizer.on('resize', function(r, w, h){
17137                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17138                 this.listWidth = w;
17139                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17140                 this.restrictHeight();
17141             }, this);
17142             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17143         }
17144         */
17145         if(!this.editable){
17146             this.editable = true;
17147             this.setEditable(false);
17148         }
17149         
17150         /*
17151         
17152         if (typeof(this.events.add.listeners) != 'undefined') {
17153             
17154             this.addicon = this.wrap.createChild(
17155                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17156        
17157             this.addicon.on('click', function(e) {
17158                 this.fireEvent('add', this);
17159             }, this);
17160         }
17161         if (typeof(this.events.edit.listeners) != 'undefined') {
17162             
17163             this.editicon = this.wrap.createChild(
17164                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17165             if (this.addicon) {
17166                 this.editicon.setStyle('margin-left', '40px');
17167             }
17168             this.editicon.on('click', function(e) {
17169                 
17170                 // we fire even  if inothing is selected..
17171                 this.fireEvent('edit', this, this.lastData );
17172                 
17173             }, this);
17174         }
17175         */
17176         
17177         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17178             "up" : function(e){
17179                 this.inKeyMode = true;
17180                 this.selectPrev();
17181             },
17182
17183             "down" : function(e){
17184                 if(!this.isExpanded()){
17185                     this.onTriggerClick();
17186                 }else{
17187                     this.inKeyMode = true;
17188                     this.selectNext();
17189                 }
17190             },
17191
17192             "enter" : function(e){
17193 //                this.onViewClick();
17194                 //return true;
17195                 this.collapse();
17196                 
17197                 if(this.fireEvent("specialkey", this, e)){
17198                     this.onViewClick(false);
17199                 }
17200                 
17201                 return true;
17202             },
17203
17204             "esc" : function(e){
17205                 this.collapse();
17206             },
17207
17208             "tab" : function(e){
17209                 this.collapse();
17210                 
17211                 if(this.fireEvent("specialkey", this, e)){
17212                     this.onViewClick(false);
17213                 }
17214                 
17215                 return true;
17216             },
17217
17218             scope : this,
17219
17220             doRelay : function(foo, bar, hname){
17221                 if(hname == 'down' || this.scope.isExpanded()){
17222                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17223                 }
17224                 return true;
17225             },
17226
17227             forceKeyDown: true
17228         });
17229         
17230         
17231         this.queryDelay = Math.max(this.queryDelay || 10,
17232                 this.mode == 'local' ? 10 : 250);
17233         
17234         
17235         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17236         
17237         if(this.typeAhead){
17238             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17239         }
17240         if(this.editable !== false){
17241             this.inputEl().on("keyup", this.onKeyUp, this);
17242         }
17243         if(this.forceSelection){
17244             this.inputEl().on('blur', this.doForce, this);
17245         }
17246         
17247         if(this.multiple){
17248             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17249             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17250         }
17251     },
17252     
17253     initTickableEvents: function()
17254     {   
17255         this.createList();
17256         
17257         if(this.hiddenName){
17258             
17259             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17260             
17261             this.hiddenField.dom.value =
17262                 this.hiddenValue !== undefined ? this.hiddenValue :
17263                 this.value !== undefined ? this.value : '';
17264
17265             // prevent input submission
17266             this.el.dom.removeAttribute('name');
17267             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17268              
17269              
17270         }
17271         
17272 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17273         
17274         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17275         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17276         if(this.triggerList){
17277             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17278         }
17279          
17280         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17281         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17282         
17283         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17284         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17285         
17286         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17287         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17288         
17289         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17290         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17291         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17292         
17293         this.okBtn.hide();
17294         this.cancelBtn.hide();
17295         
17296         var _this = this;
17297         
17298         (function(){
17299             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17300             _this.list.setWidth(lw);
17301         }).defer(100);
17302         
17303         this.list.on('mouseover', this.onViewOver, this);
17304         this.list.on('mousemove', this.onViewMove, this);
17305         
17306         this.list.on('scroll', this.onViewScroll, this);
17307         
17308         if(!this.tpl){
17309             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17310                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17311         }
17312
17313         this.view = new Roo.View(this.list, this.tpl, {
17314             singleSelect:true,
17315             tickable:true,
17316             parent:this,
17317             store: this.store,
17318             selectedClass: this.selectedClass
17319         });
17320         
17321         //this.view.wrapEl.setDisplayed(false);
17322         this.view.on('click', this.onViewClick, this);
17323         
17324         
17325         
17326         this.store.on('beforeload', this.onBeforeLoad, this);
17327         this.store.on('load', this.onLoad, this);
17328         this.store.on('loadexception', this.onLoadException, this);
17329         
17330         if(this.editable){
17331             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17332                 "up" : function(e){
17333                     this.inKeyMode = true;
17334                     this.selectPrev();
17335                 },
17336
17337                 "down" : function(e){
17338                     this.inKeyMode = true;
17339                     this.selectNext();
17340                 },
17341
17342                 "enter" : function(e){
17343                     if(this.fireEvent("specialkey", this, e)){
17344                         this.onViewClick(false);
17345                     }
17346                     
17347                     return true;
17348                 },
17349
17350                 "esc" : function(e){
17351                     this.onTickableFooterButtonClick(e, false, false);
17352                 },
17353
17354                 "tab" : function(e){
17355                     this.fireEvent("specialkey", this, e);
17356                     
17357                     this.onTickableFooterButtonClick(e, false, false);
17358                     
17359                     return true;
17360                 },
17361
17362                 scope : this,
17363
17364                 doRelay : function(e, fn, key){
17365                     if(this.scope.isExpanded()){
17366                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17367                     }
17368                     return true;
17369                 },
17370
17371                 forceKeyDown: true
17372             });
17373         }
17374         
17375         this.queryDelay = Math.max(this.queryDelay || 10,
17376                 this.mode == 'local' ? 10 : 250);
17377         
17378         
17379         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17380         
17381         if(this.typeAhead){
17382             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17383         }
17384         
17385         if(this.editable !== false){
17386             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17387         }
17388         
17389         this.indicator = this.indicatorEl();
17390         
17391         if(this.indicator){
17392             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17393             this.indicator.hide();
17394         }
17395         
17396     },
17397
17398     onDestroy : function(){
17399         if(this.view){
17400             this.view.setStore(null);
17401             this.view.el.removeAllListeners();
17402             this.view.el.remove();
17403             this.view.purgeListeners();
17404         }
17405         if(this.list){
17406             this.list.dom.innerHTML  = '';
17407         }
17408         
17409         if(this.store){
17410             this.store.un('beforeload', this.onBeforeLoad, this);
17411             this.store.un('load', this.onLoad, this);
17412             this.store.un('loadexception', this.onLoadException, this);
17413         }
17414         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17415     },
17416
17417     // private
17418     fireKey : function(e){
17419         if(e.isNavKeyPress() && !this.list.isVisible()){
17420             this.fireEvent("specialkey", this, e);
17421         }
17422     },
17423
17424     // private
17425     onResize: function(w, h)
17426     {
17427         
17428         
17429 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17430 //        
17431 //        if(typeof w != 'number'){
17432 //            // we do not handle it!?!?
17433 //            return;
17434 //        }
17435 //        var tw = this.trigger.getWidth();
17436 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17437 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17438 //        var x = w - tw;
17439 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17440 //            
17441 //        //this.trigger.setStyle('left', x+'px');
17442 //        
17443 //        if(this.list && this.listWidth === undefined){
17444 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17445 //            this.list.setWidth(lw);
17446 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17447 //        }
17448         
17449     
17450         
17451     },
17452
17453     /**
17454      * Allow or prevent the user from directly editing the field text.  If false is passed,
17455      * the user will only be able to select from the items defined in the dropdown list.  This method
17456      * is the runtime equivalent of setting the 'editable' config option at config time.
17457      * @param {Boolean} value True to allow the user to directly edit the field text
17458      */
17459     setEditable : function(value){
17460         if(value == this.editable){
17461             return;
17462         }
17463         this.editable = value;
17464         if(!value){
17465             this.inputEl().dom.setAttribute('readOnly', true);
17466             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17467             this.inputEl().addClass('x-combo-noedit');
17468         }else{
17469             this.inputEl().dom.removeAttribute('readOnly');
17470             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17471             this.inputEl().removeClass('x-combo-noedit');
17472         }
17473     },
17474
17475     // private
17476     
17477     onBeforeLoad : function(combo,opts){
17478         if(!this.hasFocus){
17479             return;
17480         }
17481          if (!opts.add) {
17482             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17483          }
17484         this.restrictHeight();
17485         this.selectedIndex = -1;
17486     },
17487
17488     // private
17489     onLoad : function(){
17490         
17491         this.hasQuery = false;
17492         
17493         if(!this.hasFocus){
17494             return;
17495         }
17496         
17497         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17498             this.loading.hide();
17499         }
17500         
17501         if(this.store.getCount() > 0){
17502             
17503             this.expand();
17504             this.restrictHeight();
17505             if(this.lastQuery == this.allQuery){
17506                 if(this.editable && !this.tickable){
17507                     this.inputEl().dom.select();
17508                 }
17509                 
17510                 if(
17511                     !this.selectByValue(this.value, true) &&
17512                     this.autoFocus && 
17513                     (
17514                         !this.store.lastOptions ||
17515                         typeof(this.store.lastOptions.add) == 'undefined' || 
17516                         this.store.lastOptions.add != true
17517                     )
17518                 ){
17519                     this.select(0, true);
17520                 }
17521             }else{
17522                 if(this.autoFocus){
17523                     this.selectNext();
17524                 }
17525                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17526                     this.taTask.delay(this.typeAheadDelay);
17527                 }
17528             }
17529         }else{
17530             this.onEmptyResults();
17531         }
17532         
17533         //this.el.focus();
17534     },
17535     // private
17536     onLoadException : function()
17537     {
17538         this.hasQuery = false;
17539         
17540         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17541             this.loading.hide();
17542         }
17543         
17544         if(this.tickable && this.editable){
17545             return;
17546         }
17547         
17548         this.collapse();
17549         // only causes errors at present
17550         //Roo.log(this.store.reader.jsonData);
17551         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17552             // fixme
17553             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17554         //}
17555         
17556         
17557     },
17558     // private
17559     onTypeAhead : function(){
17560         if(this.store.getCount() > 0){
17561             var r = this.store.getAt(0);
17562             var newValue = r.data[this.displayField];
17563             var len = newValue.length;
17564             var selStart = this.getRawValue().length;
17565             
17566             if(selStart != len){
17567                 this.setRawValue(newValue);
17568                 this.selectText(selStart, newValue.length);
17569             }
17570         }
17571     },
17572
17573     // private
17574     onSelect : function(record, index){
17575         
17576         if(this.fireEvent('beforeselect', this, record, index) !== false){
17577         
17578             this.setFromData(index > -1 ? record.data : false);
17579             
17580             this.collapse();
17581             this.fireEvent('select', this, record, index);
17582         }
17583     },
17584
17585     /**
17586      * Returns the currently selected field value or empty string if no value is set.
17587      * @return {String} value The selected value
17588      */
17589     getValue : function()
17590     {
17591         if(Roo.isIOS && this.useNativeIOS){
17592             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17593         }
17594         
17595         if(this.multiple){
17596             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17597         }
17598         
17599         if(this.valueField){
17600             return typeof this.value != 'undefined' ? this.value : '';
17601         }else{
17602             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17603         }
17604     },
17605     
17606     getRawValue : function()
17607     {
17608         if(Roo.isIOS && this.useNativeIOS){
17609             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17610         }
17611         
17612         var v = this.inputEl().getValue();
17613         
17614         return v;
17615     },
17616
17617     /**
17618      * Clears any text/value currently set in the field
17619      */
17620     clearValue : function(){
17621         
17622         if(this.hiddenField){
17623             this.hiddenField.dom.value = '';
17624         }
17625         this.value = '';
17626         this.setRawValue('');
17627         this.lastSelectionText = '';
17628         this.lastData = false;
17629         
17630         var close = this.closeTriggerEl();
17631         
17632         if(close){
17633             close.hide();
17634         }
17635         
17636         this.validate();
17637         
17638     },
17639
17640     /**
17641      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17642      * will be displayed in the field.  If the value does not match the data value of an existing item,
17643      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17644      * Otherwise the field will be blank (although the value will still be set).
17645      * @param {String} value The value to match
17646      */
17647     setValue : function(v)
17648     {
17649         if(Roo.isIOS && this.useNativeIOS){
17650             this.setIOSValue(v);
17651             return;
17652         }
17653         
17654         if(this.multiple){
17655             this.syncValue();
17656             return;
17657         }
17658         
17659         var text = v;
17660         if(this.valueField){
17661             var r = this.findRecord(this.valueField, v);
17662             if(r){
17663                 text = r.data[this.displayField];
17664             }else if(this.valueNotFoundText !== undefined){
17665                 text = this.valueNotFoundText;
17666             }
17667         }
17668         this.lastSelectionText = text;
17669         if(this.hiddenField){
17670             this.hiddenField.dom.value = v;
17671         }
17672         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17673         this.value = v;
17674         
17675         var close = this.closeTriggerEl();
17676         
17677         if(close){
17678             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17679         }
17680         
17681         this.validate();
17682     },
17683     /**
17684      * @property {Object} the last set data for the element
17685      */
17686     
17687     lastData : false,
17688     /**
17689      * Sets the value of the field based on a object which is related to the record format for the store.
17690      * @param {Object} value the value to set as. or false on reset?
17691      */
17692     setFromData : function(o){
17693         
17694         if(this.multiple){
17695             this.addItem(o);
17696             return;
17697         }
17698             
17699         var dv = ''; // display value
17700         var vv = ''; // value value..
17701         this.lastData = o;
17702         if (this.displayField) {
17703             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17704         } else {
17705             // this is an error condition!!!
17706             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17707         }
17708         
17709         if(this.valueField){
17710             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17711         }
17712         
17713         var close = this.closeTriggerEl();
17714         
17715         if(close){
17716             if(dv.length || vv * 1 > 0){
17717                 close.show() ;
17718                 this.blockFocus=true;
17719             } else {
17720                 close.hide();
17721             }             
17722         }
17723         
17724         if(this.hiddenField){
17725             this.hiddenField.dom.value = vv;
17726             
17727             this.lastSelectionText = dv;
17728             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17729             this.value = vv;
17730             return;
17731         }
17732         // no hidden field.. - we store the value in 'value', but still display
17733         // display field!!!!
17734         this.lastSelectionText = dv;
17735         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17736         this.value = vv;
17737         
17738         
17739         
17740     },
17741     // private
17742     reset : function(){
17743         // overridden so that last data is reset..
17744         
17745         if(this.multiple){
17746             this.clearItem();
17747             return;
17748         }
17749         
17750         this.setValue(this.originalValue);
17751         //this.clearInvalid();
17752         this.lastData = false;
17753         if (this.view) {
17754             this.view.clearSelections();
17755         }
17756         
17757         this.validate();
17758     },
17759     // private
17760     findRecord : function(prop, value){
17761         var record;
17762         if(this.store.getCount() > 0){
17763             this.store.each(function(r){
17764                 if(r.data[prop] == value){
17765                     record = r;
17766                     return false;
17767                 }
17768                 return true;
17769             });
17770         }
17771         return record;
17772     },
17773     
17774     getName: function()
17775     {
17776         // returns hidden if it's set..
17777         if (!this.rendered) {return ''};
17778         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17779         
17780     },
17781     // private
17782     onViewMove : function(e, t){
17783         this.inKeyMode = false;
17784     },
17785
17786     // private
17787     onViewOver : function(e, t){
17788         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17789             return;
17790         }
17791         var item = this.view.findItemFromChild(t);
17792         
17793         if(item){
17794             var index = this.view.indexOf(item);
17795             this.select(index, false);
17796         }
17797     },
17798
17799     // private
17800     onViewClick : function(view, doFocus, el, e)
17801     {
17802         var index = this.view.getSelectedIndexes()[0];
17803         
17804         var r = this.store.getAt(index);
17805         
17806         if(this.tickable){
17807             
17808             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17809                 return;
17810             }
17811             
17812             var rm = false;
17813             var _this = this;
17814             
17815             Roo.each(this.tickItems, function(v,k){
17816                 
17817                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17818                     Roo.log(v);
17819                     _this.tickItems.splice(k, 1);
17820                     
17821                     if(typeof(e) == 'undefined' && view == false){
17822                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17823                     }
17824                     
17825                     rm = true;
17826                     return;
17827                 }
17828             });
17829             
17830             if(rm){
17831                 return;
17832             }
17833             
17834             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17835                 this.tickItems.push(r.data);
17836             }
17837             
17838             if(typeof(e) == 'undefined' && view == false){
17839                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17840             }
17841                     
17842             return;
17843         }
17844         
17845         if(r){
17846             this.onSelect(r, index);
17847         }
17848         if(doFocus !== false && !this.blockFocus){
17849             this.inputEl().focus();
17850         }
17851     },
17852
17853     // private
17854     restrictHeight : function(){
17855         //this.innerList.dom.style.height = '';
17856         //var inner = this.innerList.dom;
17857         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17858         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17859         //this.list.beginUpdate();
17860         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17861         this.list.alignTo(this.inputEl(), this.listAlign);
17862         this.list.alignTo(this.inputEl(), this.listAlign);
17863         //this.list.endUpdate();
17864     },
17865
17866     // private
17867     onEmptyResults : function(){
17868         
17869         if(this.tickable && this.editable){
17870             this.hasFocus = false;
17871             this.restrictHeight();
17872             return;
17873         }
17874         
17875         this.collapse();
17876     },
17877
17878     /**
17879      * Returns true if the dropdown list is expanded, else false.
17880      */
17881     isExpanded : function(){
17882         return this.list.isVisible();
17883     },
17884
17885     /**
17886      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17887      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17888      * @param {String} value The data value of the item to select
17889      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17890      * selected item if it is not currently in view (defaults to true)
17891      * @return {Boolean} True if the value matched an item in the list, else false
17892      */
17893     selectByValue : function(v, scrollIntoView){
17894         if(v !== undefined && v !== null){
17895             var r = this.findRecord(this.valueField || this.displayField, v);
17896             if(r){
17897                 this.select(this.store.indexOf(r), scrollIntoView);
17898                 return true;
17899             }
17900         }
17901         return false;
17902     },
17903
17904     /**
17905      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17906      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17907      * @param {Number} index The zero-based index of the list item to select
17908      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17909      * selected item if it is not currently in view (defaults to true)
17910      */
17911     select : function(index, scrollIntoView){
17912         this.selectedIndex = index;
17913         this.view.select(index);
17914         if(scrollIntoView !== false){
17915             var el = this.view.getNode(index);
17916             /*
17917              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17918              */
17919             if(el){
17920                 this.list.scrollChildIntoView(el, false);
17921             }
17922         }
17923     },
17924
17925     // private
17926     selectNext : function(){
17927         var ct = this.store.getCount();
17928         if(ct > 0){
17929             if(this.selectedIndex == -1){
17930                 this.select(0);
17931             }else if(this.selectedIndex < ct-1){
17932                 this.select(this.selectedIndex+1);
17933             }
17934         }
17935     },
17936
17937     // private
17938     selectPrev : function(){
17939         var ct = this.store.getCount();
17940         if(ct > 0){
17941             if(this.selectedIndex == -1){
17942                 this.select(0);
17943             }else if(this.selectedIndex != 0){
17944                 this.select(this.selectedIndex-1);
17945             }
17946         }
17947     },
17948
17949     // private
17950     onKeyUp : function(e){
17951         if(this.editable !== false && !e.isSpecialKey()){
17952             this.lastKey = e.getKey();
17953             this.dqTask.delay(this.queryDelay);
17954         }
17955     },
17956
17957     // private
17958     validateBlur : function(){
17959         return !this.list || !this.list.isVisible();   
17960     },
17961
17962     // private
17963     initQuery : function(){
17964         
17965         var v = this.getRawValue();
17966         
17967         if(this.tickable && this.editable){
17968             v = this.tickableInputEl().getValue();
17969         }
17970         
17971         this.doQuery(v);
17972     },
17973
17974     // private
17975     doForce : function(){
17976         if(this.inputEl().dom.value.length > 0){
17977             this.inputEl().dom.value =
17978                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17979              
17980         }
17981     },
17982
17983     /**
17984      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17985      * query allowing the query action to be canceled if needed.
17986      * @param {String} query The SQL query to execute
17987      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17988      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17989      * saved in the current store (defaults to false)
17990      */
17991     doQuery : function(q, forceAll){
17992         
17993         if(q === undefined || q === null){
17994             q = '';
17995         }
17996         var qe = {
17997             query: q,
17998             forceAll: forceAll,
17999             combo: this,
18000             cancel:false
18001         };
18002         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18003             return false;
18004         }
18005         q = qe.query;
18006         
18007         forceAll = qe.forceAll;
18008         if(forceAll === true || (q.length >= this.minChars)){
18009             
18010             this.hasQuery = true;
18011             
18012             if(this.lastQuery != q || this.alwaysQuery){
18013                 this.lastQuery = q;
18014                 if(this.mode == 'local'){
18015                     this.selectedIndex = -1;
18016                     if(forceAll){
18017                         this.store.clearFilter();
18018                     }else{
18019                         
18020                         if(this.specialFilter){
18021                             this.fireEvent('specialfilter', this);
18022                             this.onLoad();
18023                             return;
18024                         }
18025                         
18026                         this.store.filter(this.displayField, q);
18027                     }
18028                     
18029                     this.store.fireEvent("datachanged", this.store);
18030                     
18031                     this.onLoad();
18032                     
18033                     
18034                 }else{
18035                     
18036                     this.store.baseParams[this.queryParam] = q;
18037                     
18038                     var options = {params : this.getParams(q)};
18039                     
18040                     if(this.loadNext){
18041                         options.add = true;
18042                         options.params.start = this.page * this.pageSize;
18043                     }
18044                     
18045                     this.store.load(options);
18046                     
18047                     /*
18048                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18049                      *  we should expand the list on onLoad
18050                      *  so command out it
18051                      */
18052 //                    this.expand();
18053                 }
18054             }else{
18055                 this.selectedIndex = -1;
18056                 this.onLoad();   
18057             }
18058         }
18059         
18060         this.loadNext = false;
18061     },
18062     
18063     // private
18064     getParams : function(q){
18065         var p = {};
18066         //p[this.queryParam] = q;
18067         
18068         if(this.pageSize){
18069             p.start = 0;
18070             p.limit = this.pageSize;
18071         }
18072         return p;
18073     },
18074
18075     /**
18076      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18077      */
18078     collapse : function(){
18079         if(!this.isExpanded()){
18080             return;
18081         }
18082         
18083         this.list.hide();
18084         
18085         this.hasFocus = false;
18086         
18087         if(this.tickable){
18088             this.okBtn.hide();
18089             this.cancelBtn.hide();
18090             this.trigger.show();
18091             
18092             if(this.editable){
18093                 this.tickableInputEl().dom.value = '';
18094                 this.tickableInputEl().blur();
18095             }
18096             
18097         }
18098         
18099         Roo.get(document).un('mousedown', this.collapseIf, this);
18100         Roo.get(document).un('mousewheel', this.collapseIf, this);
18101         if (!this.editable) {
18102             Roo.get(document).un('keydown', this.listKeyPress, this);
18103         }
18104         this.fireEvent('collapse', this);
18105         
18106         this.validate();
18107     },
18108
18109     // private
18110     collapseIf : function(e){
18111         var in_combo  = e.within(this.el);
18112         var in_list =  e.within(this.list);
18113         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18114         
18115         if (in_combo || in_list || is_list) {
18116             //e.stopPropagation();
18117             return;
18118         }
18119         
18120         if(this.tickable){
18121             this.onTickableFooterButtonClick(e, false, false);
18122         }
18123
18124         this.collapse();
18125         
18126     },
18127
18128     /**
18129      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18130      */
18131     expand : function(){
18132        
18133         if(this.isExpanded() || !this.hasFocus){
18134             return;
18135         }
18136         
18137         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18138         this.list.setWidth(lw);
18139         
18140         Roo.log('expand');
18141         
18142         this.list.show();
18143         
18144         this.restrictHeight();
18145         
18146         if(this.tickable){
18147             
18148             this.tickItems = Roo.apply([], this.item);
18149             
18150             this.okBtn.show();
18151             this.cancelBtn.show();
18152             this.trigger.hide();
18153             
18154             if(this.editable){
18155                 this.tickableInputEl().focus();
18156             }
18157             
18158         }
18159         
18160         Roo.get(document).on('mousedown', this.collapseIf, this);
18161         Roo.get(document).on('mousewheel', this.collapseIf, this);
18162         if (!this.editable) {
18163             Roo.get(document).on('keydown', this.listKeyPress, this);
18164         }
18165         
18166         this.fireEvent('expand', this);
18167     },
18168
18169     // private
18170     // Implements the default empty TriggerField.onTriggerClick function
18171     onTriggerClick : function(e)
18172     {
18173         Roo.log('trigger click');
18174         
18175         if(this.disabled || !this.triggerList){
18176             return;
18177         }
18178         
18179         this.page = 0;
18180         this.loadNext = false;
18181         
18182         if(this.isExpanded()){
18183             this.collapse();
18184             if (!this.blockFocus) {
18185                 this.inputEl().focus();
18186             }
18187             
18188         }else {
18189             this.hasFocus = true;
18190             if(this.triggerAction == 'all') {
18191                 this.doQuery(this.allQuery, true);
18192             } else {
18193                 this.doQuery(this.getRawValue());
18194             }
18195             if (!this.blockFocus) {
18196                 this.inputEl().focus();
18197             }
18198         }
18199     },
18200     
18201     onTickableTriggerClick : function(e)
18202     {
18203         if(this.disabled){
18204             return;
18205         }
18206         
18207         this.page = 0;
18208         this.loadNext = false;
18209         this.hasFocus = true;
18210         
18211         if(this.triggerAction == 'all') {
18212             this.doQuery(this.allQuery, true);
18213         } else {
18214             this.doQuery(this.getRawValue());
18215         }
18216     },
18217     
18218     onSearchFieldClick : function(e)
18219     {
18220         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18221             this.onTickableFooterButtonClick(e, false, false);
18222             return;
18223         }
18224         
18225         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18226             return;
18227         }
18228         
18229         this.page = 0;
18230         this.loadNext = false;
18231         this.hasFocus = true;
18232         
18233         if(this.triggerAction == 'all') {
18234             this.doQuery(this.allQuery, true);
18235         } else {
18236             this.doQuery(this.getRawValue());
18237         }
18238     },
18239     
18240     listKeyPress : function(e)
18241     {
18242         //Roo.log('listkeypress');
18243         // scroll to first matching element based on key pres..
18244         if (e.isSpecialKey()) {
18245             return false;
18246         }
18247         var k = String.fromCharCode(e.getKey()).toUpperCase();
18248         //Roo.log(k);
18249         var match  = false;
18250         var csel = this.view.getSelectedNodes();
18251         var cselitem = false;
18252         if (csel.length) {
18253             var ix = this.view.indexOf(csel[0]);
18254             cselitem  = this.store.getAt(ix);
18255             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18256                 cselitem = false;
18257             }
18258             
18259         }
18260         
18261         this.store.each(function(v) { 
18262             if (cselitem) {
18263                 // start at existing selection.
18264                 if (cselitem.id == v.id) {
18265                     cselitem = false;
18266                 }
18267                 return true;
18268             }
18269                 
18270             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18271                 match = this.store.indexOf(v);
18272                 return false;
18273             }
18274             return true;
18275         }, this);
18276         
18277         if (match === false) {
18278             return true; // no more action?
18279         }
18280         // scroll to?
18281         this.view.select(match);
18282         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18283         sn.scrollIntoView(sn.dom.parentNode, false);
18284     },
18285     
18286     onViewScroll : function(e, t){
18287         
18288         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){
18289             return;
18290         }
18291         
18292         this.hasQuery = true;
18293         
18294         this.loading = this.list.select('.loading', true).first();
18295         
18296         if(this.loading === null){
18297             this.list.createChild({
18298                 tag: 'div',
18299                 cls: 'loading roo-select2-more-results roo-select2-active',
18300                 html: 'Loading more results...'
18301             });
18302             
18303             this.loading = this.list.select('.loading', true).first();
18304             
18305             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18306             
18307             this.loading.hide();
18308         }
18309         
18310         this.loading.show();
18311         
18312         var _combo = this;
18313         
18314         this.page++;
18315         this.loadNext = true;
18316         
18317         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18318         
18319         return;
18320     },
18321     
18322     addItem : function(o)
18323     {   
18324         var dv = ''; // display value
18325         
18326         if (this.displayField) {
18327             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18328         } else {
18329             // this is an error condition!!!
18330             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18331         }
18332         
18333         if(!dv.length){
18334             return;
18335         }
18336         
18337         var choice = this.choices.createChild({
18338             tag: 'li',
18339             cls: 'roo-select2-search-choice',
18340             cn: [
18341                 {
18342                     tag: 'div',
18343                     html: dv
18344                 },
18345                 {
18346                     tag: 'a',
18347                     href: '#',
18348                     cls: 'roo-select2-search-choice-close fa fa-times',
18349                     tabindex: '-1'
18350                 }
18351             ]
18352             
18353         }, this.searchField);
18354         
18355         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18356         
18357         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18358         
18359         this.item.push(o);
18360         
18361         this.lastData = o;
18362         
18363         this.syncValue();
18364         
18365         this.inputEl().dom.value = '';
18366         
18367         this.validate();
18368     },
18369     
18370     onRemoveItem : function(e, _self, o)
18371     {
18372         e.preventDefault();
18373         
18374         this.lastItem = Roo.apply([], this.item);
18375         
18376         var index = this.item.indexOf(o.data) * 1;
18377         
18378         if( index < 0){
18379             Roo.log('not this item?!');
18380             return;
18381         }
18382         
18383         this.item.splice(index, 1);
18384         o.item.remove();
18385         
18386         this.syncValue();
18387         
18388         this.fireEvent('remove', this, e);
18389         
18390         this.validate();
18391         
18392     },
18393     
18394     syncValue : function()
18395     {
18396         if(!this.item.length){
18397             this.clearValue();
18398             return;
18399         }
18400             
18401         var value = [];
18402         var _this = this;
18403         Roo.each(this.item, function(i){
18404             if(_this.valueField){
18405                 value.push(i[_this.valueField]);
18406                 return;
18407             }
18408
18409             value.push(i);
18410         });
18411
18412         this.value = value.join(',');
18413
18414         if(this.hiddenField){
18415             this.hiddenField.dom.value = this.value;
18416         }
18417         
18418         this.store.fireEvent("datachanged", this.store);
18419         
18420         this.validate();
18421     },
18422     
18423     clearItem : function()
18424     {
18425         if(!this.multiple){
18426             return;
18427         }
18428         
18429         this.item = [];
18430         
18431         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18432            c.remove();
18433         });
18434         
18435         this.syncValue();
18436         
18437         this.validate();
18438         
18439         if(this.tickable && !Roo.isTouch){
18440             this.view.refresh();
18441         }
18442     },
18443     
18444     inputEl: function ()
18445     {
18446         if(Roo.isIOS && this.useNativeIOS){
18447             return this.el.select('select.roo-ios-select', true).first();
18448         }
18449         
18450         if(Roo.isTouch && this.mobileTouchView){
18451             return this.el.select('input.form-control',true).first();
18452         }
18453         
18454         if(this.tickable){
18455             return this.searchField;
18456         }
18457         
18458         return this.el.select('input.form-control',true).first();
18459     },
18460     
18461     onTickableFooterButtonClick : function(e, btn, el)
18462     {
18463         e.preventDefault();
18464         
18465         this.lastItem = Roo.apply([], this.item);
18466         
18467         if(btn && btn.name == 'cancel'){
18468             this.tickItems = Roo.apply([], this.item);
18469             this.collapse();
18470             return;
18471         }
18472         
18473         this.clearItem();
18474         
18475         var _this = this;
18476         
18477         Roo.each(this.tickItems, function(o){
18478             _this.addItem(o);
18479         });
18480         
18481         this.collapse();
18482         
18483     },
18484     
18485     validate : function()
18486     {
18487         if(this.getVisibilityEl().hasClass('hidden')){
18488             return true;
18489         }
18490         
18491         var v = this.getRawValue();
18492         
18493         if(this.multiple){
18494             v = this.getValue();
18495         }
18496         
18497         if(this.disabled || this.allowBlank || v.length){
18498             this.markValid();
18499             return true;
18500         }
18501         
18502         this.markInvalid();
18503         return false;
18504     },
18505     
18506     tickableInputEl : function()
18507     {
18508         if(!this.tickable || !this.editable){
18509             return this.inputEl();
18510         }
18511         
18512         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18513     },
18514     
18515     
18516     getAutoCreateTouchView : function()
18517     {
18518         var id = Roo.id();
18519         
18520         var cfg = {
18521             cls: 'form-group' //input-group
18522         };
18523         
18524         var input =  {
18525             tag: 'input',
18526             id : id,
18527             type : this.inputType,
18528             cls : 'form-control x-combo-noedit',
18529             autocomplete: 'new-password',
18530             placeholder : this.placeholder || '',
18531             readonly : true
18532         };
18533         
18534         if (this.name) {
18535             input.name = this.name;
18536         }
18537         
18538         if (this.size) {
18539             input.cls += ' input-' + this.size;
18540         }
18541         
18542         if (this.disabled) {
18543             input.disabled = true;
18544         }
18545         
18546         var inputblock = {
18547             cls : 'roo-combobox-wrap',
18548             cn : [
18549                 input
18550             ]
18551         };
18552         
18553         if(this.before){
18554             inputblock.cls += ' input-group';
18555             
18556             inputblock.cn.unshift({
18557                 tag :'span',
18558                 cls : 'input-group-addon input-group-prepend input-group-text',
18559                 html : this.before
18560             });
18561         }
18562         
18563         if(this.removable && !this.multiple){
18564             inputblock.cls += ' roo-removable';
18565             
18566             inputblock.cn.push({
18567                 tag: 'button',
18568                 html : 'x',
18569                 cls : 'roo-combo-removable-btn close'
18570             });
18571         }
18572
18573         if(this.hasFeedback && !this.allowBlank){
18574             
18575             inputblock.cls += ' has-feedback';
18576             
18577             inputblock.cn.push({
18578                 tag: 'span',
18579                 cls: 'glyphicon form-control-feedback'
18580             });
18581             
18582         }
18583         
18584         if (this.after) {
18585             
18586             inputblock.cls += (this.before) ? '' : ' input-group';
18587             
18588             inputblock.cn.push({
18589                 tag :'span',
18590                 cls : 'input-group-addon input-group-append input-group-text',
18591                 html : this.after
18592             });
18593         }
18594
18595         
18596         var ibwrap = inputblock;
18597         
18598         if(this.multiple){
18599             ibwrap = {
18600                 tag: 'ul',
18601                 cls: 'roo-select2-choices',
18602                 cn:[
18603                     {
18604                         tag: 'li',
18605                         cls: 'roo-select2-search-field',
18606                         cn: [
18607
18608                             inputblock
18609                         ]
18610                     }
18611                 ]
18612             };
18613         
18614             
18615         }
18616         
18617         var combobox = {
18618             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18619             cn: [
18620                 {
18621                     tag: 'input',
18622                     type : 'hidden',
18623                     cls: 'form-hidden-field'
18624                 },
18625                 ibwrap
18626             ]
18627         };
18628         
18629         if(!this.multiple && this.showToggleBtn){
18630             
18631             var caret = {
18632                 cls: 'caret'
18633             };
18634             
18635             if (this.caret != false) {
18636                 caret = {
18637                      tag: 'i',
18638                      cls: 'fa fa-' + this.caret
18639                 };
18640                 
18641             }
18642             
18643             combobox.cn.push({
18644                 tag :'span',
18645                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18646                 cn : [
18647                     Roo.bootstrap.version == 3 ? caret : '',
18648                     {
18649                         tag: 'span',
18650                         cls: 'combobox-clear',
18651                         cn  : [
18652                             {
18653                                 tag : 'i',
18654                                 cls: 'icon-remove'
18655                             }
18656                         ]
18657                     }
18658                 ]
18659
18660             })
18661         }
18662         
18663         if(this.multiple){
18664             combobox.cls += ' roo-select2-container-multi';
18665         }
18666         
18667         var required =  this.allowBlank ?  {
18668                     tag : 'i',
18669                     style: 'display: none'
18670                 } : {
18671                    tag : 'i',
18672                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18673                    tooltip : 'This field is required'
18674                 };
18675         
18676         var align = this.labelAlign || this.parentLabelAlign();
18677         
18678         if (align ==='left' && this.fieldLabel.length) {
18679
18680             cfg.cn = [
18681                 required,
18682                 {
18683                     tag: 'label',
18684                     cls : 'control-label col-form-label',
18685                     html : this.fieldLabel
18686
18687                 },
18688                 {
18689                     cls : 'roo-combobox-wrap ', 
18690                     cn: [
18691                         combobox
18692                     ]
18693                 }
18694             ];
18695             
18696             var labelCfg = cfg.cn[1];
18697             var contentCfg = cfg.cn[2];
18698             
18699
18700             if(this.indicatorpos == 'right'){
18701                 cfg.cn = [
18702                     {
18703                         tag: 'label',
18704                         'for' :  id,
18705                         cls : 'control-label col-form-label',
18706                         cn : [
18707                             {
18708                                 tag : 'span',
18709                                 html : this.fieldLabel
18710                             },
18711                             required
18712                         ]
18713                     },
18714                     {
18715                         cls : "roo-combobox-wrap ",
18716                         cn: [
18717                             combobox
18718                         ]
18719                     }
18720
18721                 ];
18722                 
18723                 labelCfg = cfg.cn[0];
18724                 contentCfg = cfg.cn[1];
18725             }
18726             
18727            
18728             
18729             if(this.labelWidth > 12){
18730                 labelCfg.style = "width: " + this.labelWidth + 'px';
18731             }
18732            
18733             if(this.labelWidth < 13 && this.labelmd == 0){
18734                 this.labelmd = this.labelWidth;
18735             }
18736             
18737             if(this.labellg > 0){
18738                 labelCfg.cls += ' col-lg-' + this.labellg;
18739                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18740             }
18741             
18742             if(this.labelmd > 0){
18743                 labelCfg.cls += ' col-md-' + this.labelmd;
18744                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18745             }
18746             
18747             if(this.labelsm > 0){
18748                 labelCfg.cls += ' col-sm-' + this.labelsm;
18749                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18750             }
18751             
18752             if(this.labelxs > 0){
18753                 labelCfg.cls += ' col-xs-' + this.labelxs;
18754                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18755             }
18756                 
18757                 
18758         } else if ( this.fieldLabel.length) {
18759             cfg.cn = [
18760                required,
18761                 {
18762                     tag: 'label',
18763                     cls : 'control-label',
18764                     html : this.fieldLabel
18765
18766                 },
18767                 {
18768                     cls : '', 
18769                     cn: [
18770                         combobox
18771                     ]
18772                 }
18773             ];
18774             
18775             if(this.indicatorpos == 'right'){
18776                 cfg.cn = [
18777                     {
18778                         tag: 'label',
18779                         cls : 'control-label',
18780                         html : this.fieldLabel,
18781                         cn : [
18782                             required
18783                         ]
18784                     },
18785                     {
18786                         cls : '', 
18787                         cn: [
18788                             combobox
18789                         ]
18790                     }
18791                 ];
18792             }
18793         } else {
18794             cfg.cn = combobox;    
18795         }
18796         
18797         
18798         var settings = this;
18799         
18800         ['xs','sm','md','lg'].map(function(size){
18801             if (settings[size]) {
18802                 cfg.cls += ' col-' + size + '-' + settings[size];
18803             }
18804         });
18805         
18806         return cfg;
18807     },
18808     
18809     initTouchView : function()
18810     {
18811         this.renderTouchView();
18812         
18813         this.touchViewEl.on('scroll', function(){
18814             this.el.dom.scrollTop = 0;
18815         }, this);
18816         
18817         this.originalValue = this.getValue();
18818         
18819         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18820         
18821         this.inputEl().on("click", this.showTouchView, this);
18822         if (this.triggerEl) {
18823             this.triggerEl.on("click", this.showTouchView, this);
18824         }
18825         
18826         
18827         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18828         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18829         
18830         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18831         
18832         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18833         this.store.on('load', this.onTouchViewLoad, this);
18834         this.store.on('loadexception', this.onTouchViewLoadException, this);
18835         
18836         if(this.hiddenName){
18837             
18838             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18839             
18840             this.hiddenField.dom.value =
18841                 this.hiddenValue !== undefined ? this.hiddenValue :
18842                 this.value !== undefined ? this.value : '';
18843         
18844             this.el.dom.removeAttribute('name');
18845             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18846         }
18847         
18848         if(this.multiple){
18849             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18850             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18851         }
18852         
18853         if(this.removable && !this.multiple){
18854             var close = this.closeTriggerEl();
18855             if(close){
18856                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18857                 close.on('click', this.removeBtnClick, this, close);
18858             }
18859         }
18860         /*
18861          * fix the bug in Safari iOS8
18862          */
18863         this.inputEl().on("focus", function(e){
18864             document.activeElement.blur();
18865         }, this);
18866         
18867         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18868         
18869         return;
18870         
18871         
18872     },
18873     
18874     renderTouchView : function()
18875     {
18876         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18877         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18878         
18879         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18880         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18881         
18882         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18883         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18884         this.touchViewBodyEl.setStyle('overflow', 'auto');
18885         
18886         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18887         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18888         
18889         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18890         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18891         
18892     },
18893     
18894     showTouchView : function()
18895     {
18896         if(this.disabled){
18897             return;
18898         }
18899         
18900         this.touchViewHeaderEl.hide();
18901
18902         if(this.modalTitle.length){
18903             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18904             this.touchViewHeaderEl.show();
18905         }
18906
18907         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18908         this.touchViewEl.show();
18909
18910         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18911         
18912         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18913         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18914
18915         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18916
18917         if(this.modalTitle.length){
18918             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18919         }
18920         
18921         this.touchViewBodyEl.setHeight(bodyHeight);
18922
18923         if(this.animate){
18924             var _this = this;
18925             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18926         }else{
18927             this.touchViewEl.addClass(['in','show']);
18928         }
18929         
18930         if(this._touchViewMask){
18931             Roo.get(document.body).addClass("x-body-masked");
18932             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18933             this._touchViewMask.setStyle('z-index', 10000);
18934             this._touchViewMask.addClass('show');
18935         }
18936         
18937         this.doTouchViewQuery();
18938         
18939     },
18940     
18941     hideTouchView : function()
18942     {
18943         this.touchViewEl.removeClass(['in','show']);
18944
18945         if(this.animate){
18946             var _this = this;
18947             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18948         }else{
18949             this.touchViewEl.setStyle('display', 'none');
18950         }
18951         
18952         if(this._touchViewMask){
18953             this._touchViewMask.removeClass('show');
18954             Roo.get(document.body).removeClass("x-body-masked");
18955         }
18956     },
18957     
18958     setTouchViewValue : function()
18959     {
18960         if(this.multiple){
18961             this.clearItem();
18962         
18963             var _this = this;
18964
18965             Roo.each(this.tickItems, function(o){
18966                 this.addItem(o);
18967             }, this);
18968         }
18969         
18970         this.hideTouchView();
18971     },
18972     
18973     doTouchViewQuery : function()
18974     {
18975         var qe = {
18976             query: '',
18977             forceAll: true,
18978             combo: this,
18979             cancel:false
18980         };
18981         
18982         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18983             return false;
18984         }
18985         
18986         if(!this.alwaysQuery || this.mode == 'local'){
18987             this.onTouchViewLoad();
18988             return;
18989         }
18990         
18991         this.store.load();
18992     },
18993     
18994     onTouchViewBeforeLoad : function(combo,opts)
18995     {
18996         return;
18997     },
18998
18999     // private
19000     onTouchViewLoad : function()
19001     {
19002         if(this.store.getCount() < 1){
19003             this.onTouchViewEmptyResults();
19004             return;
19005         }
19006         
19007         this.clearTouchView();
19008         
19009         var rawValue = this.getRawValue();
19010         
19011         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19012         
19013         this.tickItems = [];
19014         
19015         this.store.data.each(function(d, rowIndex){
19016             var row = this.touchViewListGroup.createChild(template);
19017             
19018             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19019                 row.addClass(d.data.cls);
19020             }
19021             
19022             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19023                 var cfg = {
19024                     data : d.data,
19025                     html : d.data[this.displayField]
19026                 };
19027                 
19028                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19029                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19030                 }
19031             }
19032             row.removeClass('selected');
19033             if(!this.multiple && this.valueField &&
19034                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19035             {
19036                 // radio buttons..
19037                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19038                 row.addClass('selected');
19039             }
19040             
19041             if(this.multiple && this.valueField &&
19042                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19043             {
19044                 
19045                 // checkboxes...
19046                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19047                 this.tickItems.push(d.data);
19048             }
19049             
19050             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19051             
19052         }, this);
19053         
19054         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19055         
19056         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19057
19058         if(this.modalTitle.length){
19059             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19060         }
19061
19062         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19063         
19064         if(this.mobile_restrict_height && listHeight < bodyHeight){
19065             this.touchViewBodyEl.setHeight(listHeight);
19066         }
19067         
19068         var _this = this;
19069         
19070         if(firstChecked && listHeight > bodyHeight){
19071             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19072         }
19073         
19074     },
19075     
19076     onTouchViewLoadException : function()
19077     {
19078         this.hideTouchView();
19079     },
19080     
19081     onTouchViewEmptyResults : function()
19082     {
19083         this.clearTouchView();
19084         
19085         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19086         
19087         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19088         
19089     },
19090     
19091     clearTouchView : function()
19092     {
19093         this.touchViewListGroup.dom.innerHTML = '';
19094     },
19095     
19096     onTouchViewClick : function(e, el, o)
19097     {
19098         e.preventDefault();
19099         
19100         var row = o.row;
19101         var rowIndex = o.rowIndex;
19102         
19103         var r = this.store.getAt(rowIndex);
19104         
19105         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19106             
19107             if(!this.multiple){
19108                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19109                     c.dom.removeAttribute('checked');
19110                 }, this);
19111
19112                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19113
19114                 this.setFromData(r.data);
19115
19116                 var close = this.closeTriggerEl();
19117
19118                 if(close){
19119                     close.show();
19120                 }
19121
19122                 this.hideTouchView();
19123
19124                 this.fireEvent('select', this, r, rowIndex);
19125
19126                 return;
19127             }
19128
19129             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19130                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19131                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19132                 return;
19133             }
19134
19135             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19136             this.addItem(r.data);
19137             this.tickItems.push(r.data);
19138         }
19139     },
19140     
19141     getAutoCreateNativeIOS : function()
19142     {
19143         var cfg = {
19144             cls: 'form-group' //input-group,
19145         };
19146         
19147         var combobox =  {
19148             tag: 'select',
19149             cls : 'roo-ios-select'
19150         };
19151         
19152         if (this.name) {
19153             combobox.name = this.name;
19154         }
19155         
19156         if (this.disabled) {
19157             combobox.disabled = true;
19158         }
19159         
19160         var settings = this;
19161         
19162         ['xs','sm','md','lg'].map(function(size){
19163             if (settings[size]) {
19164                 cfg.cls += ' col-' + size + '-' + settings[size];
19165             }
19166         });
19167         
19168         cfg.cn = combobox;
19169         
19170         return cfg;
19171         
19172     },
19173     
19174     initIOSView : function()
19175     {
19176         this.store.on('load', this.onIOSViewLoad, this);
19177         
19178         return;
19179     },
19180     
19181     onIOSViewLoad : function()
19182     {
19183         if(this.store.getCount() < 1){
19184             return;
19185         }
19186         
19187         this.clearIOSView();
19188         
19189         if(this.allowBlank) {
19190             
19191             var default_text = '-- SELECT --';
19192             
19193             if(this.placeholder.length){
19194                 default_text = this.placeholder;
19195             }
19196             
19197             if(this.emptyTitle.length){
19198                 default_text += ' - ' + this.emptyTitle + ' -';
19199             }
19200             
19201             var opt = this.inputEl().createChild({
19202                 tag: 'option',
19203                 value : 0,
19204                 html : default_text
19205             });
19206             
19207             var o = {};
19208             o[this.valueField] = 0;
19209             o[this.displayField] = default_text;
19210             
19211             this.ios_options.push({
19212                 data : o,
19213                 el : opt
19214             });
19215             
19216         }
19217         
19218         this.store.data.each(function(d, rowIndex){
19219             
19220             var html = '';
19221             
19222             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19223                 html = d.data[this.displayField];
19224             }
19225             
19226             var value = '';
19227             
19228             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19229                 value = d.data[this.valueField];
19230             }
19231             
19232             var option = {
19233                 tag: 'option',
19234                 value : value,
19235                 html : html
19236             };
19237             
19238             if(this.value == d.data[this.valueField]){
19239                 option['selected'] = true;
19240             }
19241             
19242             var opt = this.inputEl().createChild(option);
19243             
19244             this.ios_options.push({
19245                 data : d.data,
19246                 el : opt
19247             });
19248             
19249         }, this);
19250         
19251         this.inputEl().on('change', function(){
19252            this.fireEvent('select', this);
19253         }, this);
19254         
19255     },
19256     
19257     clearIOSView: function()
19258     {
19259         this.inputEl().dom.innerHTML = '';
19260         
19261         this.ios_options = [];
19262     },
19263     
19264     setIOSValue: function(v)
19265     {
19266         this.value = v;
19267         
19268         if(!this.ios_options){
19269             return;
19270         }
19271         
19272         Roo.each(this.ios_options, function(opts){
19273            
19274            opts.el.dom.removeAttribute('selected');
19275            
19276            if(opts.data[this.valueField] != v){
19277                return;
19278            }
19279            
19280            opts.el.dom.setAttribute('selected', true);
19281            
19282         }, this);
19283     }
19284
19285     /** 
19286     * @cfg {Boolean} grow 
19287     * @hide 
19288     */
19289     /** 
19290     * @cfg {Number} growMin 
19291     * @hide 
19292     */
19293     /** 
19294     * @cfg {Number} growMax 
19295     * @hide 
19296     */
19297     /**
19298      * @hide
19299      * @method autoSize
19300      */
19301 });
19302
19303 Roo.apply(Roo.bootstrap.ComboBox,  {
19304     
19305     header : {
19306         tag: 'div',
19307         cls: 'modal-header',
19308         cn: [
19309             {
19310                 tag: 'h4',
19311                 cls: 'modal-title'
19312             }
19313         ]
19314     },
19315     
19316     body : {
19317         tag: 'div',
19318         cls: 'modal-body',
19319         cn: [
19320             {
19321                 tag: 'ul',
19322                 cls: 'list-group'
19323             }
19324         ]
19325     },
19326     
19327     listItemRadio : {
19328         tag: 'li',
19329         cls: 'list-group-item',
19330         cn: [
19331             {
19332                 tag: 'span',
19333                 cls: 'roo-combobox-list-group-item-value'
19334             },
19335             {
19336                 tag: 'div',
19337                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19338                 cn: [
19339                     {
19340                         tag: 'input',
19341                         type: 'radio'
19342                     },
19343                     {
19344                         tag: 'label'
19345                     }
19346                 ]
19347             }
19348         ]
19349     },
19350     
19351     listItemCheckbox : {
19352         tag: 'li',
19353         cls: 'list-group-item',
19354         cn: [
19355             {
19356                 tag: 'span',
19357                 cls: 'roo-combobox-list-group-item-value'
19358             },
19359             {
19360                 tag: 'div',
19361                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19362                 cn: [
19363                     {
19364                         tag: 'input',
19365                         type: 'checkbox'
19366                     },
19367                     {
19368                         tag: 'label'
19369                     }
19370                 ]
19371             }
19372         ]
19373     },
19374     
19375     emptyResult : {
19376         tag: 'div',
19377         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19378     },
19379     
19380     footer : {
19381         tag: 'div',
19382         cls: 'modal-footer',
19383         cn: [
19384             {
19385                 tag: 'div',
19386                 cls: 'row',
19387                 cn: [
19388                     {
19389                         tag: 'div',
19390                         cls: 'col-xs-6 text-left',
19391                         cn: {
19392                             tag: 'button',
19393                             cls: 'btn btn-danger roo-touch-view-cancel',
19394                             html: 'Cancel'
19395                         }
19396                     },
19397                     {
19398                         tag: 'div',
19399                         cls: 'col-xs-6 text-right',
19400                         cn: {
19401                             tag: 'button',
19402                             cls: 'btn btn-success roo-touch-view-ok',
19403                             html: 'OK'
19404                         }
19405                     }
19406                 ]
19407             }
19408         ]
19409         
19410     }
19411 });
19412
19413 Roo.apply(Roo.bootstrap.ComboBox,  {
19414     
19415     touchViewTemplate : {
19416         tag: 'div',
19417         cls: 'modal fade roo-combobox-touch-view',
19418         cn: [
19419             {
19420                 tag: 'div',
19421                 cls: 'modal-dialog',
19422                 style : 'position:fixed', // we have to fix position....
19423                 cn: [
19424                     {
19425                         tag: 'div',
19426                         cls: 'modal-content',
19427                         cn: [
19428                             Roo.bootstrap.ComboBox.header,
19429                             Roo.bootstrap.ComboBox.body,
19430                             Roo.bootstrap.ComboBox.footer
19431                         ]
19432                     }
19433                 ]
19434             }
19435         ]
19436     }
19437 });/*
19438  * Based on:
19439  * Ext JS Library 1.1.1
19440  * Copyright(c) 2006-2007, Ext JS, LLC.
19441  *
19442  * Originally Released Under LGPL - original licence link has changed is not relivant.
19443  *
19444  * Fork - LGPL
19445  * <script type="text/javascript">
19446  */
19447
19448 /**
19449  * @class Roo.View
19450  * @extends Roo.util.Observable
19451  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19452  * This class also supports single and multi selection modes. <br>
19453  * Create a data model bound view:
19454  <pre><code>
19455  var store = new Roo.data.Store(...);
19456
19457  var view = new Roo.View({
19458     el : "my-element",
19459     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19460  
19461     singleSelect: true,
19462     selectedClass: "ydataview-selected",
19463     store: store
19464  });
19465
19466  // listen for node click?
19467  view.on("click", function(vw, index, node, e){
19468  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19469  });
19470
19471  // load XML data
19472  dataModel.load("foobar.xml");
19473  </code></pre>
19474  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19475  * <br><br>
19476  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19477  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19478  * 
19479  * Note: old style constructor is still suported (container, template, config)
19480  * 
19481  * @constructor
19482  * Create a new View
19483  * @param {Object} config The config object
19484  * 
19485  */
19486 Roo.View = function(config, depreciated_tpl, depreciated_config){
19487     
19488     this.parent = false;
19489     
19490     if (typeof(depreciated_tpl) == 'undefined') {
19491         // new way.. - universal constructor.
19492         Roo.apply(this, config);
19493         this.el  = Roo.get(this.el);
19494     } else {
19495         // old format..
19496         this.el  = Roo.get(config);
19497         this.tpl = depreciated_tpl;
19498         Roo.apply(this, depreciated_config);
19499     }
19500     this.wrapEl  = this.el.wrap().wrap();
19501     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19502     
19503     
19504     if(typeof(this.tpl) == "string"){
19505         this.tpl = new Roo.Template(this.tpl);
19506     } else {
19507         // support xtype ctors..
19508         this.tpl = new Roo.factory(this.tpl, Roo);
19509     }
19510     
19511     
19512     this.tpl.compile();
19513     
19514     /** @private */
19515     this.addEvents({
19516         /**
19517          * @event beforeclick
19518          * Fires before a click is processed. Returns false to cancel the default action.
19519          * @param {Roo.View} this
19520          * @param {Number} index The index of the target node
19521          * @param {HTMLElement} node The target node
19522          * @param {Roo.EventObject} e The raw event object
19523          */
19524             "beforeclick" : true,
19525         /**
19526          * @event click
19527          * Fires when a template node is clicked.
19528          * @param {Roo.View} this
19529          * @param {Number} index The index of the target node
19530          * @param {HTMLElement} node The target node
19531          * @param {Roo.EventObject} e The raw event object
19532          */
19533             "click" : true,
19534         /**
19535          * @event dblclick
19536          * Fires when a template node is double clicked.
19537          * @param {Roo.View} this
19538          * @param {Number} index The index of the target node
19539          * @param {HTMLElement} node The target node
19540          * @param {Roo.EventObject} e The raw event object
19541          */
19542             "dblclick" : true,
19543         /**
19544          * @event contextmenu
19545          * Fires when a template node is right clicked.
19546          * @param {Roo.View} this
19547          * @param {Number} index The index of the target node
19548          * @param {HTMLElement} node The target node
19549          * @param {Roo.EventObject} e The raw event object
19550          */
19551             "contextmenu" : true,
19552         /**
19553          * @event selectionchange
19554          * Fires when the selected nodes change.
19555          * @param {Roo.View} this
19556          * @param {Array} selections Array of the selected nodes
19557          */
19558             "selectionchange" : true,
19559     
19560         /**
19561          * @event beforeselect
19562          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19563          * @param {Roo.View} this
19564          * @param {HTMLElement} node The node to be selected
19565          * @param {Array} selections Array of currently selected nodes
19566          */
19567             "beforeselect" : true,
19568         /**
19569          * @event preparedata
19570          * Fires on every row to render, to allow you to change the data.
19571          * @param {Roo.View} this
19572          * @param {Object} data to be rendered (change this)
19573          */
19574           "preparedata" : true
19575           
19576           
19577         });
19578
19579
19580
19581     this.el.on({
19582         "click": this.onClick,
19583         "dblclick": this.onDblClick,
19584         "contextmenu": this.onContextMenu,
19585         scope:this
19586     });
19587
19588     this.selections = [];
19589     this.nodes = [];
19590     this.cmp = new Roo.CompositeElementLite([]);
19591     if(this.store){
19592         this.store = Roo.factory(this.store, Roo.data);
19593         this.setStore(this.store, true);
19594     }
19595     
19596     if ( this.footer && this.footer.xtype) {
19597            
19598          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19599         
19600         this.footer.dataSource = this.store;
19601         this.footer.container = fctr;
19602         this.footer = Roo.factory(this.footer, Roo);
19603         fctr.insertFirst(this.el);
19604         
19605         // this is a bit insane - as the paging toolbar seems to detach the el..
19606 //        dom.parentNode.parentNode.parentNode
19607          // they get detached?
19608     }
19609     
19610     
19611     Roo.View.superclass.constructor.call(this);
19612     
19613     
19614 };
19615
19616 Roo.extend(Roo.View, Roo.util.Observable, {
19617     
19618      /**
19619      * @cfg {Roo.data.Store} store Data store to load data from.
19620      */
19621     store : false,
19622     
19623     /**
19624      * @cfg {String|Roo.Element} el The container element.
19625      */
19626     el : '',
19627     
19628     /**
19629      * @cfg {String|Roo.Template} tpl The template used by this View 
19630      */
19631     tpl : false,
19632     /**
19633      * @cfg {String} dataName the named area of the template to use as the data area
19634      *                          Works with domtemplates roo-name="name"
19635      */
19636     dataName: false,
19637     /**
19638      * @cfg {String} selectedClass The css class to add to selected nodes
19639      */
19640     selectedClass : "x-view-selected",
19641      /**
19642      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19643      */
19644     emptyText : "",
19645     
19646     /**
19647      * @cfg {String} text to display on mask (default Loading)
19648      */
19649     mask : false,
19650     /**
19651      * @cfg {Boolean} multiSelect Allow multiple selection
19652      */
19653     multiSelect : false,
19654     /**
19655      * @cfg {Boolean} singleSelect Allow single selection
19656      */
19657     singleSelect:  false,
19658     
19659     /**
19660      * @cfg {Boolean} toggleSelect - selecting 
19661      */
19662     toggleSelect : false,
19663     
19664     /**
19665      * @cfg {Boolean} tickable - selecting 
19666      */
19667     tickable : false,
19668     
19669     /**
19670      * Returns the element this view is bound to.
19671      * @return {Roo.Element}
19672      */
19673     getEl : function(){
19674         return this.wrapEl;
19675     },
19676     
19677     
19678
19679     /**
19680      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19681      */
19682     refresh : function(){
19683         //Roo.log('refresh');
19684         var t = this.tpl;
19685         
19686         // if we are using something like 'domtemplate', then
19687         // the what gets used is:
19688         // t.applySubtemplate(NAME, data, wrapping data..)
19689         // the outer template then get' applied with
19690         //     the store 'extra data'
19691         // and the body get's added to the
19692         //      roo-name="data" node?
19693         //      <span class='roo-tpl-{name}'></span> ?????
19694         
19695         
19696         
19697         this.clearSelections();
19698         this.el.update("");
19699         var html = [];
19700         var records = this.store.getRange();
19701         if(records.length < 1) {
19702             
19703             // is this valid??  = should it render a template??
19704             
19705             this.el.update(this.emptyText);
19706             return;
19707         }
19708         var el = this.el;
19709         if (this.dataName) {
19710             this.el.update(t.apply(this.store.meta)); //????
19711             el = this.el.child('.roo-tpl-' + this.dataName);
19712         }
19713         
19714         for(var i = 0, len = records.length; i < len; i++){
19715             var data = this.prepareData(records[i].data, i, records[i]);
19716             this.fireEvent("preparedata", this, data, i, records[i]);
19717             
19718             var d = Roo.apply({}, data);
19719             
19720             if(this.tickable){
19721                 Roo.apply(d, {'roo-id' : Roo.id()});
19722                 
19723                 var _this = this;
19724             
19725                 Roo.each(this.parent.item, function(item){
19726                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19727                         return;
19728                     }
19729                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19730                 });
19731             }
19732             
19733             html[html.length] = Roo.util.Format.trim(
19734                 this.dataName ?
19735                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19736                     t.apply(d)
19737             );
19738         }
19739         
19740         
19741         
19742         el.update(html.join(""));
19743         this.nodes = el.dom.childNodes;
19744         this.updateIndexes(0);
19745     },
19746     
19747
19748     /**
19749      * Function to override to reformat the data that is sent to
19750      * the template for each node.
19751      * DEPRICATED - use the preparedata event handler.
19752      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19753      * a JSON object for an UpdateManager bound view).
19754      */
19755     prepareData : function(data, index, record)
19756     {
19757         this.fireEvent("preparedata", this, data, index, record);
19758         return data;
19759     },
19760
19761     onUpdate : function(ds, record){
19762         // Roo.log('on update');   
19763         this.clearSelections();
19764         var index = this.store.indexOf(record);
19765         var n = this.nodes[index];
19766         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19767         n.parentNode.removeChild(n);
19768         this.updateIndexes(index, index);
19769     },
19770
19771     
19772     
19773 // --------- FIXME     
19774     onAdd : function(ds, records, index)
19775     {
19776         //Roo.log(['on Add', ds, records, index] );        
19777         this.clearSelections();
19778         if(this.nodes.length == 0){
19779             this.refresh();
19780             return;
19781         }
19782         var n = this.nodes[index];
19783         for(var i = 0, len = records.length; i < len; i++){
19784             var d = this.prepareData(records[i].data, i, records[i]);
19785             if(n){
19786                 this.tpl.insertBefore(n, d);
19787             }else{
19788                 
19789                 this.tpl.append(this.el, d);
19790             }
19791         }
19792         this.updateIndexes(index);
19793     },
19794
19795     onRemove : function(ds, record, index){
19796        // Roo.log('onRemove');
19797         this.clearSelections();
19798         var el = this.dataName  ?
19799             this.el.child('.roo-tpl-' + this.dataName) :
19800             this.el; 
19801         
19802         el.dom.removeChild(this.nodes[index]);
19803         this.updateIndexes(index);
19804     },
19805
19806     /**
19807      * Refresh an individual node.
19808      * @param {Number} index
19809      */
19810     refreshNode : function(index){
19811         this.onUpdate(this.store, this.store.getAt(index));
19812     },
19813
19814     updateIndexes : function(startIndex, endIndex){
19815         var ns = this.nodes;
19816         startIndex = startIndex || 0;
19817         endIndex = endIndex || ns.length - 1;
19818         for(var i = startIndex; i <= endIndex; i++){
19819             ns[i].nodeIndex = i;
19820         }
19821     },
19822
19823     /**
19824      * Changes the data store this view uses and refresh the view.
19825      * @param {Store} store
19826      */
19827     setStore : function(store, initial){
19828         if(!initial && this.store){
19829             this.store.un("datachanged", this.refresh);
19830             this.store.un("add", this.onAdd);
19831             this.store.un("remove", this.onRemove);
19832             this.store.un("update", this.onUpdate);
19833             this.store.un("clear", this.refresh);
19834             this.store.un("beforeload", this.onBeforeLoad);
19835             this.store.un("load", this.onLoad);
19836             this.store.un("loadexception", this.onLoad);
19837         }
19838         if(store){
19839           
19840             store.on("datachanged", this.refresh, this);
19841             store.on("add", this.onAdd, this);
19842             store.on("remove", this.onRemove, this);
19843             store.on("update", this.onUpdate, this);
19844             store.on("clear", this.refresh, this);
19845             store.on("beforeload", this.onBeforeLoad, this);
19846             store.on("load", this.onLoad, this);
19847             store.on("loadexception", this.onLoad, this);
19848         }
19849         
19850         if(store){
19851             this.refresh();
19852         }
19853     },
19854     /**
19855      * onbeforeLoad - masks the loading area.
19856      *
19857      */
19858     onBeforeLoad : function(store,opts)
19859     {
19860          //Roo.log('onBeforeLoad');   
19861         if (!opts.add) {
19862             this.el.update("");
19863         }
19864         this.el.mask(this.mask ? this.mask : "Loading" ); 
19865     },
19866     onLoad : function ()
19867     {
19868         this.el.unmask();
19869     },
19870     
19871
19872     /**
19873      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19874      * @param {HTMLElement} node
19875      * @return {HTMLElement} The template node
19876      */
19877     findItemFromChild : function(node){
19878         var el = this.dataName  ?
19879             this.el.child('.roo-tpl-' + this.dataName,true) :
19880             this.el.dom; 
19881         
19882         if(!node || node.parentNode == el){
19883                     return node;
19884             }
19885             var p = node.parentNode;
19886             while(p && p != el){
19887             if(p.parentNode == el){
19888                 return p;
19889             }
19890             p = p.parentNode;
19891         }
19892             return null;
19893     },
19894
19895     /** @ignore */
19896     onClick : function(e){
19897         var item = this.findItemFromChild(e.getTarget());
19898         if(item){
19899             var index = this.indexOf(item);
19900             if(this.onItemClick(item, index, e) !== false){
19901                 this.fireEvent("click", this, index, item, e);
19902             }
19903         }else{
19904             this.clearSelections();
19905         }
19906     },
19907
19908     /** @ignore */
19909     onContextMenu : function(e){
19910         var item = this.findItemFromChild(e.getTarget());
19911         if(item){
19912             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19913         }
19914     },
19915
19916     /** @ignore */
19917     onDblClick : function(e){
19918         var item = this.findItemFromChild(e.getTarget());
19919         if(item){
19920             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19921         }
19922     },
19923
19924     onItemClick : function(item, index, e)
19925     {
19926         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19927             return false;
19928         }
19929         if (this.toggleSelect) {
19930             var m = this.isSelected(item) ? 'unselect' : 'select';
19931             //Roo.log(m);
19932             var _t = this;
19933             _t[m](item, true, false);
19934             return true;
19935         }
19936         if(this.multiSelect || this.singleSelect){
19937             if(this.multiSelect && e.shiftKey && this.lastSelection){
19938                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19939             }else{
19940                 this.select(item, this.multiSelect && e.ctrlKey);
19941                 this.lastSelection = item;
19942             }
19943             
19944             if(!this.tickable){
19945                 e.preventDefault();
19946             }
19947             
19948         }
19949         return true;
19950     },
19951
19952     /**
19953      * Get the number of selected nodes.
19954      * @return {Number}
19955      */
19956     getSelectionCount : function(){
19957         return this.selections.length;
19958     },
19959
19960     /**
19961      * Get the currently selected nodes.
19962      * @return {Array} An array of HTMLElements
19963      */
19964     getSelectedNodes : function(){
19965         return this.selections;
19966     },
19967
19968     /**
19969      * Get the indexes of the selected nodes.
19970      * @return {Array}
19971      */
19972     getSelectedIndexes : function(){
19973         var indexes = [], s = this.selections;
19974         for(var i = 0, len = s.length; i < len; i++){
19975             indexes.push(s[i].nodeIndex);
19976         }
19977         return indexes;
19978     },
19979
19980     /**
19981      * Clear all selections
19982      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19983      */
19984     clearSelections : function(suppressEvent){
19985         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19986             this.cmp.elements = this.selections;
19987             this.cmp.removeClass(this.selectedClass);
19988             this.selections = [];
19989             if(!suppressEvent){
19990                 this.fireEvent("selectionchange", this, this.selections);
19991             }
19992         }
19993     },
19994
19995     /**
19996      * Returns true if the passed node is selected
19997      * @param {HTMLElement/Number} node The node or node index
19998      * @return {Boolean}
19999      */
20000     isSelected : function(node){
20001         var s = this.selections;
20002         if(s.length < 1){
20003             return false;
20004         }
20005         node = this.getNode(node);
20006         return s.indexOf(node) !== -1;
20007     },
20008
20009     /**
20010      * Selects nodes.
20011      * @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
20012      * @param {Boolean} keepExisting (optional) true to keep existing selections
20013      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20014      */
20015     select : function(nodeInfo, keepExisting, suppressEvent){
20016         if(nodeInfo instanceof Array){
20017             if(!keepExisting){
20018                 this.clearSelections(true);
20019             }
20020             for(var i = 0, len = nodeInfo.length; i < len; i++){
20021                 this.select(nodeInfo[i], true, true);
20022             }
20023             return;
20024         } 
20025         var node = this.getNode(nodeInfo);
20026         if(!node || this.isSelected(node)){
20027             return; // already selected.
20028         }
20029         if(!keepExisting){
20030             this.clearSelections(true);
20031         }
20032         
20033         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20034             Roo.fly(node).addClass(this.selectedClass);
20035             this.selections.push(node);
20036             if(!suppressEvent){
20037                 this.fireEvent("selectionchange", this, this.selections);
20038             }
20039         }
20040         
20041         
20042     },
20043       /**
20044      * Unselects nodes.
20045      * @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
20046      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20047      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20048      */
20049     unselect : function(nodeInfo, keepExisting, suppressEvent)
20050     {
20051         if(nodeInfo instanceof Array){
20052             Roo.each(this.selections, function(s) {
20053                 this.unselect(s, nodeInfo);
20054             }, this);
20055             return;
20056         }
20057         var node = this.getNode(nodeInfo);
20058         if(!node || !this.isSelected(node)){
20059             //Roo.log("not selected");
20060             return; // not selected.
20061         }
20062         // fireevent???
20063         var ns = [];
20064         Roo.each(this.selections, function(s) {
20065             if (s == node ) {
20066                 Roo.fly(node).removeClass(this.selectedClass);
20067
20068                 return;
20069             }
20070             ns.push(s);
20071         },this);
20072         
20073         this.selections= ns;
20074         this.fireEvent("selectionchange", this, this.selections);
20075     },
20076
20077     /**
20078      * Gets a template node.
20079      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20080      * @return {HTMLElement} The node or null if it wasn't found
20081      */
20082     getNode : function(nodeInfo){
20083         if(typeof nodeInfo == "string"){
20084             return document.getElementById(nodeInfo);
20085         }else if(typeof nodeInfo == "number"){
20086             return this.nodes[nodeInfo];
20087         }
20088         return nodeInfo;
20089     },
20090
20091     /**
20092      * Gets a range template nodes.
20093      * @param {Number} startIndex
20094      * @param {Number} endIndex
20095      * @return {Array} An array of nodes
20096      */
20097     getNodes : function(start, end){
20098         var ns = this.nodes;
20099         start = start || 0;
20100         end = typeof end == "undefined" ? ns.length - 1 : end;
20101         var nodes = [];
20102         if(start <= end){
20103             for(var i = start; i <= end; i++){
20104                 nodes.push(ns[i]);
20105             }
20106         } else{
20107             for(var i = start; i >= end; i--){
20108                 nodes.push(ns[i]);
20109             }
20110         }
20111         return nodes;
20112     },
20113
20114     /**
20115      * Finds the index of the passed node
20116      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20117      * @return {Number} The index of the node or -1
20118      */
20119     indexOf : function(node){
20120         node = this.getNode(node);
20121         if(typeof node.nodeIndex == "number"){
20122             return node.nodeIndex;
20123         }
20124         var ns = this.nodes;
20125         for(var i = 0, len = ns.length; i < len; i++){
20126             if(ns[i] == node){
20127                 return i;
20128             }
20129         }
20130         return -1;
20131     }
20132 });
20133 /*
20134  * - LGPL
20135  *
20136  * based on jquery fullcalendar
20137  * 
20138  */
20139
20140 Roo.bootstrap = Roo.bootstrap || {};
20141 /**
20142  * @class Roo.bootstrap.Calendar
20143  * @extends Roo.bootstrap.Component
20144  * Bootstrap Calendar class
20145  * @cfg {Boolean} loadMask (true|false) default false
20146  * @cfg {Object} header generate the user specific header of the calendar, default false
20147
20148  * @constructor
20149  * Create a new Container
20150  * @param {Object} config The config object
20151  */
20152
20153
20154
20155 Roo.bootstrap.Calendar = function(config){
20156     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20157      this.addEvents({
20158         /**
20159              * @event select
20160              * Fires when a date is selected
20161              * @param {DatePicker} this
20162              * @param {Date} date The selected date
20163              */
20164         'select': true,
20165         /**
20166              * @event monthchange
20167              * Fires when the displayed month changes 
20168              * @param {DatePicker} this
20169              * @param {Date} date The selected month
20170              */
20171         'monthchange': true,
20172         /**
20173              * @event evententer
20174              * Fires when mouse over an event
20175              * @param {Calendar} this
20176              * @param {event} Event
20177              */
20178         'evententer': true,
20179         /**
20180              * @event eventleave
20181              * Fires when the mouse leaves an
20182              * @param {Calendar} this
20183              * @param {event}
20184              */
20185         'eventleave': true,
20186         /**
20187              * @event eventclick
20188              * Fires when the mouse click an
20189              * @param {Calendar} this
20190              * @param {event}
20191              */
20192         'eventclick': true
20193         
20194     });
20195
20196 };
20197
20198 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20199     
20200      /**
20201      * @cfg {Number} startDay
20202      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20203      */
20204     startDay : 0,
20205     
20206     loadMask : false,
20207     
20208     header : false,
20209       
20210     getAutoCreate : function(){
20211         
20212         
20213         var fc_button = function(name, corner, style, content ) {
20214             return Roo.apply({},{
20215                 tag : 'span',
20216                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20217                          (corner.length ?
20218                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20219                             ''
20220                         ),
20221                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20222                 unselectable: 'on'
20223             });
20224         };
20225         
20226         var header = {};
20227         
20228         if(!this.header){
20229             header = {
20230                 tag : 'table',
20231                 cls : 'fc-header',
20232                 style : 'width:100%',
20233                 cn : [
20234                     {
20235                         tag: 'tr',
20236                         cn : [
20237                             {
20238                                 tag : 'td',
20239                                 cls : 'fc-header-left',
20240                                 cn : [
20241                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20242                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20243                                     { tag: 'span', cls: 'fc-header-space' },
20244                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20245
20246
20247                                 ]
20248                             },
20249
20250                             {
20251                                 tag : 'td',
20252                                 cls : 'fc-header-center',
20253                                 cn : [
20254                                     {
20255                                         tag: 'span',
20256                                         cls: 'fc-header-title',
20257                                         cn : {
20258                                             tag: 'H2',
20259                                             html : 'month / year'
20260                                         }
20261                                     }
20262
20263                                 ]
20264                             },
20265                             {
20266                                 tag : 'td',
20267                                 cls : 'fc-header-right',
20268                                 cn : [
20269                               /*      fc_button('month', 'left', '', 'month' ),
20270                                     fc_button('week', '', '', 'week' ),
20271                                     fc_button('day', 'right', '', 'day' )
20272                                 */    
20273
20274                                 ]
20275                             }
20276
20277                         ]
20278                     }
20279                 ]
20280             };
20281         }
20282         
20283         header = this.header;
20284         
20285        
20286         var cal_heads = function() {
20287             var ret = [];
20288             // fixme - handle this.
20289             
20290             for (var i =0; i < Date.dayNames.length; i++) {
20291                 var d = Date.dayNames[i];
20292                 ret.push({
20293                     tag: 'th',
20294                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20295                     html : d.substring(0,3)
20296                 });
20297                 
20298             }
20299             ret[0].cls += ' fc-first';
20300             ret[6].cls += ' fc-last';
20301             return ret;
20302         };
20303         var cal_cell = function(n) {
20304             return  {
20305                 tag: 'td',
20306                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20307                 cn : [
20308                     {
20309                         cn : [
20310                             {
20311                                 cls: 'fc-day-number',
20312                                 html: 'D'
20313                             },
20314                             {
20315                                 cls: 'fc-day-content',
20316                              
20317                                 cn : [
20318                                      {
20319                                         style: 'position: relative;' // height: 17px;
20320                                     }
20321                                 ]
20322                             }
20323                             
20324                             
20325                         ]
20326                     }
20327                 ]
20328                 
20329             }
20330         };
20331         var cal_rows = function() {
20332             
20333             var ret = [];
20334             for (var r = 0; r < 6; r++) {
20335                 var row= {
20336                     tag : 'tr',
20337                     cls : 'fc-week',
20338                     cn : []
20339                 };
20340                 
20341                 for (var i =0; i < Date.dayNames.length; i++) {
20342                     var d = Date.dayNames[i];
20343                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20344
20345                 }
20346                 row.cn[0].cls+=' fc-first';
20347                 row.cn[0].cn[0].style = 'min-height:90px';
20348                 row.cn[6].cls+=' fc-last';
20349                 ret.push(row);
20350                 
20351             }
20352             ret[0].cls += ' fc-first';
20353             ret[4].cls += ' fc-prev-last';
20354             ret[5].cls += ' fc-last';
20355             return ret;
20356             
20357         };
20358         
20359         var cal_table = {
20360             tag: 'table',
20361             cls: 'fc-border-separate',
20362             style : 'width:100%',
20363             cellspacing  : 0,
20364             cn : [
20365                 { 
20366                     tag: 'thead',
20367                     cn : [
20368                         { 
20369                             tag: 'tr',
20370                             cls : 'fc-first fc-last',
20371                             cn : cal_heads()
20372                         }
20373                     ]
20374                 },
20375                 { 
20376                     tag: 'tbody',
20377                     cn : cal_rows()
20378                 }
20379                   
20380             ]
20381         };
20382          
20383          var cfg = {
20384             cls : 'fc fc-ltr',
20385             cn : [
20386                 header,
20387                 {
20388                     cls : 'fc-content',
20389                     style : "position: relative;",
20390                     cn : [
20391                         {
20392                             cls : 'fc-view fc-view-month fc-grid',
20393                             style : 'position: relative',
20394                             unselectable : 'on',
20395                             cn : [
20396                                 {
20397                                     cls : 'fc-event-container',
20398                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20399                                 },
20400                                 cal_table
20401                             ]
20402                         }
20403                     ]
20404     
20405                 }
20406            ] 
20407             
20408         };
20409         
20410          
20411         
20412         return cfg;
20413     },
20414     
20415     
20416     initEvents : function()
20417     {
20418         if(!this.store){
20419             throw "can not find store for calendar";
20420         }
20421         
20422         var mark = {
20423             tag: "div",
20424             cls:"x-dlg-mask",
20425             style: "text-align:center",
20426             cn: [
20427                 {
20428                     tag: "div",
20429                     style: "background-color:white;width:50%;margin:250 auto",
20430                     cn: [
20431                         {
20432                             tag: "img",
20433                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20434                         },
20435                         {
20436                             tag: "span",
20437                             html: "Loading"
20438                         }
20439                         
20440                     ]
20441                 }
20442             ]
20443         };
20444         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20445         
20446         var size = this.el.select('.fc-content', true).first().getSize();
20447         this.maskEl.setSize(size.width, size.height);
20448         this.maskEl.enableDisplayMode("block");
20449         if(!this.loadMask){
20450             this.maskEl.hide();
20451         }
20452         
20453         this.store = Roo.factory(this.store, Roo.data);
20454         this.store.on('load', this.onLoad, this);
20455         this.store.on('beforeload', this.onBeforeLoad, this);
20456         
20457         this.resize();
20458         
20459         this.cells = this.el.select('.fc-day',true);
20460         //Roo.log(this.cells);
20461         this.textNodes = this.el.query('.fc-day-number');
20462         this.cells.addClassOnOver('fc-state-hover');
20463         
20464         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20465         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20466         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20467         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20468         
20469         this.on('monthchange', this.onMonthChange, this);
20470         
20471         this.update(new Date().clearTime());
20472     },
20473     
20474     resize : function() {
20475         var sz  = this.el.getSize();
20476         
20477         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20478         this.el.select('.fc-day-content div',true).setHeight(34);
20479     },
20480     
20481     
20482     // private
20483     showPrevMonth : function(e){
20484         this.update(this.activeDate.add("mo", -1));
20485     },
20486     showToday : function(e){
20487         this.update(new Date().clearTime());
20488     },
20489     // private
20490     showNextMonth : function(e){
20491         this.update(this.activeDate.add("mo", 1));
20492     },
20493
20494     // private
20495     showPrevYear : function(){
20496         this.update(this.activeDate.add("y", -1));
20497     },
20498
20499     // private
20500     showNextYear : function(){
20501         this.update(this.activeDate.add("y", 1));
20502     },
20503
20504     
20505    // private
20506     update : function(date)
20507     {
20508         var vd = this.activeDate;
20509         this.activeDate = date;
20510 //        if(vd && this.el){
20511 //            var t = date.getTime();
20512 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20513 //                Roo.log('using add remove');
20514 //                
20515 //                this.fireEvent('monthchange', this, date);
20516 //                
20517 //                this.cells.removeClass("fc-state-highlight");
20518 //                this.cells.each(function(c){
20519 //                   if(c.dateValue == t){
20520 //                       c.addClass("fc-state-highlight");
20521 //                       setTimeout(function(){
20522 //                            try{c.dom.firstChild.focus();}catch(e){}
20523 //                       }, 50);
20524 //                       return false;
20525 //                   }
20526 //                   return true;
20527 //                });
20528 //                return;
20529 //            }
20530 //        }
20531         
20532         var days = date.getDaysInMonth();
20533         
20534         var firstOfMonth = date.getFirstDateOfMonth();
20535         var startingPos = firstOfMonth.getDay()-this.startDay;
20536         
20537         if(startingPos < this.startDay){
20538             startingPos += 7;
20539         }
20540         
20541         var pm = date.add(Date.MONTH, -1);
20542         var prevStart = pm.getDaysInMonth()-startingPos;
20543 //        
20544         this.cells = this.el.select('.fc-day',true);
20545         this.textNodes = this.el.query('.fc-day-number');
20546         this.cells.addClassOnOver('fc-state-hover');
20547         
20548         var cells = this.cells.elements;
20549         var textEls = this.textNodes;
20550         
20551         Roo.each(cells, function(cell){
20552             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20553         });
20554         
20555         days += startingPos;
20556
20557         // convert everything to numbers so it's fast
20558         var day = 86400000;
20559         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20560         //Roo.log(d);
20561         //Roo.log(pm);
20562         //Roo.log(prevStart);
20563         
20564         var today = new Date().clearTime().getTime();
20565         var sel = date.clearTime().getTime();
20566         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20567         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20568         var ddMatch = this.disabledDatesRE;
20569         var ddText = this.disabledDatesText;
20570         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20571         var ddaysText = this.disabledDaysText;
20572         var format = this.format;
20573         
20574         var setCellClass = function(cal, cell){
20575             cell.row = 0;
20576             cell.events = [];
20577             cell.more = [];
20578             //Roo.log('set Cell Class');
20579             cell.title = "";
20580             var t = d.getTime();
20581             
20582             //Roo.log(d);
20583             
20584             cell.dateValue = t;
20585             if(t == today){
20586                 cell.className += " fc-today";
20587                 cell.className += " fc-state-highlight";
20588                 cell.title = cal.todayText;
20589             }
20590             if(t == sel){
20591                 // disable highlight in other month..
20592                 //cell.className += " fc-state-highlight";
20593                 
20594             }
20595             // disabling
20596             if(t < min) {
20597                 cell.className = " fc-state-disabled";
20598                 cell.title = cal.minText;
20599                 return;
20600             }
20601             if(t > max) {
20602                 cell.className = " fc-state-disabled";
20603                 cell.title = cal.maxText;
20604                 return;
20605             }
20606             if(ddays){
20607                 if(ddays.indexOf(d.getDay()) != -1){
20608                     cell.title = ddaysText;
20609                     cell.className = " fc-state-disabled";
20610                 }
20611             }
20612             if(ddMatch && format){
20613                 var fvalue = d.dateFormat(format);
20614                 if(ddMatch.test(fvalue)){
20615                     cell.title = ddText.replace("%0", fvalue);
20616                     cell.className = " fc-state-disabled";
20617                 }
20618             }
20619             
20620             if (!cell.initialClassName) {
20621                 cell.initialClassName = cell.dom.className;
20622             }
20623             
20624             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20625         };
20626
20627         var i = 0;
20628         
20629         for(; i < startingPos; i++) {
20630             textEls[i].innerHTML = (++prevStart);
20631             d.setDate(d.getDate()+1);
20632             
20633             cells[i].className = "fc-past fc-other-month";
20634             setCellClass(this, cells[i]);
20635         }
20636         
20637         var intDay = 0;
20638         
20639         for(; i < days; i++){
20640             intDay = i - startingPos + 1;
20641             textEls[i].innerHTML = (intDay);
20642             d.setDate(d.getDate()+1);
20643             
20644             cells[i].className = ''; // "x-date-active";
20645             setCellClass(this, cells[i]);
20646         }
20647         var extraDays = 0;
20648         
20649         for(; i < 42; i++) {
20650             textEls[i].innerHTML = (++extraDays);
20651             d.setDate(d.getDate()+1);
20652             
20653             cells[i].className = "fc-future fc-other-month";
20654             setCellClass(this, cells[i]);
20655         }
20656         
20657         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20658         
20659         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20660         
20661         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20662         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20663         
20664         if(totalRows != 6){
20665             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20666             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20667         }
20668         
20669         this.fireEvent('monthchange', this, date);
20670         
20671         
20672         /*
20673         if(!this.internalRender){
20674             var main = this.el.dom.firstChild;
20675             var w = main.offsetWidth;
20676             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20677             Roo.fly(main).setWidth(w);
20678             this.internalRender = true;
20679             // opera does not respect the auto grow header center column
20680             // then, after it gets a width opera refuses to recalculate
20681             // without a second pass
20682             if(Roo.isOpera && !this.secondPass){
20683                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20684                 this.secondPass = true;
20685                 this.update.defer(10, this, [date]);
20686             }
20687         }
20688         */
20689         
20690     },
20691     
20692     findCell : function(dt) {
20693         dt = dt.clearTime().getTime();
20694         var ret = false;
20695         this.cells.each(function(c){
20696             //Roo.log("check " +c.dateValue + '?=' + dt);
20697             if(c.dateValue == dt){
20698                 ret = c;
20699                 return false;
20700             }
20701             return true;
20702         });
20703         
20704         return ret;
20705     },
20706     
20707     findCells : function(ev) {
20708         var s = ev.start.clone().clearTime().getTime();
20709        // Roo.log(s);
20710         var e= ev.end.clone().clearTime().getTime();
20711        // Roo.log(e);
20712         var ret = [];
20713         this.cells.each(function(c){
20714              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20715             
20716             if(c.dateValue > e){
20717                 return ;
20718             }
20719             if(c.dateValue < s){
20720                 return ;
20721             }
20722             ret.push(c);
20723         });
20724         
20725         return ret;    
20726     },
20727     
20728 //    findBestRow: function(cells)
20729 //    {
20730 //        var ret = 0;
20731 //        
20732 //        for (var i =0 ; i < cells.length;i++) {
20733 //            ret  = Math.max(cells[i].rows || 0,ret);
20734 //        }
20735 //        return ret;
20736 //        
20737 //    },
20738     
20739     
20740     addItem : function(ev)
20741     {
20742         // look for vertical location slot in
20743         var cells = this.findCells(ev);
20744         
20745 //        ev.row = this.findBestRow(cells);
20746         
20747         // work out the location.
20748         
20749         var crow = false;
20750         var rows = [];
20751         for(var i =0; i < cells.length; i++) {
20752             
20753             cells[i].row = cells[0].row;
20754             
20755             if(i == 0){
20756                 cells[i].row = cells[i].row + 1;
20757             }
20758             
20759             if (!crow) {
20760                 crow = {
20761                     start : cells[i],
20762                     end :  cells[i]
20763                 };
20764                 continue;
20765             }
20766             if (crow.start.getY() == cells[i].getY()) {
20767                 // on same row.
20768                 crow.end = cells[i];
20769                 continue;
20770             }
20771             // different row.
20772             rows.push(crow);
20773             crow = {
20774                 start: cells[i],
20775                 end : cells[i]
20776             };
20777             
20778         }
20779         
20780         rows.push(crow);
20781         ev.els = [];
20782         ev.rows = rows;
20783         ev.cells = cells;
20784         
20785         cells[0].events.push(ev);
20786         
20787         this.calevents.push(ev);
20788     },
20789     
20790     clearEvents: function() {
20791         
20792         if(!this.calevents){
20793             return;
20794         }
20795         
20796         Roo.each(this.cells.elements, function(c){
20797             c.row = 0;
20798             c.events = [];
20799             c.more = [];
20800         });
20801         
20802         Roo.each(this.calevents, function(e) {
20803             Roo.each(e.els, function(el) {
20804                 el.un('mouseenter' ,this.onEventEnter, this);
20805                 el.un('mouseleave' ,this.onEventLeave, this);
20806                 el.remove();
20807             },this);
20808         },this);
20809         
20810         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20811             e.remove();
20812         });
20813         
20814     },
20815     
20816     renderEvents: function()
20817     {   
20818         var _this = this;
20819         
20820         this.cells.each(function(c) {
20821             
20822             if(c.row < 5){
20823                 return;
20824             }
20825             
20826             var ev = c.events;
20827             
20828             var r = 4;
20829             if(c.row != c.events.length){
20830                 r = 4 - (4 - (c.row - c.events.length));
20831             }
20832             
20833             c.events = ev.slice(0, r);
20834             c.more = ev.slice(r);
20835             
20836             if(c.more.length && c.more.length == 1){
20837                 c.events.push(c.more.pop());
20838             }
20839             
20840             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20841             
20842         });
20843             
20844         this.cells.each(function(c) {
20845             
20846             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20847             
20848             
20849             for (var e = 0; e < c.events.length; e++){
20850                 var ev = c.events[e];
20851                 var rows = ev.rows;
20852                 
20853                 for(var i = 0; i < rows.length; i++) {
20854                 
20855                     // how many rows should it span..
20856
20857                     var  cfg = {
20858                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20859                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20860
20861                         unselectable : "on",
20862                         cn : [
20863                             {
20864                                 cls: 'fc-event-inner',
20865                                 cn : [
20866     //                                {
20867     //                                  tag:'span',
20868     //                                  cls: 'fc-event-time',
20869     //                                  html : cells.length > 1 ? '' : ev.time
20870     //                                },
20871                                     {
20872                                       tag:'span',
20873                                       cls: 'fc-event-title',
20874                                       html : String.format('{0}', ev.title)
20875                                     }
20876
20877
20878                                 ]
20879                             },
20880                             {
20881                                 cls: 'ui-resizable-handle ui-resizable-e',
20882                                 html : '&nbsp;&nbsp;&nbsp'
20883                             }
20884
20885                         ]
20886                     };
20887
20888                     if (i == 0) {
20889                         cfg.cls += ' fc-event-start';
20890                     }
20891                     if ((i+1) == rows.length) {
20892                         cfg.cls += ' fc-event-end';
20893                     }
20894
20895                     var ctr = _this.el.select('.fc-event-container',true).first();
20896                     var cg = ctr.createChild(cfg);
20897
20898                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20899                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20900
20901                     var r = (c.more.length) ? 1 : 0;
20902                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20903                     cg.setWidth(ebox.right - sbox.x -2);
20904
20905                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20906                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20907                     cg.on('click', _this.onEventClick, _this, ev);
20908
20909                     ev.els.push(cg);
20910                     
20911                 }
20912                 
20913             }
20914             
20915             
20916             if(c.more.length){
20917                 var  cfg = {
20918                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20919                     style : 'position: absolute',
20920                     unselectable : "on",
20921                     cn : [
20922                         {
20923                             cls: 'fc-event-inner',
20924                             cn : [
20925                                 {
20926                                   tag:'span',
20927                                   cls: 'fc-event-title',
20928                                   html : 'More'
20929                                 }
20930
20931
20932                             ]
20933                         },
20934                         {
20935                             cls: 'ui-resizable-handle ui-resizable-e',
20936                             html : '&nbsp;&nbsp;&nbsp'
20937                         }
20938
20939                     ]
20940                 };
20941
20942                 var ctr = _this.el.select('.fc-event-container',true).first();
20943                 var cg = ctr.createChild(cfg);
20944
20945                 var sbox = c.select('.fc-day-content',true).first().getBox();
20946                 var ebox = c.select('.fc-day-content',true).first().getBox();
20947                 //Roo.log(cg);
20948                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20949                 cg.setWidth(ebox.right - sbox.x -2);
20950
20951                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20952                 
20953             }
20954             
20955         });
20956         
20957         
20958         
20959     },
20960     
20961     onEventEnter: function (e, el,event,d) {
20962         this.fireEvent('evententer', this, el, event);
20963     },
20964     
20965     onEventLeave: function (e, el,event,d) {
20966         this.fireEvent('eventleave', this, el, event);
20967     },
20968     
20969     onEventClick: function (e, el,event,d) {
20970         this.fireEvent('eventclick', this, el, event);
20971     },
20972     
20973     onMonthChange: function () {
20974         this.store.load();
20975     },
20976     
20977     onMoreEventClick: function(e, el, more)
20978     {
20979         var _this = this;
20980         
20981         this.calpopover.placement = 'right';
20982         this.calpopover.setTitle('More');
20983         
20984         this.calpopover.setContent('');
20985         
20986         var ctr = this.calpopover.el.select('.popover-content', true).first();
20987         
20988         Roo.each(more, function(m){
20989             var cfg = {
20990                 cls : 'fc-event-hori fc-event-draggable',
20991                 html : m.title
20992             };
20993             var cg = ctr.createChild(cfg);
20994             
20995             cg.on('click', _this.onEventClick, _this, m);
20996         });
20997         
20998         this.calpopover.show(el);
20999         
21000         
21001     },
21002     
21003     onLoad: function () 
21004     {   
21005         this.calevents = [];
21006         var cal = this;
21007         
21008         if(this.store.getCount() > 0){
21009             this.store.data.each(function(d){
21010                cal.addItem({
21011                     id : d.data.id,
21012                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21013                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21014                     time : d.data.start_time,
21015                     title : d.data.title,
21016                     description : d.data.description,
21017                     venue : d.data.venue
21018                 });
21019             });
21020         }
21021         
21022         this.renderEvents();
21023         
21024         if(this.calevents.length && this.loadMask){
21025             this.maskEl.hide();
21026         }
21027     },
21028     
21029     onBeforeLoad: function()
21030     {
21031         this.clearEvents();
21032         if(this.loadMask){
21033             this.maskEl.show();
21034         }
21035     }
21036 });
21037
21038  
21039  /*
21040  * - LGPL
21041  *
21042  * element
21043  * 
21044  */
21045
21046 /**
21047  * @class Roo.bootstrap.Popover
21048  * @extends Roo.bootstrap.Component
21049  * Bootstrap Popover class
21050  * @cfg {String} html contents of the popover   (or false to use children..)
21051  * @cfg {String} title of popover (or false to hide)
21052  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21053  * @cfg {String} trigger click || hover (or false to trigger manually)
21054  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21055  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21056  *      - if false and it has a 'parent' then it will be automatically added to that element
21057  *      - if string - Roo.get  will be called 
21058  * @cfg {Number} delay - delay before showing
21059  
21060  * @constructor
21061  * Create a new Popover
21062  * @param {Object} config The config object
21063  */
21064
21065 Roo.bootstrap.Popover = function(config){
21066     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21067     
21068     this.addEvents({
21069         // raw events
21070          /**
21071          * @event show
21072          * After the popover show
21073          * 
21074          * @param {Roo.bootstrap.Popover} this
21075          */
21076         "show" : true,
21077         /**
21078          * @event hide
21079          * After the popover hide
21080          * 
21081          * @param {Roo.bootstrap.Popover} this
21082          */
21083         "hide" : true
21084     });
21085 };
21086
21087 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21088     
21089     title: false,
21090     html: false,
21091     
21092     placement : 'right',
21093     trigger : 'hover', // hover
21094     modal : false,
21095     delay : 0,
21096     
21097     over: false,
21098     
21099     can_build_overlaid : false,
21100     
21101     maskEl : false, // the mask element
21102     headerEl : false,
21103     contentEl : false,
21104     alignEl : false, // when show is called with an element - this get's stored.
21105     
21106     getChildContainer : function()
21107     {
21108         return this.contentEl;
21109         
21110     },
21111     getPopoverHeader : function()
21112     {
21113         this.title = true; // flag not to hide it..
21114         this.headerEl.addClass('p-0');
21115         return this.headerEl
21116     },
21117     
21118     
21119     getAutoCreate : function(){
21120          
21121         var cfg = {
21122            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21123            style: 'display:block',
21124            cn : [
21125                 {
21126                     cls : 'arrow'
21127                 },
21128                 {
21129                     cls : 'popover-inner ',
21130                     cn : [
21131                         {
21132                             tag: 'h3',
21133                             cls: 'popover-title popover-header',
21134                             html : this.title === false ? '' : this.title
21135                         },
21136                         {
21137                             cls : 'popover-content popover-body '  + (this.cls || ''),
21138                             html : this.html || ''
21139                         }
21140                     ]
21141                     
21142                 }
21143            ]
21144         };
21145         
21146         return cfg;
21147     },
21148     /**
21149      * @param {string} the title
21150      */
21151     setTitle: function(str)
21152     {
21153         this.title = str;
21154         if (this.el) {
21155             this.headerEl.dom.innerHTML = str;
21156         }
21157         
21158     },
21159     /**
21160      * @param {string} the body content
21161      */
21162     setContent: function(str)
21163     {
21164         this.html = str;
21165         if (this.contentEl) {
21166             this.contentEl.dom.innerHTML = str;
21167         }
21168         
21169     },
21170     // as it get's added to the bottom of the page.
21171     onRender : function(ct, position)
21172     {
21173         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21174         
21175         
21176         
21177         if(!this.el){
21178             var cfg = Roo.apply({},  this.getAutoCreate());
21179             cfg.id = Roo.id();
21180             
21181             if (this.cls) {
21182                 cfg.cls += ' ' + this.cls;
21183             }
21184             if (this.style) {
21185                 cfg.style = this.style;
21186             }
21187             //Roo.log("adding to ");
21188             this.el = Roo.get(document.body).createChild(cfg, position);
21189 //            Roo.log(this.el);
21190         }
21191         
21192         this.contentEl = this.el.select('.popover-content',true).first();
21193         this.headerEl =  this.el.select('.popover-title',true).first();
21194         
21195         var nitems = [];
21196         if(typeof(this.items) != 'undefined'){
21197             var items = this.items;
21198             delete this.items;
21199
21200             for(var i =0;i < items.length;i++) {
21201                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21202             }
21203         }
21204
21205         this.items = nitems;
21206         
21207         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21208         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21209         
21210         
21211         
21212         this.initEvents();
21213     },
21214     
21215     resizeMask : function()
21216     {
21217         this.maskEl.setSize(
21218             Roo.lib.Dom.getViewWidth(true),
21219             Roo.lib.Dom.getViewHeight(true)
21220         );
21221     },
21222     
21223     initEvents : function()
21224     {
21225         
21226         if (!this.modal) { 
21227             Roo.bootstrap.Popover.register(this);
21228         }
21229          
21230         this.arrowEl = this.el.select('.arrow',true).first();
21231         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21232         this.el.enableDisplayMode('block');
21233         this.el.hide();
21234  
21235         
21236         if (this.over === false && !this.parent()) {
21237             return; 
21238         }
21239         if (this.triggers === false) {
21240             return;
21241         }
21242          
21243         // support parent
21244         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21245         var triggers = this.trigger ? this.trigger.split(' ') : [];
21246         Roo.each(triggers, function(trigger) {
21247         
21248             if (trigger == 'click') {
21249                 on_el.on('click', this.toggle, this);
21250             } else if (trigger != 'manual') {
21251                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21252                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21253       
21254                 on_el.on(eventIn  ,this.enter, this);
21255                 on_el.on(eventOut, this.leave, this);
21256             }
21257         }, this);
21258     },
21259     
21260     
21261     // private
21262     timeout : null,
21263     hoverState : null,
21264     
21265     toggle : function () {
21266         this.hoverState == 'in' ? this.leave() : this.enter();
21267     },
21268     
21269     enter : function () {
21270         
21271         clearTimeout(this.timeout);
21272     
21273         this.hoverState = 'in';
21274     
21275         if (!this.delay || !this.delay.show) {
21276             this.show();
21277             return;
21278         }
21279         var _t = this;
21280         this.timeout = setTimeout(function () {
21281             if (_t.hoverState == 'in') {
21282                 _t.show();
21283             }
21284         }, this.delay.show)
21285     },
21286     
21287     leave : function() {
21288         clearTimeout(this.timeout);
21289     
21290         this.hoverState = 'out';
21291     
21292         if (!this.delay || !this.delay.hide) {
21293             this.hide();
21294             return;
21295         }
21296         var _t = this;
21297         this.timeout = setTimeout(function () {
21298             if (_t.hoverState == 'out') {
21299                 _t.hide();
21300             }
21301         }, this.delay.hide)
21302     },
21303     /**
21304      * Show the popover
21305      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21306      * @param {string} (left|right|top|bottom) position
21307      */
21308     show : function (on_el, placement)
21309     {
21310         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21311         on_el = on_el || false; // default to false
21312          
21313         if (!on_el) {
21314             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21315                 on_el = this.parent().el;
21316             } else if (this.over) {
21317                 on_el = Roo.get(this.over);
21318             }
21319             
21320         }
21321         
21322         this.alignEl = Roo.get( on_el );
21323
21324         if (!this.el) {
21325             this.render(document.body);
21326         }
21327         
21328         
21329          
21330         
21331         if (this.title === false) {
21332             this.headerEl.hide();
21333         }
21334         
21335        
21336         this.el.show();
21337         this.el.dom.style.display = 'block';
21338          
21339  
21340         if (this.alignEl) {
21341             this.updatePosition(this.placement, true);
21342              
21343         } else {
21344             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21345             var es = this.el.getSize();
21346             var x = Roo.lib.Dom.getViewWidth()/2;
21347             var y = Roo.lib.Dom.getViewHeight()/2;
21348             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21349             
21350         }
21351
21352         
21353         //var arrow = this.el.select('.arrow',true).first();
21354         //arrow.set(align[2], 
21355         
21356         this.el.addClass('in');
21357         
21358          
21359         
21360         this.hoverState = 'in';
21361         
21362         if (this.modal) {
21363             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21364             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21365             this.maskEl.dom.style.display = 'block';
21366             this.maskEl.addClass('show');
21367         }
21368         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21369  
21370         this.fireEvent('show', this);
21371         
21372     },
21373     /**
21374      * fire this manually after loading a grid in the table for example
21375      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21376      * @param {Boolean} try and move it if we cant get right position.
21377      */
21378     updatePosition : function(placement, try_move)
21379     {
21380         // allow for calling with no parameters
21381         placement = placement   ? placement :  this.placement;
21382         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21383         
21384         this.el.removeClass([
21385             'fade','top','bottom', 'left', 'right','in',
21386             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21387         ]);
21388         this.el.addClass(placement + ' bs-popover-' + placement);
21389         
21390         if (!this.alignEl ) {
21391             return false;
21392         }
21393         
21394         switch (placement) {
21395             case 'right':
21396                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21397                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21398                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21399                     //normal display... or moved up/down.
21400                     this.el.setXY(offset);
21401                     var xy = this.alignEl.getAnchorXY('tr', false);
21402                     xy[0]+=2;xy[1]+=5;
21403                     this.arrowEl.setXY(xy);
21404                     return true;
21405                 }
21406                 // continue through...
21407                 return this.updatePosition('left', false);
21408                 
21409             
21410             case 'left':
21411                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21412                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21413                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21414                     //normal display... or moved up/down.
21415                     this.el.setXY(offset);
21416                     var xy = this.alignEl.getAnchorXY('tl', false);
21417                     xy[0]-=10;xy[1]+=5; // << fix me
21418                     this.arrowEl.setXY(xy);
21419                     return true;
21420                 }
21421                 // call self...
21422                 return this.updatePosition('right', false);
21423             
21424             case 'top':
21425                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21426                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21427                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21428                     //normal display... or moved up/down.
21429                     this.el.setXY(offset);
21430                     var xy = this.alignEl.getAnchorXY('t', false);
21431                     xy[1]-=10; // << fix me
21432                     this.arrowEl.setXY(xy);
21433                     return true;
21434                 }
21435                 // fall through
21436                return this.updatePosition('bottom', false);
21437             
21438             case 'bottom':
21439                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21440                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21441                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21442                     //normal display... or moved up/down.
21443                     this.el.setXY(offset);
21444                     var xy = this.alignEl.getAnchorXY('b', false);
21445                      xy[1]+=2; // << fix me
21446                     this.arrowEl.setXY(xy);
21447                     return true;
21448                 }
21449                 // fall through
21450                 return this.updatePosition('top', false);
21451                 
21452             
21453         }
21454         
21455         
21456         return false;
21457     },
21458     
21459     hide : function()
21460     {
21461         this.el.setXY([0,0]);
21462         this.el.removeClass('in');
21463         this.el.hide();
21464         this.hoverState = null;
21465         this.maskEl.hide(); // always..
21466         this.fireEvent('hide', this);
21467     }
21468     
21469 });
21470
21471
21472 Roo.apply(Roo.bootstrap.Popover, {
21473
21474     alignment : {
21475         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21476         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21477         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21478         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21479     },
21480     
21481     zIndex : 20001,
21482
21483     clickHander : false,
21484     
21485     
21486
21487     onMouseDown : function(e)
21488     {
21489         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21490             /// what is nothing is showing..
21491             this.hideAll();
21492         }
21493          
21494     },
21495     
21496     
21497     popups : [],
21498     
21499     register : function(popup)
21500     {
21501         if (!Roo.bootstrap.Popover.clickHandler) {
21502             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21503         }
21504         // hide other popups.
21505         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21506         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21507         this.hideAll(); //<< why?
21508         //this.popups.push(popup);
21509     },
21510     hideAll : function()
21511     {
21512         this.popups.forEach(function(p) {
21513             p.hide();
21514         });
21515     },
21516     onShow : function() {
21517         Roo.bootstrap.Popover.popups.push(this);
21518     },
21519     onHide : function() {
21520         Roo.bootstrap.Popover.popups.remove(this);
21521     } 
21522
21523 });/*
21524  * - LGPL
21525  *
21526  * Card header - holder for the card header elements.
21527  * 
21528  */
21529
21530 /**
21531  * @class Roo.bootstrap.PopoverNav
21532  * @extends Roo.bootstrap.NavGroup
21533  * Bootstrap Popover header navigation class
21534  * @constructor
21535  * Create a new Popover Header Navigation 
21536  * @param {Object} config The config object
21537  */
21538
21539 Roo.bootstrap.PopoverNav = function(config){
21540     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21541 };
21542
21543 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21544     
21545     
21546     container_method : 'getPopoverHeader' 
21547     
21548      
21549     
21550     
21551    
21552 });
21553
21554  
21555
21556  /*
21557  * - LGPL
21558  *
21559  * Progress
21560  * 
21561  */
21562
21563 /**
21564  * @class Roo.bootstrap.Progress
21565  * @extends Roo.bootstrap.Component
21566  * Bootstrap Progress class
21567  * @cfg {Boolean} striped striped of the progress bar
21568  * @cfg {Boolean} active animated of the progress bar
21569  * 
21570  * 
21571  * @constructor
21572  * Create a new Progress
21573  * @param {Object} config The config object
21574  */
21575
21576 Roo.bootstrap.Progress = function(config){
21577     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21578 };
21579
21580 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21581     
21582     striped : false,
21583     active: false,
21584     
21585     getAutoCreate : function(){
21586         var cfg = {
21587             tag: 'div',
21588             cls: 'progress'
21589         };
21590         
21591         
21592         if(this.striped){
21593             cfg.cls += ' progress-striped';
21594         }
21595       
21596         if(this.active){
21597             cfg.cls += ' active';
21598         }
21599         
21600         
21601         return cfg;
21602     }
21603    
21604 });
21605
21606  
21607
21608  /*
21609  * - LGPL
21610  *
21611  * ProgressBar
21612  * 
21613  */
21614
21615 /**
21616  * @class Roo.bootstrap.ProgressBar
21617  * @extends Roo.bootstrap.Component
21618  * Bootstrap ProgressBar class
21619  * @cfg {Number} aria_valuenow aria-value now
21620  * @cfg {Number} aria_valuemin aria-value min
21621  * @cfg {Number} aria_valuemax aria-value max
21622  * @cfg {String} label label for the progress bar
21623  * @cfg {String} panel (success | info | warning | danger )
21624  * @cfg {String} role role of the progress bar
21625  * @cfg {String} sr_only text
21626  * 
21627  * 
21628  * @constructor
21629  * Create a new ProgressBar
21630  * @param {Object} config The config object
21631  */
21632
21633 Roo.bootstrap.ProgressBar = function(config){
21634     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21635 };
21636
21637 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21638     
21639     aria_valuenow : 0,
21640     aria_valuemin : 0,
21641     aria_valuemax : 100,
21642     label : false,
21643     panel : false,
21644     role : false,
21645     sr_only: false,
21646     
21647     getAutoCreate : function()
21648     {
21649         
21650         var cfg = {
21651             tag: 'div',
21652             cls: 'progress-bar',
21653             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21654         };
21655         
21656         if(this.sr_only){
21657             cfg.cn = {
21658                 tag: 'span',
21659                 cls: 'sr-only',
21660                 html: this.sr_only
21661             }
21662         }
21663         
21664         if(this.role){
21665             cfg.role = this.role;
21666         }
21667         
21668         if(this.aria_valuenow){
21669             cfg['aria-valuenow'] = this.aria_valuenow;
21670         }
21671         
21672         if(this.aria_valuemin){
21673             cfg['aria-valuemin'] = this.aria_valuemin;
21674         }
21675         
21676         if(this.aria_valuemax){
21677             cfg['aria-valuemax'] = this.aria_valuemax;
21678         }
21679         
21680         if(this.label && !this.sr_only){
21681             cfg.html = this.label;
21682         }
21683         
21684         if(this.panel){
21685             cfg.cls += ' progress-bar-' + this.panel;
21686         }
21687         
21688         return cfg;
21689     },
21690     
21691     update : function(aria_valuenow)
21692     {
21693         this.aria_valuenow = aria_valuenow;
21694         
21695         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21696     }
21697    
21698 });
21699
21700  
21701
21702  /*
21703  * - LGPL
21704  *
21705  * column
21706  * 
21707  */
21708
21709 /**
21710  * @class Roo.bootstrap.TabGroup
21711  * @extends Roo.bootstrap.Column
21712  * Bootstrap Column class
21713  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21714  * @cfg {Boolean} carousel true to make the group behave like a carousel
21715  * @cfg {Boolean} bullets show bullets for the panels
21716  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21717  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21718  * @cfg {Boolean} showarrow (true|false) show arrow default true
21719  * 
21720  * @constructor
21721  * Create a new TabGroup
21722  * @param {Object} config The config object
21723  */
21724
21725 Roo.bootstrap.TabGroup = function(config){
21726     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21727     if (!this.navId) {
21728         this.navId = Roo.id();
21729     }
21730     this.tabs = [];
21731     Roo.bootstrap.TabGroup.register(this);
21732     
21733 };
21734
21735 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21736     
21737     carousel : false,
21738     transition : false,
21739     bullets : 0,
21740     timer : 0,
21741     autoslide : false,
21742     slideFn : false,
21743     slideOnTouch : false,
21744     showarrow : true,
21745     
21746     getAutoCreate : function()
21747     {
21748         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21749         
21750         cfg.cls += ' tab-content';
21751         
21752         if (this.carousel) {
21753             cfg.cls += ' carousel slide';
21754             
21755             cfg.cn = [{
21756                cls : 'carousel-inner',
21757                cn : []
21758             }];
21759         
21760             if(this.bullets  && !Roo.isTouch){
21761                 
21762                 var bullets = {
21763                     cls : 'carousel-bullets',
21764                     cn : []
21765                 };
21766                
21767                 if(this.bullets_cls){
21768                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21769                 }
21770                 
21771                 bullets.cn.push({
21772                     cls : 'clear'
21773                 });
21774                 
21775                 cfg.cn[0].cn.push(bullets);
21776             }
21777             
21778             if(this.showarrow){
21779                 cfg.cn[0].cn.push({
21780                     tag : 'div',
21781                     class : 'carousel-arrow',
21782                     cn : [
21783                         {
21784                             tag : 'div',
21785                             class : 'carousel-prev',
21786                             cn : [
21787                                 {
21788                                     tag : 'i',
21789                                     class : 'fa fa-chevron-left'
21790                                 }
21791                             ]
21792                         },
21793                         {
21794                             tag : 'div',
21795                             class : 'carousel-next',
21796                             cn : [
21797                                 {
21798                                     tag : 'i',
21799                                     class : 'fa fa-chevron-right'
21800                                 }
21801                             ]
21802                         }
21803                     ]
21804                 });
21805             }
21806             
21807         }
21808         
21809         return cfg;
21810     },
21811     
21812     initEvents:  function()
21813     {
21814 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21815 //            this.el.on("touchstart", this.onTouchStart, this);
21816 //        }
21817         
21818         if(this.autoslide){
21819             var _this = this;
21820             
21821             this.slideFn = window.setInterval(function() {
21822                 _this.showPanelNext();
21823             }, this.timer);
21824         }
21825         
21826         if(this.showarrow){
21827             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21828             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21829         }
21830         
21831         
21832     },
21833     
21834 //    onTouchStart : function(e, el, o)
21835 //    {
21836 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21837 //            return;
21838 //        }
21839 //        
21840 //        this.showPanelNext();
21841 //    },
21842     
21843     
21844     getChildContainer : function()
21845     {
21846         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21847     },
21848     
21849     /**
21850     * register a Navigation item
21851     * @param {Roo.bootstrap.NavItem} the navitem to add
21852     */
21853     register : function(item)
21854     {
21855         this.tabs.push( item);
21856         item.navId = this.navId; // not really needed..
21857         this.addBullet();
21858     
21859     },
21860     
21861     getActivePanel : function()
21862     {
21863         var r = false;
21864         Roo.each(this.tabs, function(t) {
21865             if (t.active) {
21866                 r = t;
21867                 return false;
21868             }
21869             return null;
21870         });
21871         return r;
21872         
21873     },
21874     getPanelByName : function(n)
21875     {
21876         var r = false;
21877         Roo.each(this.tabs, function(t) {
21878             if (t.tabId == n) {
21879                 r = t;
21880                 return false;
21881             }
21882             return null;
21883         });
21884         return r;
21885     },
21886     indexOfPanel : function(p)
21887     {
21888         var r = false;
21889         Roo.each(this.tabs, function(t,i) {
21890             if (t.tabId == p.tabId) {
21891                 r = i;
21892                 return false;
21893             }
21894             return null;
21895         });
21896         return r;
21897     },
21898     /**
21899      * show a specific panel
21900      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21901      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21902      */
21903     showPanel : function (pan)
21904     {
21905         if(this.transition || typeof(pan) == 'undefined'){
21906             Roo.log("waiting for the transitionend");
21907             return false;
21908         }
21909         
21910         if (typeof(pan) == 'number') {
21911             pan = this.tabs[pan];
21912         }
21913         
21914         if (typeof(pan) == 'string') {
21915             pan = this.getPanelByName(pan);
21916         }
21917         
21918         var cur = this.getActivePanel();
21919         
21920         if(!pan || !cur){
21921             Roo.log('pan or acitve pan is undefined');
21922             return false;
21923         }
21924         
21925         if (pan.tabId == this.getActivePanel().tabId) {
21926             return true;
21927         }
21928         
21929         if (false === cur.fireEvent('beforedeactivate')) {
21930             return false;
21931         }
21932         
21933         if(this.bullets > 0 && !Roo.isTouch){
21934             this.setActiveBullet(this.indexOfPanel(pan));
21935         }
21936         
21937         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21938             
21939             //class="carousel-item carousel-item-next carousel-item-left"
21940             
21941             this.transition = true;
21942             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21943             var lr = dir == 'next' ? 'left' : 'right';
21944             pan.el.addClass(dir); // or prev
21945             pan.el.addClass('carousel-item-' + dir); // or prev
21946             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21947             cur.el.addClass(lr); // or right
21948             pan.el.addClass(lr);
21949             cur.el.addClass('carousel-item-' +lr); // or right
21950             pan.el.addClass('carousel-item-' +lr);
21951             
21952             
21953             var _this = this;
21954             cur.el.on('transitionend', function() {
21955                 Roo.log("trans end?");
21956                 
21957                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21958                 pan.setActive(true);
21959                 
21960                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21961                 cur.setActive(false);
21962                 
21963                 _this.transition = false;
21964                 
21965             }, this, { single:  true } );
21966             
21967             return true;
21968         }
21969         
21970         cur.setActive(false);
21971         pan.setActive(true);
21972         
21973         return true;
21974         
21975     },
21976     showPanelNext : function()
21977     {
21978         var i = this.indexOfPanel(this.getActivePanel());
21979         
21980         if (i >= this.tabs.length - 1 && !this.autoslide) {
21981             return;
21982         }
21983         
21984         if (i >= this.tabs.length - 1 && this.autoslide) {
21985             i = -1;
21986         }
21987         
21988         this.showPanel(this.tabs[i+1]);
21989     },
21990     
21991     showPanelPrev : function()
21992     {
21993         var i = this.indexOfPanel(this.getActivePanel());
21994         
21995         if (i  < 1 && !this.autoslide) {
21996             return;
21997         }
21998         
21999         if (i < 1 && this.autoslide) {
22000             i = this.tabs.length;
22001         }
22002         
22003         this.showPanel(this.tabs[i-1]);
22004     },
22005     
22006     
22007     addBullet: function()
22008     {
22009         if(!this.bullets || Roo.isTouch){
22010             return;
22011         }
22012         var ctr = this.el.select('.carousel-bullets',true).first();
22013         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22014         var bullet = ctr.createChild({
22015             cls : 'bullet bullet-' + i
22016         },ctr.dom.lastChild);
22017         
22018         
22019         var _this = this;
22020         
22021         bullet.on('click', (function(e, el, o, ii, t){
22022
22023             e.preventDefault();
22024
22025             this.showPanel(ii);
22026
22027             if(this.autoslide && this.slideFn){
22028                 clearInterval(this.slideFn);
22029                 this.slideFn = window.setInterval(function() {
22030                     _this.showPanelNext();
22031                 }, this.timer);
22032             }
22033
22034         }).createDelegate(this, [i, bullet], true));
22035                 
22036         
22037     },
22038      
22039     setActiveBullet : function(i)
22040     {
22041         if(Roo.isTouch){
22042             return;
22043         }
22044         
22045         Roo.each(this.el.select('.bullet', true).elements, function(el){
22046             el.removeClass('selected');
22047         });
22048
22049         var bullet = this.el.select('.bullet-' + i, true).first();
22050         
22051         if(!bullet){
22052             return;
22053         }
22054         
22055         bullet.addClass('selected');
22056     }
22057     
22058     
22059   
22060 });
22061
22062  
22063
22064  
22065  
22066 Roo.apply(Roo.bootstrap.TabGroup, {
22067     
22068     groups: {},
22069      /**
22070     * register a Navigation Group
22071     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22072     */
22073     register : function(navgrp)
22074     {
22075         this.groups[navgrp.navId] = navgrp;
22076         
22077     },
22078     /**
22079     * fetch a Navigation Group based on the navigation ID
22080     * if one does not exist , it will get created.
22081     * @param {string} the navgroup to add
22082     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22083     */
22084     get: function(navId) {
22085         if (typeof(this.groups[navId]) == 'undefined') {
22086             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22087         }
22088         return this.groups[navId] ;
22089     }
22090     
22091     
22092     
22093 });
22094
22095  /*
22096  * - LGPL
22097  *
22098  * TabPanel
22099  * 
22100  */
22101
22102 /**
22103  * @class Roo.bootstrap.TabPanel
22104  * @extends Roo.bootstrap.Component
22105  * Bootstrap TabPanel class
22106  * @cfg {Boolean} active panel active
22107  * @cfg {String} html panel content
22108  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22109  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22110  * @cfg {String} href click to link..
22111  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22112  * 
22113  * 
22114  * @constructor
22115  * Create a new TabPanel
22116  * @param {Object} config The config object
22117  */
22118
22119 Roo.bootstrap.TabPanel = function(config){
22120     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22121     this.addEvents({
22122         /**
22123              * @event changed
22124              * Fires when the active status changes
22125              * @param {Roo.bootstrap.TabPanel} this
22126              * @param {Boolean} state the new state
22127             
22128          */
22129         'changed': true,
22130         /**
22131              * @event beforedeactivate
22132              * Fires before a tab is de-activated - can be used to do validation on a form.
22133              * @param {Roo.bootstrap.TabPanel} this
22134              * @return {Boolean} false if there is an error
22135             
22136          */
22137         'beforedeactivate': true
22138      });
22139     
22140     this.tabId = this.tabId || Roo.id();
22141   
22142 };
22143
22144 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22145     
22146     active: false,
22147     html: false,
22148     tabId: false,
22149     navId : false,
22150     href : '',
22151     touchSlide : false,
22152     getAutoCreate : function(){
22153         
22154         
22155         var cfg = {
22156             tag: 'div',
22157             // item is needed for carousel - not sure if it has any effect otherwise
22158             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22159             html: this.html || ''
22160         };
22161         
22162         if(this.active){
22163             cfg.cls += ' active';
22164         }
22165         
22166         if(this.tabId){
22167             cfg.tabId = this.tabId;
22168         }
22169         
22170         
22171         
22172         return cfg;
22173     },
22174     
22175     initEvents:  function()
22176     {
22177         var p = this.parent();
22178         
22179         this.navId = this.navId || p.navId;
22180         
22181         if (typeof(this.navId) != 'undefined') {
22182             // not really needed.. but just in case.. parent should be a NavGroup.
22183             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22184             
22185             tg.register(this);
22186             
22187             var i = tg.tabs.length - 1;
22188             
22189             if(this.active && tg.bullets > 0 && i < tg.bullets){
22190                 tg.setActiveBullet(i);
22191             }
22192         }
22193         
22194         this.el.on('click', this.onClick, this);
22195         
22196         if(Roo.isTouch && this.touchSlide){
22197             this.el.on("touchstart", this.onTouchStart, this);
22198             this.el.on("touchmove", this.onTouchMove, this);
22199             this.el.on("touchend", this.onTouchEnd, this);
22200         }
22201         
22202     },
22203     
22204     onRender : function(ct, position)
22205     {
22206         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22207     },
22208     
22209     setActive : function(state)
22210     {
22211         Roo.log("panel - set active " + this.tabId + "=" + state);
22212         
22213         this.active = state;
22214         if (!state) {
22215             this.el.removeClass('active');
22216             
22217         } else  if (!this.el.hasClass('active')) {
22218             this.el.addClass('active');
22219         }
22220         
22221         this.fireEvent('changed', this, state);
22222     },
22223     
22224     onClick : function(e)
22225     {
22226         e.preventDefault();
22227         
22228         if(!this.href.length){
22229             return;
22230         }
22231         
22232         window.location.href = this.href;
22233     },
22234     
22235     startX : 0,
22236     startY : 0,
22237     endX : 0,
22238     endY : 0,
22239     swiping : false,
22240     
22241     onTouchStart : function(e)
22242     {
22243         this.swiping = false;
22244         
22245         this.startX = e.browserEvent.touches[0].clientX;
22246         this.startY = e.browserEvent.touches[0].clientY;
22247     },
22248     
22249     onTouchMove : function(e)
22250     {
22251         this.swiping = true;
22252         
22253         this.endX = e.browserEvent.touches[0].clientX;
22254         this.endY = e.browserEvent.touches[0].clientY;
22255     },
22256     
22257     onTouchEnd : function(e)
22258     {
22259         if(!this.swiping){
22260             this.onClick(e);
22261             return;
22262         }
22263         
22264         var tabGroup = this.parent();
22265         
22266         if(this.endX > this.startX){ // swiping right
22267             tabGroup.showPanelPrev();
22268             return;
22269         }
22270         
22271         if(this.startX > this.endX){ // swiping left
22272             tabGroup.showPanelNext();
22273             return;
22274         }
22275     }
22276     
22277     
22278 });
22279  
22280
22281  
22282
22283  /*
22284  * - LGPL
22285  *
22286  * DateField
22287  * 
22288  */
22289
22290 /**
22291  * @class Roo.bootstrap.DateField
22292  * @extends Roo.bootstrap.Input
22293  * Bootstrap DateField class
22294  * @cfg {Number} weekStart default 0
22295  * @cfg {String} viewMode default empty, (months|years)
22296  * @cfg {String} minViewMode default empty, (months|years)
22297  * @cfg {Number} startDate default -Infinity
22298  * @cfg {Number} endDate default Infinity
22299  * @cfg {Boolean} todayHighlight default false
22300  * @cfg {Boolean} todayBtn default false
22301  * @cfg {Boolean} calendarWeeks default false
22302  * @cfg {Object} daysOfWeekDisabled default empty
22303  * @cfg {Boolean} singleMode default false (true | false)
22304  * 
22305  * @cfg {Boolean} keyboardNavigation default true
22306  * @cfg {String} language default en
22307  * 
22308  * @constructor
22309  * Create a new DateField
22310  * @param {Object} config The config object
22311  */
22312
22313 Roo.bootstrap.DateField = function(config){
22314     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22315      this.addEvents({
22316             /**
22317              * @event show
22318              * Fires when this field show.
22319              * @param {Roo.bootstrap.DateField} this
22320              * @param {Mixed} date The date value
22321              */
22322             show : true,
22323             /**
22324              * @event show
22325              * Fires when this field hide.
22326              * @param {Roo.bootstrap.DateField} this
22327              * @param {Mixed} date The date value
22328              */
22329             hide : true,
22330             /**
22331              * @event select
22332              * Fires when select a date.
22333              * @param {Roo.bootstrap.DateField} this
22334              * @param {Mixed} date The date value
22335              */
22336             select : true,
22337             /**
22338              * @event beforeselect
22339              * Fires when before select a date.
22340              * @param {Roo.bootstrap.DateField} this
22341              * @param {Mixed} date The date value
22342              */
22343             beforeselect : true
22344         });
22345 };
22346
22347 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22348     
22349     /**
22350      * @cfg {String} format
22351      * The default date format string which can be overriden for localization support.  The format must be
22352      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22353      */
22354     format : "m/d/y",
22355     /**
22356      * @cfg {String} altFormats
22357      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22358      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22359      */
22360     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22361     
22362     weekStart : 0,
22363     
22364     viewMode : '',
22365     
22366     minViewMode : '',
22367     
22368     todayHighlight : false,
22369     
22370     todayBtn: false,
22371     
22372     language: 'en',
22373     
22374     keyboardNavigation: true,
22375     
22376     calendarWeeks: false,
22377     
22378     startDate: -Infinity,
22379     
22380     endDate: Infinity,
22381     
22382     daysOfWeekDisabled: [],
22383     
22384     _events: [],
22385     
22386     singleMode : false,
22387     
22388     UTCDate: function()
22389     {
22390         return new Date(Date.UTC.apply(Date, arguments));
22391     },
22392     
22393     UTCToday: function()
22394     {
22395         var today = new Date();
22396         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22397     },
22398     
22399     getDate: function() {
22400             var d = this.getUTCDate();
22401             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22402     },
22403     
22404     getUTCDate: function() {
22405             return this.date;
22406     },
22407     
22408     setDate: function(d) {
22409             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22410     },
22411     
22412     setUTCDate: function(d) {
22413             this.date = d;
22414             this.setValue(this.formatDate(this.date));
22415     },
22416         
22417     onRender: function(ct, position)
22418     {
22419         
22420         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22421         
22422         this.language = this.language || 'en';
22423         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22424         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22425         
22426         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22427         this.format = this.format || 'm/d/y';
22428         this.isInline = false;
22429         this.isInput = true;
22430         this.component = this.el.select('.add-on', true).first() || false;
22431         this.component = (this.component && this.component.length === 0) ? false : this.component;
22432         this.hasInput = this.component && this.inputEl().length;
22433         
22434         if (typeof(this.minViewMode === 'string')) {
22435             switch (this.minViewMode) {
22436                 case 'months':
22437                     this.minViewMode = 1;
22438                     break;
22439                 case 'years':
22440                     this.minViewMode = 2;
22441                     break;
22442                 default:
22443                     this.minViewMode = 0;
22444                     break;
22445             }
22446         }
22447         
22448         if (typeof(this.viewMode === 'string')) {
22449             switch (this.viewMode) {
22450                 case 'months':
22451                     this.viewMode = 1;
22452                     break;
22453                 case 'years':
22454                     this.viewMode = 2;
22455                     break;
22456                 default:
22457                     this.viewMode = 0;
22458                     break;
22459             }
22460         }
22461                 
22462         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22463         
22464 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22465         
22466         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22467         
22468         this.picker().on('mousedown', this.onMousedown, this);
22469         this.picker().on('click', this.onClick, this);
22470         
22471         this.picker().addClass('datepicker-dropdown');
22472         
22473         this.startViewMode = this.viewMode;
22474         
22475         if(this.singleMode){
22476             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22477                 v.setVisibilityMode(Roo.Element.DISPLAY);
22478                 v.hide();
22479             });
22480             
22481             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22482                 v.setStyle('width', '189px');
22483             });
22484         }
22485         
22486         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22487             if(!this.calendarWeeks){
22488                 v.remove();
22489                 return;
22490             }
22491             
22492             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22493             v.attr('colspan', function(i, val){
22494                 return parseInt(val) + 1;
22495             });
22496         });
22497                         
22498         
22499         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22500         
22501         this.setStartDate(this.startDate);
22502         this.setEndDate(this.endDate);
22503         
22504         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22505         
22506         this.fillDow();
22507         this.fillMonths();
22508         this.update();
22509         this.showMode();
22510         
22511         if(this.isInline) {
22512             this.showPopup();
22513         }
22514     },
22515     
22516     picker : function()
22517     {
22518         return this.pickerEl;
22519 //        return this.el.select('.datepicker', true).first();
22520     },
22521     
22522     fillDow: function()
22523     {
22524         var dowCnt = this.weekStart;
22525         
22526         var dow = {
22527             tag: 'tr',
22528             cn: [
22529                 
22530             ]
22531         };
22532         
22533         if(this.calendarWeeks){
22534             dow.cn.push({
22535                 tag: 'th',
22536                 cls: 'cw',
22537                 html: '&nbsp;'
22538             })
22539         }
22540         
22541         while (dowCnt < this.weekStart + 7) {
22542             dow.cn.push({
22543                 tag: 'th',
22544                 cls: 'dow',
22545                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22546             });
22547         }
22548         
22549         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22550     },
22551     
22552     fillMonths: function()
22553     {    
22554         var i = 0;
22555         var months = this.picker().select('>.datepicker-months td', true).first();
22556         
22557         months.dom.innerHTML = '';
22558         
22559         while (i < 12) {
22560             var month = {
22561                 tag: 'span',
22562                 cls: 'month',
22563                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22564             };
22565             
22566             months.createChild(month);
22567         }
22568         
22569     },
22570     
22571     update: function()
22572     {
22573         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;
22574         
22575         if (this.date < this.startDate) {
22576             this.viewDate = new Date(this.startDate);
22577         } else if (this.date > this.endDate) {
22578             this.viewDate = new Date(this.endDate);
22579         } else {
22580             this.viewDate = new Date(this.date);
22581         }
22582         
22583         this.fill();
22584     },
22585     
22586     fill: function() 
22587     {
22588         var d = new Date(this.viewDate),
22589                 year = d.getUTCFullYear(),
22590                 month = d.getUTCMonth(),
22591                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22592                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22593                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22594                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22595                 currentDate = this.date && this.date.valueOf(),
22596                 today = this.UTCToday();
22597         
22598         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22599         
22600 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22601         
22602 //        this.picker.select('>tfoot th.today').
22603 //                                              .text(dates[this.language].today)
22604 //                                              .toggle(this.todayBtn !== false);
22605     
22606         this.updateNavArrows();
22607         this.fillMonths();
22608                                                 
22609         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22610         
22611         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22612          
22613         prevMonth.setUTCDate(day);
22614         
22615         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22616         
22617         var nextMonth = new Date(prevMonth);
22618         
22619         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22620         
22621         nextMonth = nextMonth.valueOf();
22622         
22623         var fillMonths = false;
22624         
22625         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22626         
22627         while(prevMonth.valueOf() <= nextMonth) {
22628             var clsName = '';
22629             
22630             if (prevMonth.getUTCDay() === this.weekStart) {
22631                 if(fillMonths){
22632                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22633                 }
22634                     
22635                 fillMonths = {
22636                     tag: 'tr',
22637                     cn: []
22638                 };
22639                 
22640                 if(this.calendarWeeks){
22641                     // ISO 8601: First week contains first thursday.
22642                     // ISO also states week starts on Monday, but we can be more abstract here.
22643                     var
22644                     // Start of current week: based on weekstart/current date
22645                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22646                     // Thursday of this week
22647                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22648                     // First Thursday of year, year from thursday
22649                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22650                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22651                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22652                     
22653                     fillMonths.cn.push({
22654                         tag: 'td',
22655                         cls: 'cw',
22656                         html: calWeek
22657                     });
22658                 }
22659             }
22660             
22661             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22662                 clsName += ' old';
22663             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22664                 clsName += ' new';
22665             }
22666             if (this.todayHighlight &&
22667                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22668                 prevMonth.getUTCMonth() == today.getMonth() &&
22669                 prevMonth.getUTCDate() == today.getDate()) {
22670                 clsName += ' today';
22671             }
22672             
22673             if (currentDate && prevMonth.valueOf() === currentDate) {
22674                 clsName += ' active';
22675             }
22676             
22677             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22678                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22679                     clsName += ' disabled';
22680             }
22681             
22682             fillMonths.cn.push({
22683                 tag: 'td',
22684                 cls: 'day ' + clsName,
22685                 html: prevMonth.getDate()
22686             });
22687             
22688             prevMonth.setDate(prevMonth.getDate()+1);
22689         }
22690           
22691         var currentYear = this.date && this.date.getUTCFullYear();
22692         var currentMonth = this.date && this.date.getUTCMonth();
22693         
22694         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22695         
22696         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22697             v.removeClass('active');
22698             
22699             if(currentYear === year && k === currentMonth){
22700                 v.addClass('active');
22701             }
22702             
22703             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22704                 v.addClass('disabled');
22705             }
22706             
22707         });
22708         
22709         
22710         year = parseInt(year/10, 10) * 10;
22711         
22712         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22713         
22714         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22715         
22716         year -= 1;
22717         for (var i = -1; i < 11; i++) {
22718             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22719                 tag: 'span',
22720                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22721                 html: year
22722             });
22723             
22724             year += 1;
22725         }
22726     },
22727     
22728     showMode: function(dir) 
22729     {
22730         if (dir) {
22731             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22732         }
22733         
22734         Roo.each(this.picker().select('>div',true).elements, function(v){
22735             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22736             v.hide();
22737         });
22738         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22739     },
22740     
22741     place: function()
22742     {
22743         if(this.isInline) {
22744             return;
22745         }
22746         
22747         this.picker().removeClass(['bottom', 'top']);
22748         
22749         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22750             /*
22751              * place to the top of element!
22752              *
22753              */
22754             
22755             this.picker().addClass('top');
22756             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22757             
22758             return;
22759         }
22760         
22761         this.picker().addClass('bottom');
22762         
22763         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22764     },
22765     
22766     parseDate : function(value)
22767     {
22768         if(!value || value instanceof Date){
22769             return value;
22770         }
22771         var v = Date.parseDate(value, this.format);
22772         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22773             v = Date.parseDate(value, 'Y-m-d');
22774         }
22775         if(!v && this.altFormats){
22776             if(!this.altFormatsArray){
22777                 this.altFormatsArray = this.altFormats.split("|");
22778             }
22779             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22780                 v = Date.parseDate(value, this.altFormatsArray[i]);
22781             }
22782         }
22783         return v;
22784     },
22785     
22786     formatDate : function(date, fmt)
22787     {   
22788         return (!date || !(date instanceof Date)) ?
22789         date : date.dateFormat(fmt || this.format);
22790     },
22791     
22792     onFocus : function()
22793     {
22794         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22795         this.showPopup();
22796     },
22797     
22798     onBlur : function()
22799     {
22800         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22801         
22802         var d = this.inputEl().getValue();
22803         
22804         this.setValue(d);
22805                 
22806         this.hidePopup();
22807     },
22808     
22809     showPopup : function()
22810     {
22811         this.picker().show();
22812         this.update();
22813         this.place();
22814         
22815         this.fireEvent('showpopup', this, this.date);
22816     },
22817     
22818     hidePopup : function()
22819     {
22820         if(this.isInline) {
22821             return;
22822         }
22823         this.picker().hide();
22824         this.viewMode = this.startViewMode;
22825         this.showMode();
22826         
22827         this.fireEvent('hidepopup', this, this.date);
22828         
22829     },
22830     
22831     onMousedown: function(e)
22832     {
22833         e.stopPropagation();
22834         e.preventDefault();
22835     },
22836     
22837     keyup: function(e)
22838     {
22839         Roo.bootstrap.DateField.superclass.keyup.call(this);
22840         this.update();
22841     },
22842
22843     setValue: function(v)
22844     {
22845         if(this.fireEvent('beforeselect', this, v) !== false){
22846             var d = new Date(this.parseDate(v) ).clearTime();
22847         
22848             if(isNaN(d.getTime())){
22849                 this.date = this.viewDate = '';
22850                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22851                 return;
22852             }
22853
22854             v = this.formatDate(d);
22855
22856             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22857
22858             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22859
22860             this.update();
22861
22862             this.fireEvent('select', this, this.date);
22863         }
22864     },
22865     
22866     getValue: function()
22867     {
22868         return this.formatDate(this.date);
22869     },
22870     
22871     fireKey: function(e)
22872     {
22873         if (!this.picker().isVisible()){
22874             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22875                 this.showPopup();
22876             }
22877             return;
22878         }
22879         
22880         var dateChanged = false,
22881         dir, day, month,
22882         newDate, newViewDate;
22883         
22884         switch(e.keyCode){
22885             case 27: // escape
22886                 this.hidePopup();
22887                 e.preventDefault();
22888                 break;
22889             case 37: // left
22890             case 39: // right
22891                 if (!this.keyboardNavigation) {
22892                     break;
22893                 }
22894                 dir = e.keyCode == 37 ? -1 : 1;
22895                 
22896                 if (e.ctrlKey){
22897                     newDate = this.moveYear(this.date, dir);
22898                     newViewDate = this.moveYear(this.viewDate, dir);
22899                 } else if (e.shiftKey){
22900                     newDate = this.moveMonth(this.date, dir);
22901                     newViewDate = this.moveMonth(this.viewDate, dir);
22902                 } else {
22903                     newDate = new Date(this.date);
22904                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22905                     newViewDate = new Date(this.viewDate);
22906                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22907                 }
22908                 if (this.dateWithinRange(newDate)){
22909                     this.date = newDate;
22910                     this.viewDate = newViewDate;
22911                     this.setValue(this.formatDate(this.date));
22912 //                    this.update();
22913                     e.preventDefault();
22914                     dateChanged = true;
22915                 }
22916                 break;
22917             case 38: // up
22918             case 40: // down
22919                 if (!this.keyboardNavigation) {
22920                     break;
22921                 }
22922                 dir = e.keyCode == 38 ? -1 : 1;
22923                 if (e.ctrlKey){
22924                     newDate = this.moveYear(this.date, dir);
22925                     newViewDate = this.moveYear(this.viewDate, dir);
22926                 } else if (e.shiftKey){
22927                     newDate = this.moveMonth(this.date, dir);
22928                     newViewDate = this.moveMonth(this.viewDate, dir);
22929                 } else {
22930                     newDate = new Date(this.date);
22931                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22932                     newViewDate = new Date(this.viewDate);
22933                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22934                 }
22935                 if (this.dateWithinRange(newDate)){
22936                     this.date = newDate;
22937                     this.viewDate = newViewDate;
22938                     this.setValue(this.formatDate(this.date));
22939 //                    this.update();
22940                     e.preventDefault();
22941                     dateChanged = true;
22942                 }
22943                 break;
22944             case 13: // enter
22945                 this.setValue(this.formatDate(this.date));
22946                 this.hidePopup();
22947                 e.preventDefault();
22948                 break;
22949             case 9: // tab
22950                 this.setValue(this.formatDate(this.date));
22951                 this.hidePopup();
22952                 break;
22953             case 16: // shift
22954             case 17: // ctrl
22955             case 18: // alt
22956                 break;
22957             default :
22958                 this.hidePopup();
22959                 
22960         }
22961     },
22962     
22963     
22964     onClick: function(e) 
22965     {
22966         e.stopPropagation();
22967         e.preventDefault();
22968         
22969         var target = e.getTarget();
22970         
22971         if(target.nodeName.toLowerCase() === 'i'){
22972             target = Roo.get(target).dom.parentNode;
22973         }
22974         
22975         var nodeName = target.nodeName;
22976         var className = target.className;
22977         var html = target.innerHTML;
22978         //Roo.log(nodeName);
22979         
22980         switch(nodeName.toLowerCase()) {
22981             case 'th':
22982                 switch(className) {
22983                     case 'switch':
22984                         this.showMode(1);
22985                         break;
22986                     case 'prev':
22987                     case 'next':
22988                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22989                         switch(this.viewMode){
22990                                 case 0:
22991                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22992                                         break;
22993                                 case 1:
22994                                 case 2:
22995                                         this.viewDate = this.moveYear(this.viewDate, dir);
22996                                         break;
22997                         }
22998                         this.fill();
22999                         break;
23000                     case 'today':
23001                         var date = new Date();
23002                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23003 //                        this.fill()
23004                         this.setValue(this.formatDate(this.date));
23005                         
23006                         this.hidePopup();
23007                         break;
23008                 }
23009                 break;
23010             case 'span':
23011                 if (className.indexOf('disabled') < 0) {
23012                 if (!this.viewDate) {
23013                     this.viewDate = new Date();
23014                 }
23015                 this.viewDate.setUTCDate(1);
23016                     if (className.indexOf('month') > -1) {
23017                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23018                     } else {
23019                         var year = parseInt(html, 10) || 0;
23020                         this.viewDate.setUTCFullYear(year);
23021                         
23022                     }
23023                     
23024                     if(this.singleMode){
23025                         this.setValue(this.formatDate(this.viewDate));
23026                         this.hidePopup();
23027                         return;
23028                     }
23029                     
23030                     this.showMode(-1);
23031                     this.fill();
23032                 }
23033                 break;
23034                 
23035             case 'td':
23036                 //Roo.log(className);
23037                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23038                     var day = parseInt(html, 10) || 1;
23039                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23040                         month = (this.viewDate || new Date()).getUTCMonth();
23041
23042                     if (className.indexOf('old') > -1) {
23043                         if(month === 0 ){
23044                             month = 11;
23045                             year -= 1;
23046                         }else{
23047                             month -= 1;
23048                         }
23049                     } else if (className.indexOf('new') > -1) {
23050                         if (month == 11) {
23051                             month = 0;
23052                             year += 1;
23053                         } else {
23054                             month += 1;
23055                         }
23056                     }
23057                     //Roo.log([year,month,day]);
23058                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23059                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23060 //                    this.fill();
23061                     //Roo.log(this.formatDate(this.date));
23062                     this.setValue(this.formatDate(this.date));
23063                     this.hidePopup();
23064                 }
23065                 break;
23066         }
23067     },
23068     
23069     setStartDate: function(startDate)
23070     {
23071         this.startDate = startDate || -Infinity;
23072         if (this.startDate !== -Infinity) {
23073             this.startDate = this.parseDate(this.startDate);
23074         }
23075         this.update();
23076         this.updateNavArrows();
23077     },
23078
23079     setEndDate: function(endDate)
23080     {
23081         this.endDate = endDate || Infinity;
23082         if (this.endDate !== Infinity) {
23083             this.endDate = this.parseDate(this.endDate);
23084         }
23085         this.update();
23086         this.updateNavArrows();
23087     },
23088     
23089     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23090     {
23091         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23092         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23093             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23094         }
23095         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23096             return parseInt(d, 10);
23097         });
23098         this.update();
23099         this.updateNavArrows();
23100     },
23101     
23102     updateNavArrows: function() 
23103     {
23104         if(this.singleMode){
23105             return;
23106         }
23107         
23108         var d = new Date(this.viewDate),
23109         year = d.getUTCFullYear(),
23110         month = d.getUTCMonth();
23111         
23112         Roo.each(this.picker().select('.prev', true).elements, function(v){
23113             v.show();
23114             switch (this.viewMode) {
23115                 case 0:
23116
23117                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23118                         v.hide();
23119                     }
23120                     break;
23121                 case 1:
23122                 case 2:
23123                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23124                         v.hide();
23125                     }
23126                     break;
23127             }
23128         });
23129         
23130         Roo.each(this.picker().select('.next', true).elements, function(v){
23131             v.show();
23132             switch (this.viewMode) {
23133                 case 0:
23134
23135                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23136                         v.hide();
23137                     }
23138                     break;
23139                 case 1:
23140                 case 2:
23141                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23142                         v.hide();
23143                     }
23144                     break;
23145             }
23146         })
23147     },
23148     
23149     moveMonth: function(date, dir)
23150     {
23151         if (!dir) {
23152             return date;
23153         }
23154         var new_date = new Date(date.valueOf()),
23155         day = new_date.getUTCDate(),
23156         month = new_date.getUTCMonth(),
23157         mag = Math.abs(dir),
23158         new_month, test;
23159         dir = dir > 0 ? 1 : -1;
23160         if (mag == 1){
23161             test = dir == -1
23162             // If going back one month, make sure month is not current month
23163             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23164             ? function(){
23165                 return new_date.getUTCMonth() == month;
23166             }
23167             // If going forward one month, make sure month is as expected
23168             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23169             : function(){
23170                 return new_date.getUTCMonth() != new_month;
23171             };
23172             new_month = month + dir;
23173             new_date.setUTCMonth(new_month);
23174             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23175             if (new_month < 0 || new_month > 11) {
23176                 new_month = (new_month + 12) % 12;
23177             }
23178         } else {
23179             // For magnitudes >1, move one month at a time...
23180             for (var i=0; i<mag; i++) {
23181                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23182                 new_date = this.moveMonth(new_date, dir);
23183             }
23184             // ...then reset the day, keeping it in the new month
23185             new_month = new_date.getUTCMonth();
23186             new_date.setUTCDate(day);
23187             test = function(){
23188                 return new_month != new_date.getUTCMonth();
23189             };
23190         }
23191         // Common date-resetting loop -- if date is beyond end of month, make it
23192         // end of month
23193         while (test()){
23194             new_date.setUTCDate(--day);
23195             new_date.setUTCMonth(new_month);
23196         }
23197         return new_date;
23198     },
23199
23200     moveYear: function(date, dir)
23201     {
23202         return this.moveMonth(date, dir*12);
23203     },
23204
23205     dateWithinRange: function(date)
23206     {
23207         return date >= this.startDate && date <= this.endDate;
23208     },
23209
23210     
23211     remove: function() 
23212     {
23213         this.picker().remove();
23214     },
23215     
23216     validateValue : function(value)
23217     {
23218         if(this.getVisibilityEl().hasClass('hidden')){
23219             return true;
23220         }
23221         
23222         if(value.length < 1)  {
23223             if(this.allowBlank){
23224                 return true;
23225             }
23226             return false;
23227         }
23228         
23229         if(value.length < this.minLength){
23230             return false;
23231         }
23232         if(value.length > this.maxLength){
23233             return false;
23234         }
23235         if(this.vtype){
23236             var vt = Roo.form.VTypes;
23237             if(!vt[this.vtype](value, this)){
23238                 return false;
23239             }
23240         }
23241         if(typeof this.validator == "function"){
23242             var msg = this.validator(value);
23243             if(msg !== true){
23244                 return false;
23245             }
23246         }
23247         
23248         if(this.regex && !this.regex.test(value)){
23249             return false;
23250         }
23251         
23252         if(typeof(this.parseDate(value)) == 'undefined'){
23253             return false;
23254         }
23255         
23256         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23257             return false;
23258         }      
23259         
23260         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23261             return false;
23262         } 
23263         
23264         
23265         return true;
23266     },
23267     
23268     reset : function()
23269     {
23270         this.date = this.viewDate = '';
23271         
23272         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23273     }
23274    
23275 });
23276
23277 Roo.apply(Roo.bootstrap.DateField,  {
23278     
23279     head : {
23280         tag: 'thead',
23281         cn: [
23282         {
23283             tag: 'tr',
23284             cn: [
23285             {
23286                 tag: 'th',
23287                 cls: 'prev',
23288                 html: '<i class="fa fa-arrow-left"/>'
23289             },
23290             {
23291                 tag: 'th',
23292                 cls: 'switch',
23293                 colspan: '5'
23294             },
23295             {
23296                 tag: 'th',
23297                 cls: 'next',
23298                 html: '<i class="fa fa-arrow-right"/>'
23299             }
23300
23301             ]
23302         }
23303         ]
23304     },
23305     
23306     content : {
23307         tag: 'tbody',
23308         cn: [
23309         {
23310             tag: 'tr',
23311             cn: [
23312             {
23313                 tag: 'td',
23314                 colspan: '7'
23315             }
23316             ]
23317         }
23318         ]
23319     },
23320     
23321     footer : {
23322         tag: 'tfoot',
23323         cn: [
23324         {
23325             tag: 'tr',
23326             cn: [
23327             {
23328                 tag: 'th',
23329                 colspan: '7',
23330                 cls: 'today'
23331             }
23332                     
23333             ]
23334         }
23335         ]
23336     },
23337     
23338     dates:{
23339         en: {
23340             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23341             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23342             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23343             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23344             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23345             today: "Today"
23346         }
23347     },
23348     
23349     modes: [
23350     {
23351         clsName: 'days',
23352         navFnc: 'Month',
23353         navStep: 1
23354     },
23355     {
23356         clsName: 'months',
23357         navFnc: 'FullYear',
23358         navStep: 1
23359     },
23360     {
23361         clsName: 'years',
23362         navFnc: 'FullYear',
23363         navStep: 10
23364     }]
23365 });
23366
23367 Roo.apply(Roo.bootstrap.DateField,  {
23368   
23369     template : {
23370         tag: 'div',
23371         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23372         cn: [
23373         {
23374             tag: 'div',
23375             cls: 'datepicker-days',
23376             cn: [
23377             {
23378                 tag: 'table',
23379                 cls: 'table-condensed',
23380                 cn:[
23381                 Roo.bootstrap.DateField.head,
23382                 {
23383                     tag: 'tbody'
23384                 },
23385                 Roo.bootstrap.DateField.footer
23386                 ]
23387             }
23388             ]
23389         },
23390         {
23391             tag: 'div',
23392             cls: 'datepicker-months',
23393             cn: [
23394             {
23395                 tag: 'table',
23396                 cls: 'table-condensed',
23397                 cn:[
23398                 Roo.bootstrap.DateField.head,
23399                 Roo.bootstrap.DateField.content,
23400                 Roo.bootstrap.DateField.footer
23401                 ]
23402             }
23403             ]
23404         },
23405         {
23406             tag: 'div',
23407             cls: 'datepicker-years',
23408             cn: [
23409             {
23410                 tag: 'table',
23411                 cls: 'table-condensed',
23412                 cn:[
23413                 Roo.bootstrap.DateField.head,
23414                 Roo.bootstrap.DateField.content,
23415                 Roo.bootstrap.DateField.footer
23416                 ]
23417             }
23418             ]
23419         }
23420         ]
23421     }
23422 });
23423
23424  
23425
23426  /*
23427  * - LGPL
23428  *
23429  * TimeField
23430  * 
23431  */
23432
23433 /**
23434  * @class Roo.bootstrap.TimeField
23435  * @extends Roo.bootstrap.Input
23436  * Bootstrap DateField class
23437  * 
23438  * 
23439  * @constructor
23440  * Create a new TimeField
23441  * @param {Object} config The config object
23442  */
23443
23444 Roo.bootstrap.TimeField = function(config){
23445     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23446     this.addEvents({
23447             /**
23448              * @event show
23449              * Fires when this field show.
23450              * @param {Roo.bootstrap.DateField} thisthis
23451              * @param {Mixed} date The date value
23452              */
23453             show : true,
23454             /**
23455              * @event show
23456              * Fires when this field hide.
23457              * @param {Roo.bootstrap.DateField} this
23458              * @param {Mixed} date The date value
23459              */
23460             hide : true,
23461             /**
23462              * @event select
23463              * Fires when select a date.
23464              * @param {Roo.bootstrap.DateField} this
23465              * @param {Mixed} date The date value
23466              */
23467             select : true
23468         });
23469 };
23470
23471 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23472     
23473     /**
23474      * @cfg {String} format
23475      * The default time format string which can be overriden for localization support.  The format must be
23476      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23477      */
23478     format : "H:i",
23479
23480     getAutoCreate : function()
23481     {
23482         this.after = '<i class="fa far fa-clock"></i>';
23483         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23484         
23485          
23486     },
23487     onRender: function(ct, position)
23488     {
23489         
23490         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23491                 
23492         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23493         
23494         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23495         
23496         this.pop = this.picker().select('>.datepicker-time',true).first();
23497         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23498         
23499         this.picker().on('mousedown', this.onMousedown, this);
23500         this.picker().on('click', this.onClick, this);
23501         
23502         this.picker().addClass('datepicker-dropdown');
23503     
23504         this.fillTime();
23505         this.update();
23506             
23507         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23508         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23509         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23510         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23511         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23512         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23513
23514     },
23515     
23516     fireKey: function(e){
23517         if (!this.picker().isVisible()){
23518             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23519                 this.show();
23520             }
23521             return;
23522         }
23523
23524         e.preventDefault();
23525         
23526         switch(e.keyCode){
23527             case 27: // escape
23528                 this.hide();
23529                 break;
23530             case 37: // left
23531             case 39: // right
23532                 this.onTogglePeriod();
23533                 break;
23534             case 38: // up
23535                 this.onIncrementMinutes();
23536                 break;
23537             case 40: // down
23538                 this.onDecrementMinutes();
23539                 break;
23540             case 13: // enter
23541             case 9: // tab
23542                 this.setTime();
23543                 break;
23544         }
23545     },
23546     
23547     onClick: function(e) {
23548         e.stopPropagation();
23549         e.preventDefault();
23550     },
23551     
23552     picker : function()
23553     {
23554         return this.pickerEl;
23555     },
23556     
23557     fillTime: function()
23558     {    
23559         var time = this.pop.select('tbody', true).first();
23560         
23561         time.dom.innerHTML = '';
23562         
23563         time.createChild({
23564             tag: 'tr',
23565             cn: [
23566                 {
23567                     tag: 'td',
23568                     cn: [
23569                         {
23570                             tag: 'a',
23571                             href: '#',
23572                             cls: 'btn',
23573                             cn: [
23574                                 {
23575                                     tag: 'i',
23576                                     cls: 'hours-up fa fas fa-chevron-up'
23577                                 }
23578                             ]
23579                         } 
23580                     ]
23581                 },
23582                 {
23583                     tag: 'td',
23584                     cls: 'separator'
23585                 },
23586                 {
23587                     tag: 'td',
23588                     cn: [
23589                         {
23590                             tag: 'a',
23591                             href: '#',
23592                             cls: 'btn',
23593                             cn: [
23594                                 {
23595                                     tag: 'i',
23596                                     cls: 'minutes-up fa fas fa-chevron-up'
23597                                 }
23598                             ]
23599                         }
23600                     ]
23601                 },
23602                 {
23603                     tag: 'td',
23604                     cls: 'separator'
23605                 }
23606             ]
23607         });
23608         
23609         time.createChild({
23610             tag: 'tr',
23611             cn: [
23612                 {
23613                     tag: 'td',
23614                     cn: [
23615                         {
23616                             tag: 'span',
23617                             cls: 'timepicker-hour',
23618                             html: '00'
23619                         }  
23620                     ]
23621                 },
23622                 {
23623                     tag: 'td',
23624                     cls: 'separator',
23625                     html: ':'
23626                 },
23627                 {
23628                     tag: 'td',
23629                     cn: [
23630                         {
23631                             tag: 'span',
23632                             cls: 'timepicker-minute',
23633                             html: '00'
23634                         }  
23635                     ]
23636                 },
23637                 {
23638                     tag: 'td',
23639                     cls: 'separator'
23640                 },
23641                 {
23642                     tag: 'td',
23643                     cn: [
23644                         {
23645                             tag: 'button',
23646                             type: 'button',
23647                             cls: 'btn btn-primary period',
23648                             html: 'AM'
23649                             
23650                         }
23651                     ]
23652                 }
23653             ]
23654         });
23655         
23656         time.createChild({
23657             tag: 'tr',
23658             cn: [
23659                 {
23660                     tag: 'td',
23661                     cn: [
23662                         {
23663                             tag: 'a',
23664                             href: '#',
23665                             cls: 'btn',
23666                             cn: [
23667                                 {
23668                                     tag: 'span',
23669                                     cls: 'hours-down fa fas fa-chevron-down'
23670                                 }
23671                             ]
23672                         }
23673                     ]
23674                 },
23675                 {
23676                     tag: 'td',
23677                     cls: 'separator'
23678                 },
23679                 {
23680                     tag: 'td',
23681                     cn: [
23682                         {
23683                             tag: 'a',
23684                             href: '#',
23685                             cls: 'btn',
23686                             cn: [
23687                                 {
23688                                     tag: 'span',
23689                                     cls: 'minutes-down fa fas fa-chevron-down'
23690                                 }
23691                             ]
23692                         }
23693                     ]
23694                 },
23695                 {
23696                     tag: 'td',
23697                     cls: 'separator'
23698                 }
23699             ]
23700         });
23701         
23702     },
23703     
23704     update: function()
23705     {
23706         
23707         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23708         
23709         this.fill();
23710     },
23711     
23712     fill: function() 
23713     {
23714         var hours = this.time.getHours();
23715         var minutes = this.time.getMinutes();
23716         var period = 'AM';
23717         
23718         if(hours > 11){
23719             period = 'PM';
23720         }
23721         
23722         if(hours == 0){
23723             hours = 12;
23724         }
23725         
23726         
23727         if(hours > 12){
23728             hours = hours - 12;
23729         }
23730         
23731         if(hours < 10){
23732             hours = '0' + hours;
23733         }
23734         
23735         if(minutes < 10){
23736             minutes = '0' + minutes;
23737         }
23738         
23739         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23740         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23741         this.pop.select('button', true).first().dom.innerHTML = period;
23742         
23743     },
23744     
23745     place: function()
23746     {   
23747         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23748         
23749         var cls = ['bottom'];
23750         
23751         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23752             cls.pop();
23753             cls.push('top');
23754         }
23755         
23756         cls.push('right');
23757         
23758         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23759             cls.pop();
23760             cls.push('left');
23761         }
23762         //this.picker().setXY(20000,20000);
23763         this.picker().addClass(cls.join('-'));
23764         
23765         var _this = this;
23766         
23767         Roo.each(cls, function(c){
23768             if(c == 'bottom'){
23769                 (function() {
23770                  //  
23771                 }).defer(200);
23772                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23773                 //_this.picker().setTop(_this.inputEl().getHeight());
23774                 return;
23775             }
23776             if(c == 'top'){
23777                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23778                 
23779                 //_this.picker().setTop(0 - _this.picker().getHeight());
23780                 return;
23781             }
23782             /*
23783             if(c == 'left'){
23784                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23785                 return;
23786             }
23787             if(c == 'right'){
23788                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23789                 return;
23790             }
23791             */
23792         });
23793         
23794     },
23795   
23796     onFocus : function()
23797     {
23798         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23799         this.show();
23800     },
23801     
23802     onBlur : function()
23803     {
23804         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23805         this.hide();
23806     },
23807     
23808     show : function()
23809     {
23810         this.picker().show();
23811         this.pop.show();
23812         this.update();
23813         this.place();
23814         
23815         this.fireEvent('show', this, this.date);
23816     },
23817     
23818     hide : function()
23819     {
23820         this.picker().hide();
23821         this.pop.hide();
23822         
23823         this.fireEvent('hide', this, this.date);
23824     },
23825     
23826     setTime : function()
23827     {
23828         this.hide();
23829         this.setValue(this.time.format(this.format));
23830         
23831         this.fireEvent('select', this, this.date);
23832         
23833         
23834     },
23835     
23836     onMousedown: function(e){
23837         e.stopPropagation();
23838         e.preventDefault();
23839     },
23840     
23841     onIncrementHours: function()
23842     {
23843         Roo.log('onIncrementHours');
23844         this.time = this.time.add(Date.HOUR, 1);
23845         this.update();
23846         
23847     },
23848     
23849     onDecrementHours: function()
23850     {
23851         Roo.log('onDecrementHours');
23852         this.time = this.time.add(Date.HOUR, -1);
23853         this.update();
23854     },
23855     
23856     onIncrementMinutes: function()
23857     {
23858         Roo.log('onIncrementMinutes');
23859         this.time = this.time.add(Date.MINUTE, 1);
23860         this.update();
23861     },
23862     
23863     onDecrementMinutes: function()
23864     {
23865         Roo.log('onDecrementMinutes');
23866         this.time = this.time.add(Date.MINUTE, -1);
23867         this.update();
23868     },
23869     
23870     onTogglePeriod: function()
23871     {
23872         Roo.log('onTogglePeriod');
23873         this.time = this.time.add(Date.HOUR, 12);
23874         this.update();
23875     }
23876     
23877    
23878 });
23879  
23880
23881 Roo.apply(Roo.bootstrap.TimeField,  {
23882   
23883     template : {
23884         tag: 'div',
23885         cls: 'datepicker dropdown-menu',
23886         cn: [
23887             {
23888                 tag: 'div',
23889                 cls: 'datepicker-time',
23890                 cn: [
23891                 {
23892                     tag: 'table',
23893                     cls: 'table-condensed',
23894                     cn:[
23895                         {
23896                             tag: 'tbody',
23897                             cn: [
23898                                 {
23899                                     tag: 'tr',
23900                                     cn: [
23901                                     {
23902                                         tag: 'td',
23903                                         colspan: '7'
23904                                     }
23905                                     ]
23906                                 }
23907                             ]
23908                         },
23909                         {
23910                             tag: 'tfoot',
23911                             cn: [
23912                                 {
23913                                     tag: 'tr',
23914                                     cn: [
23915                                     {
23916                                         tag: 'th',
23917                                         colspan: '7',
23918                                         cls: '',
23919                                         cn: [
23920                                             {
23921                                                 tag: 'button',
23922                                                 cls: 'btn btn-info ok',
23923                                                 html: 'OK'
23924                                             }
23925                                         ]
23926                                     }
23927                     
23928                                     ]
23929                                 }
23930                             ]
23931                         }
23932                     ]
23933                 }
23934                 ]
23935             }
23936         ]
23937     }
23938 });
23939
23940  
23941
23942  /*
23943  * - LGPL
23944  *
23945  * MonthField
23946  * 
23947  */
23948
23949 /**
23950  * @class Roo.bootstrap.MonthField
23951  * @extends Roo.bootstrap.Input
23952  * Bootstrap MonthField class
23953  * 
23954  * @cfg {String} language default en
23955  * 
23956  * @constructor
23957  * Create a new MonthField
23958  * @param {Object} config The config object
23959  */
23960
23961 Roo.bootstrap.MonthField = function(config){
23962     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23963     
23964     this.addEvents({
23965         /**
23966          * @event show
23967          * Fires when this field show.
23968          * @param {Roo.bootstrap.MonthField} this
23969          * @param {Mixed} date The date value
23970          */
23971         show : true,
23972         /**
23973          * @event show
23974          * Fires when this field hide.
23975          * @param {Roo.bootstrap.MonthField} this
23976          * @param {Mixed} date The date value
23977          */
23978         hide : true,
23979         /**
23980          * @event select
23981          * Fires when select a date.
23982          * @param {Roo.bootstrap.MonthField} this
23983          * @param {String} oldvalue The old value
23984          * @param {String} newvalue The new value
23985          */
23986         select : true
23987     });
23988 };
23989
23990 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23991     
23992     onRender: function(ct, position)
23993     {
23994         
23995         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23996         
23997         this.language = this.language || 'en';
23998         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23999         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24000         
24001         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24002         this.isInline = false;
24003         this.isInput = true;
24004         this.component = this.el.select('.add-on', true).first() || false;
24005         this.component = (this.component && this.component.length === 0) ? false : this.component;
24006         this.hasInput = this.component && this.inputEL().length;
24007         
24008         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24009         
24010         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24011         
24012         this.picker().on('mousedown', this.onMousedown, this);
24013         this.picker().on('click', this.onClick, this);
24014         
24015         this.picker().addClass('datepicker-dropdown');
24016         
24017         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24018             v.setStyle('width', '189px');
24019         });
24020         
24021         this.fillMonths();
24022         
24023         this.update();
24024         
24025         if(this.isInline) {
24026             this.show();
24027         }
24028         
24029     },
24030     
24031     setValue: function(v, suppressEvent)
24032     {   
24033         var o = this.getValue();
24034         
24035         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24036         
24037         this.update();
24038
24039         if(suppressEvent !== true){
24040             this.fireEvent('select', this, o, v);
24041         }
24042         
24043     },
24044     
24045     getValue: function()
24046     {
24047         return this.value;
24048     },
24049     
24050     onClick: function(e) 
24051     {
24052         e.stopPropagation();
24053         e.preventDefault();
24054         
24055         var target = e.getTarget();
24056         
24057         if(target.nodeName.toLowerCase() === 'i'){
24058             target = Roo.get(target).dom.parentNode;
24059         }
24060         
24061         var nodeName = target.nodeName;
24062         var className = target.className;
24063         var html = target.innerHTML;
24064         
24065         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24066             return;
24067         }
24068         
24069         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24070         
24071         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24072         
24073         this.hide();
24074                         
24075     },
24076     
24077     picker : function()
24078     {
24079         return this.pickerEl;
24080     },
24081     
24082     fillMonths: function()
24083     {    
24084         var i = 0;
24085         var months = this.picker().select('>.datepicker-months td', true).first();
24086         
24087         months.dom.innerHTML = '';
24088         
24089         while (i < 12) {
24090             var month = {
24091                 tag: 'span',
24092                 cls: 'month',
24093                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24094             };
24095             
24096             months.createChild(month);
24097         }
24098         
24099     },
24100     
24101     update: function()
24102     {
24103         var _this = this;
24104         
24105         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24106             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24107         }
24108         
24109         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24110             e.removeClass('active');
24111             
24112             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24113                 e.addClass('active');
24114             }
24115         })
24116     },
24117     
24118     place: function()
24119     {
24120         if(this.isInline) {
24121             return;
24122         }
24123         
24124         this.picker().removeClass(['bottom', 'top']);
24125         
24126         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24127             /*
24128              * place to the top of element!
24129              *
24130              */
24131             
24132             this.picker().addClass('top');
24133             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24134             
24135             return;
24136         }
24137         
24138         this.picker().addClass('bottom');
24139         
24140         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24141     },
24142     
24143     onFocus : function()
24144     {
24145         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24146         this.show();
24147     },
24148     
24149     onBlur : function()
24150     {
24151         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24152         
24153         var d = this.inputEl().getValue();
24154         
24155         this.setValue(d);
24156                 
24157         this.hide();
24158     },
24159     
24160     show : function()
24161     {
24162         this.picker().show();
24163         this.picker().select('>.datepicker-months', true).first().show();
24164         this.update();
24165         this.place();
24166         
24167         this.fireEvent('show', this, this.date);
24168     },
24169     
24170     hide : function()
24171     {
24172         if(this.isInline) {
24173             return;
24174         }
24175         this.picker().hide();
24176         this.fireEvent('hide', this, this.date);
24177         
24178     },
24179     
24180     onMousedown: function(e)
24181     {
24182         e.stopPropagation();
24183         e.preventDefault();
24184     },
24185     
24186     keyup: function(e)
24187     {
24188         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24189         this.update();
24190     },
24191
24192     fireKey: function(e)
24193     {
24194         if (!this.picker().isVisible()){
24195             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24196                 this.show();
24197             }
24198             return;
24199         }
24200         
24201         var dir;
24202         
24203         switch(e.keyCode){
24204             case 27: // escape
24205                 this.hide();
24206                 e.preventDefault();
24207                 break;
24208             case 37: // left
24209             case 39: // right
24210                 dir = e.keyCode == 37 ? -1 : 1;
24211                 
24212                 this.vIndex = this.vIndex + dir;
24213                 
24214                 if(this.vIndex < 0){
24215                     this.vIndex = 0;
24216                 }
24217                 
24218                 if(this.vIndex > 11){
24219                     this.vIndex = 11;
24220                 }
24221                 
24222                 if(isNaN(this.vIndex)){
24223                     this.vIndex = 0;
24224                 }
24225                 
24226                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24227                 
24228                 break;
24229             case 38: // up
24230             case 40: // down
24231                 
24232                 dir = e.keyCode == 38 ? -1 : 1;
24233                 
24234                 this.vIndex = this.vIndex + dir * 4;
24235                 
24236                 if(this.vIndex < 0){
24237                     this.vIndex = 0;
24238                 }
24239                 
24240                 if(this.vIndex > 11){
24241                     this.vIndex = 11;
24242                 }
24243                 
24244                 if(isNaN(this.vIndex)){
24245                     this.vIndex = 0;
24246                 }
24247                 
24248                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24249                 break;
24250                 
24251             case 13: // enter
24252                 
24253                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24254                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24255                 }
24256                 
24257                 this.hide();
24258                 e.preventDefault();
24259                 break;
24260             case 9: // tab
24261                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24262                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24263                 }
24264                 this.hide();
24265                 break;
24266             case 16: // shift
24267             case 17: // ctrl
24268             case 18: // alt
24269                 break;
24270             default :
24271                 this.hide();
24272                 
24273         }
24274     },
24275     
24276     remove: function() 
24277     {
24278         this.picker().remove();
24279     }
24280    
24281 });
24282
24283 Roo.apply(Roo.bootstrap.MonthField,  {
24284     
24285     content : {
24286         tag: 'tbody',
24287         cn: [
24288         {
24289             tag: 'tr',
24290             cn: [
24291             {
24292                 tag: 'td',
24293                 colspan: '7'
24294             }
24295             ]
24296         }
24297         ]
24298     },
24299     
24300     dates:{
24301         en: {
24302             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24303             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24304         }
24305     }
24306 });
24307
24308 Roo.apply(Roo.bootstrap.MonthField,  {
24309   
24310     template : {
24311         tag: 'div',
24312         cls: 'datepicker dropdown-menu roo-dynamic',
24313         cn: [
24314             {
24315                 tag: 'div',
24316                 cls: 'datepicker-months',
24317                 cn: [
24318                 {
24319                     tag: 'table',
24320                     cls: 'table-condensed',
24321                     cn:[
24322                         Roo.bootstrap.DateField.content
24323                     ]
24324                 }
24325                 ]
24326             }
24327         ]
24328     }
24329 });
24330
24331  
24332
24333  
24334  /*
24335  * - LGPL
24336  *
24337  * CheckBox
24338  * 
24339  */
24340
24341 /**
24342  * @class Roo.bootstrap.CheckBox
24343  * @extends Roo.bootstrap.Input
24344  * Bootstrap CheckBox class
24345  * 
24346  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24347  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24348  * @cfg {String} boxLabel The text that appears beside the checkbox
24349  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24350  * @cfg {Boolean} checked initnal the element
24351  * @cfg {Boolean} inline inline the element (default false)
24352  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24353  * @cfg {String} tooltip label tooltip
24354  * 
24355  * @constructor
24356  * Create a new CheckBox
24357  * @param {Object} config The config object
24358  */
24359
24360 Roo.bootstrap.CheckBox = function(config){
24361     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24362    
24363     this.addEvents({
24364         /**
24365         * @event check
24366         * Fires when the element is checked or unchecked.
24367         * @param {Roo.bootstrap.CheckBox} this This input
24368         * @param {Boolean} checked The new checked value
24369         */
24370        check : true,
24371        /**
24372         * @event click
24373         * Fires when the element is click.
24374         * @param {Roo.bootstrap.CheckBox} this This input
24375         */
24376        click : true
24377     });
24378     
24379 };
24380
24381 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24382   
24383     inputType: 'checkbox',
24384     inputValue: 1,
24385     valueOff: 0,
24386     boxLabel: false,
24387     checked: false,
24388     weight : false,
24389     inline: false,
24390     tooltip : '',
24391     
24392     // checkbox success does not make any sense really.. 
24393     invalidClass : "",
24394     validClass : "",
24395     
24396     
24397     getAutoCreate : function()
24398     {
24399         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24400         
24401         var id = Roo.id();
24402         
24403         var cfg = {};
24404         
24405         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24406         
24407         if(this.inline){
24408             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24409         }
24410         
24411         var input =  {
24412             tag: 'input',
24413             id : id,
24414             type : this.inputType,
24415             value : this.inputValue,
24416             cls : 'roo-' + this.inputType, //'form-box',
24417             placeholder : this.placeholder || ''
24418             
24419         };
24420         
24421         if(this.inputType != 'radio'){
24422             var hidden =  {
24423                 tag: 'input',
24424                 type : 'hidden',
24425                 cls : 'roo-hidden-value',
24426                 value : this.checked ? this.inputValue : this.valueOff
24427             };
24428         }
24429         
24430             
24431         if (this.weight) { // Validity check?
24432             cfg.cls += " " + this.inputType + "-" + this.weight;
24433         }
24434         
24435         if (this.disabled) {
24436             input.disabled=true;
24437         }
24438         
24439         if(this.checked){
24440             input.checked = this.checked;
24441         }
24442         
24443         if (this.name) {
24444             
24445             input.name = this.name;
24446             
24447             if(this.inputType != 'radio'){
24448                 hidden.name = this.name;
24449                 input.name = '_hidden_' + this.name;
24450             }
24451         }
24452         
24453         if (this.size) {
24454             input.cls += ' input-' + this.size;
24455         }
24456         
24457         var settings=this;
24458         
24459         ['xs','sm','md','lg'].map(function(size){
24460             if (settings[size]) {
24461                 cfg.cls += ' col-' + size + '-' + settings[size];
24462             }
24463         });
24464         
24465         var inputblock = input;
24466          
24467         if (this.before || this.after) {
24468             
24469             inputblock = {
24470                 cls : 'input-group',
24471                 cn :  [] 
24472             };
24473             
24474             if (this.before) {
24475                 inputblock.cn.push({
24476                     tag :'span',
24477                     cls : 'input-group-addon',
24478                     html : this.before
24479                 });
24480             }
24481             
24482             inputblock.cn.push(input);
24483             
24484             if(this.inputType != 'radio'){
24485                 inputblock.cn.push(hidden);
24486             }
24487             
24488             if (this.after) {
24489                 inputblock.cn.push({
24490                     tag :'span',
24491                     cls : 'input-group-addon',
24492                     html : this.after
24493                 });
24494             }
24495             
24496         }
24497         var boxLabelCfg = false;
24498         
24499         if(this.boxLabel){
24500            
24501             boxLabelCfg = {
24502                 tag: 'label',
24503                 //'for': id, // box label is handled by onclick - so no for...
24504                 cls: 'box-label',
24505                 html: this.boxLabel
24506             };
24507             if(this.tooltip){
24508                 boxLabelCfg.tooltip = this.tooltip;
24509             }
24510              
24511         }
24512         
24513         
24514         if (align ==='left' && this.fieldLabel.length) {
24515 //                Roo.log("left and has label");
24516             cfg.cn = [
24517                 {
24518                     tag: 'label',
24519                     'for' :  id,
24520                     cls : 'control-label',
24521                     html : this.fieldLabel
24522                 },
24523                 {
24524                     cls : "", 
24525                     cn: [
24526                         inputblock
24527                     ]
24528                 }
24529             ];
24530             
24531             if (boxLabelCfg) {
24532                 cfg.cn[1].cn.push(boxLabelCfg);
24533             }
24534             
24535             if(this.labelWidth > 12){
24536                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24537             }
24538             
24539             if(this.labelWidth < 13 && this.labelmd == 0){
24540                 this.labelmd = this.labelWidth;
24541             }
24542             
24543             if(this.labellg > 0){
24544                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24545                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24546             }
24547             
24548             if(this.labelmd > 0){
24549                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24550                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24551             }
24552             
24553             if(this.labelsm > 0){
24554                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24555                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24556             }
24557             
24558             if(this.labelxs > 0){
24559                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24560                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24561             }
24562             
24563         } else if ( this.fieldLabel.length) {
24564 //                Roo.log(" label");
24565                 cfg.cn = [
24566                    
24567                     {
24568                         tag: this.boxLabel ? 'span' : 'label',
24569                         'for': id,
24570                         cls: 'control-label box-input-label',
24571                         //cls : 'input-group-addon',
24572                         html : this.fieldLabel
24573                     },
24574                     
24575                     inputblock
24576                     
24577                 ];
24578                 if (boxLabelCfg) {
24579                     cfg.cn.push(boxLabelCfg);
24580                 }
24581
24582         } else {
24583             
24584 //                Roo.log(" no label && no align");
24585                 cfg.cn = [  inputblock ] ;
24586                 if (boxLabelCfg) {
24587                     cfg.cn.push(boxLabelCfg);
24588                 }
24589
24590                 
24591         }
24592         
24593        
24594         
24595         if(this.inputType != 'radio'){
24596             cfg.cn.push(hidden);
24597         }
24598         
24599         return cfg;
24600         
24601     },
24602     
24603     /**
24604      * return the real input element.
24605      */
24606     inputEl: function ()
24607     {
24608         return this.el.select('input.roo-' + this.inputType,true).first();
24609     },
24610     hiddenEl: function ()
24611     {
24612         return this.el.select('input.roo-hidden-value',true).first();
24613     },
24614     
24615     labelEl: function()
24616     {
24617         return this.el.select('label.control-label',true).first();
24618     },
24619     /* depricated... */
24620     
24621     label: function()
24622     {
24623         return this.labelEl();
24624     },
24625     
24626     boxLabelEl: function()
24627     {
24628         return this.el.select('label.box-label',true).first();
24629     },
24630     
24631     initEvents : function()
24632     {
24633 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24634         
24635         this.inputEl().on('click', this.onClick,  this);
24636         
24637         if (this.boxLabel) { 
24638             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24639         }
24640         
24641         this.startValue = this.getValue();
24642         
24643         if(this.groupId){
24644             Roo.bootstrap.CheckBox.register(this);
24645         }
24646     },
24647     
24648     onClick : function(e)
24649     {   
24650         if(this.fireEvent('click', this, e) !== false){
24651             this.setChecked(!this.checked);
24652         }
24653         
24654     },
24655     
24656     setChecked : function(state,suppressEvent)
24657     {
24658         this.startValue = this.getValue();
24659
24660         if(this.inputType == 'radio'){
24661             
24662             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24663                 e.dom.checked = false;
24664             });
24665             
24666             this.inputEl().dom.checked = true;
24667             
24668             this.inputEl().dom.value = this.inputValue;
24669             
24670             if(suppressEvent !== true){
24671                 this.fireEvent('check', this, true);
24672             }
24673             
24674             this.validate();
24675             
24676             return;
24677         }
24678         
24679         this.checked = state;
24680         
24681         this.inputEl().dom.checked = state;
24682         
24683         
24684         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24685         
24686         if(suppressEvent !== true){
24687             this.fireEvent('check', this, state);
24688         }
24689         
24690         this.validate();
24691     },
24692     
24693     getValue : function()
24694     {
24695         if(this.inputType == 'radio'){
24696             return this.getGroupValue();
24697         }
24698         
24699         return this.hiddenEl().dom.value;
24700         
24701     },
24702     
24703     getGroupValue : function()
24704     {
24705         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24706             return '';
24707         }
24708         
24709         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24710     },
24711     
24712     setValue : function(v,suppressEvent)
24713     {
24714         if(this.inputType == 'radio'){
24715             this.setGroupValue(v, suppressEvent);
24716             return;
24717         }
24718         
24719         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24720         
24721         this.validate();
24722     },
24723     
24724     setGroupValue : function(v, suppressEvent)
24725     {
24726         this.startValue = this.getValue();
24727         
24728         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24729             e.dom.checked = false;
24730             
24731             if(e.dom.value == v){
24732                 e.dom.checked = true;
24733             }
24734         });
24735         
24736         if(suppressEvent !== true){
24737             this.fireEvent('check', this, true);
24738         }
24739
24740         this.validate();
24741         
24742         return;
24743     },
24744     
24745     validate : function()
24746     {
24747         if(this.getVisibilityEl().hasClass('hidden')){
24748             return true;
24749         }
24750         
24751         if(
24752                 this.disabled || 
24753                 (this.inputType == 'radio' && this.validateRadio()) ||
24754                 (this.inputType == 'checkbox' && this.validateCheckbox())
24755         ){
24756             this.markValid();
24757             return true;
24758         }
24759         
24760         this.markInvalid();
24761         return false;
24762     },
24763     
24764     validateRadio : function()
24765     {
24766         if(this.getVisibilityEl().hasClass('hidden')){
24767             return true;
24768         }
24769         
24770         if(this.allowBlank){
24771             return true;
24772         }
24773         
24774         var valid = false;
24775         
24776         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24777             if(!e.dom.checked){
24778                 return;
24779             }
24780             
24781             valid = true;
24782             
24783             return false;
24784         });
24785         
24786         return valid;
24787     },
24788     
24789     validateCheckbox : function()
24790     {
24791         if(!this.groupId){
24792             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24793             //return (this.getValue() == this.inputValue) ? true : false;
24794         }
24795         
24796         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24797         
24798         if(!group){
24799             return false;
24800         }
24801         
24802         var r = false;
24803         
24804         for(var i in group){
24805             if(group[i].el.isVisible(true)){
24806                 r = false;
24807                 break;
24808             }
24809             
24810             r = true;
24811         }
24812         
24813         for(var i in group){
24814             if(r){
24815                 break;
24816             }
24817             
24818             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24819         }
24820         
24821         return r;
24822     },
24823     
24824     /**
24825      * Mark this field as valid
24826      */
24827     markValid : function()
24828     {
24829         var _this = this;
24830         
24831         this.fireEvent('valid', this);
24832         
24833         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24834         
24835         if(this.groupId){
24836             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24837         }
24838         
24839         if(label){
24840             label.markValid();
24841         }
24842
24843         if(this.inputType == 'radio'){
24844             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24845                 var fg = e.findParent('.form-group', false, true);
24846                 if (Roo.bootstrap.version == 3) {
24847                     fg.removeClass([_this.invalidClass, _this.validClass]);
24848                     fg.addClass(_this.validClass);
24849                 } else {
24850                     fg.removeClass(['is-valid', 'is-invalid']);
24851                     fg.addClass('is-valid');
24852                 }
24853             });
24854             
24855             return;
24856         }
24857
24858         if(!this.groupId){
24859             var fg = this.el.findParent('.form-group', false, true);
24860             if (Roo.bootstrap.version == 3) {
24861                 fg.removeClass([this.invalidClass, this.validClass]);
24862                 fg.addClass(this.validClass);
24863             } else {
24864                 fg.removeClass(['is-valid', 'is-invalid']);
24865                 fg.addClass('is-valid');
24866             }
24867             return;
24868         }
24869         
24870         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24871         
24872         if(!group){
24873             return;
24874         }
24875         
24876         for(var i in group){
24877             var fg = group[i].el.findParent('.form-group', false, true);
24878             if (Roo.bootstrap.version == 3) {
24879                 fg.removeClass([this.invalidClass, this.validClass]);
24880                 fg.addClass(this.validClass);
24881             } else {
24882                 fg.removeClass(['is-valid', 'is-invalid']);
24883                 fg.addClass('is-valid');
24884             }
24885         }
24886     },
24887     
24888      /**
24889      * Mark this field as invalid
24890      * @param {String} msg The validation message
24891      */
24892     markInvalid : function(msg)
24893     {
24894         if(this.allowBlank){
24895             return;
24896         }
24897         
24898         var _this = this;
24899         
24900         this.fireEvent('invalid', this, msg);
24901         
24902         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24903         
24904         if(this.groupId){
24905             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24906         }
24907         
24908         if(label){
24909             label.markInvalid();
24910         }
24911             
24912         if(this.inputType == 'radio'){
24913             
24914             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24915                 var fg = e.findParent('.form-group', false, true);
24916                 if (Roo.bootstrap.version == 3) {
24917                     fg.removeClass([_this.invalidClass, _this.validClass]);
24918                     fg.addClass(_this.invalidClass);
24919                 } else {
24920                     fg.removeClass(['is-invalid', 'is-valid']);
24921                     fg.addClass('is-invalid');
24922                 }
24923             });
24924             
24925             return;
24926         }
24927         
24928         if(!this.groupId){
24929             var fg = this.el.findParent('.form-group', false, true);
24930             if (Roo.bootstrap.version == 3) {
24931                 fg.removeClass([_this.invalidClass, _this.validClass]);
24932                 fg.addClass(_this.invalidClass);
24933             } else {
24934                 fg.removeClass(['is-invalid', 'is-valid']);
24935                 fg.addClass('is-invalid');
24936             }
24937             return;
24938         }
24939         
24940         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24941         
24942         if(!group){
24943             return;
24944         }
24945         
24946         for(var i in group){
24947             var fg = group[i].el.findParent('.form-group', false, true);
24948             if (Roo.bootstrap.version == 3) {
24949                 fg.removeClass([_this.invalidClass, _this.validClass]);
24950                 fg.addClass(_this.invalidClass);
24951             } else {
24952                 fg.removeClass(['is-invalid', 'is-valid']);
24953                 fg.addClass('is-invalid');
24954             }
24955         }
24956         
24957     },
24958     
24959     clearInvalid : function()
24960     {
24961         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24962         
24963         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24964         
24965         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24966         
24967         if (label && label.iconEl) {
24968             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24969             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24970         }
24971     },
24972     
24973     disable : function()
24974     {
24975         if(this.inputType != 'radio'){
24976             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24977             return;
24978         }
24979         
24980         var _this = this;
24981         
24982         if(this.rendered){
24983             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24984                 _this.getActionEl().addClass(this.disabledClass);
24985                 e.dom.disabled = true;
24986             });
24987         }
24988         
24989         this.disabled = true;
24990         this.fireEvent("disable", this);
24991         return this;
24992     },
24993
24994     enable : function()
24995     {
24996         if(this.inputType != 'radio'){
24997             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24998             return;
24999         }
25000         
25001         var _this = this;
25002         
25003         if(this.rendered){
25004             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25005                 _this.getActionEl().removeClass(this.disabledClass);
25006                 e.dom.disabled = false;
25007             });
25008         }
25009         
25010         this.disabled = false;
25011         this.fireEvent("enable", this);
25012         return this;
25013     },
25014     
25015     setBoxLabel : function(v)
25016     {
25017         this.boxLabel = v;
25018         
25019         if(this.rendered){
25020             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25021         }
25022     }
25023
25024 });
25025
25026 Roo.apply(Roo.bootstrap.CheckBox, {
25027     
25028     groups: {},
25029     
25030      /**
25031     * register a CheckBox Group
25032     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25033     */
25034     register : function(checkbox)
25035     {
25036         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25037             this.groups[checkbox.groupId] = {};
25038         }
25039         
25040         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25041             return;
25042         }
25043         
25044         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25045         
25046     },
25047     /**
25048     * fetch a CheckBox Group based on the group ID
25049     * @param {string} the group ID
25050     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25051     */
25052     get: function(groupId) {
25053         if (typeof(this.groups[groupId]) == 'undefined') {
25054             return false;
25055         }
25056         
25057         return this.groups[groupId] ;
25058     }
25059     
25060     
25061 });
25062 /*
25063  * - LGPL
25064  *
25065  * RadioItem
25066  * 
25067  */
25068
25069 /**
25070  * @class Roo.bootstrap.Radio
25071  * @extends Roo.bootstrap.Component
25072  * Bootstrap Radio class
25073  * @cfg {String} boxLabel - the label associated
25074  * @cfg {String} value - the value of radio
25075  * 
25076  * @constructor
25077  * Create a new Radio
25078  * @param {Object} config The config object
25079  */
25080 Roo.bootstrap.Radio = function(config){
25081     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25082     
25083 };
25084
25085 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25086     
25087     boxLabel : '',
25088     
25089     value : '',
25090     
25091     getAutoCreate : function()
25092     {
25093         var cfg = {
25094             tag : 'div',
25095             cls : 'form-group radio',
25096             cn : [
25097                 {
25098                     tag : 'label',
25099                     cls : 'box-label',
25100                     html : this.boxLabel
25101                 }
25102             ]
25103         };
25104         
25105         return cfg;
25106     },
25107     
25108     initEvents : function() 
25109     {
25110         this.parent().register(this);
25111         
25112         this.el.on('click', this.onClick, this);
25113         
25114     },
25115     
25116     onClick : function(e)
25117     {
25118         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25119             this.setChecked(true);
25120         }
25121     },
25122     
25123     setChecked : function(state, suppressEvent)
25124     {
25125         this.parent().setValue(this.value, suppressEvent);
25126         
25127     },
25128     
25129     setBoxLabel : function(v)
25130     {
25131         this.boxLabel = v;
25132         
25133         if(this.rendered){
25134             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25135         }
25136     }
25137     
25138 });
25139  
25140
25141  /*
25142  * - LGPL
25143  *
25144  * Input
25145  * 
25146  */
25147
25148 /**
25149  * @class Roo.bootstrap.SecurePass
25150  * @extends Roo.bootstrap.Input
25151  * Bootstrap SecurePass class
25152  *
25153  * 
25154  * @constructor
25155  * Create a new SecurePass
25156  * @param {Object} config The config object
25157  */
25158  
25159 Roo.bootstrap.SecurePass = function (config) {
25160     // these go here, so the translation tool can replace them..
25161     this.errors = {
25162         PwdEmpty: "Please type a password, and then retype it to confirm.",
25163         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25164         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25165         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25166         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25167         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25168         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25169         TooWeak: "Your password is Too Weak."
25170     },
25171     this.meterLabel = "Password strength:";
25172     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25173     this.meterClass = [
25174         "roo-password-meter-tooweak", 
25175         "roo-password-meter-weak", 
25176         "roo-password-meter-medium", 
25177         "roo-password-meter-strong", 
25178         "roo-password-meter-grey"
25179     ];
25180     
25181     this.errors = {};
25182     
25183     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25184 }
25185
25186 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25187     /**
25188      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25189      * {
25190      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25191      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25192      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25193      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25194      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25195      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25196      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25197      * })
25198      */
25199     // private
25200     
25201     meterWidth: 300,
25202     errorMsg :'',    
25203     errors: false,
25204     imageRoot: '/',
25205     /**
25206      * @cfg {String/Object} Label for the strength meter (defaults to
25207      * 'Password strength:')
25208      */
25209     // private
25210     meterLabel: '',
25211     /**
25212      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25213      * ['Weak', 'Medium', 'Strong'])
25214      */
25215     // private    
25216     pwdStrengths: false,    
25217     // private
25218     strength: 0,
25219     // private
25220     _lastPwd: null,
25221     // private
25222     kCapitalLetter: 0,
25223     kSmallLetter: 1,
25224     kDigit: 2,
25225     kPunctuation: 3,
25226     
25227     insecure: false,
25228     // private
25229     initEvents: function ()
25230     {
25231         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25232
25233         if (this.el.is('input[type=password]') && Roo.isSafari) {
25234             this.el.on('keydown', this.SafariOnKeyDown, this);
25235         }
25236
25237         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25238     },
25239     // private
25240     onRender: function (ct, position)
25241     {
25242         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25243         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25244         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25245
25246         this.trigger.createChild({
25247                    cn: [
25248                     {
25249                     //id: 'PwdMeter',
25250                     tag: 'div',
25251                     cls: 'roo-password-meter-grey col-xs-12',
25252                     style: {
25253                         //width: 0,
25254                         //width: this.meterWidth + 'px'                                                
25255                         }
25256                     },
25257                     {                            
25258                          cls: 'roo-password-meter-text'                          
25259                     }
25260                 ]            
25261         });
25262
25263          
25264         if (this.hideTrigger) {
25265             this.trigger.setDisplayed(false);
25266         }
25267         this.setSize(this.width || '', this.height || '');
25268     },
25269     // private
25270     onDestroy: function ()
25271     {
25272         if (this.trigger) {
25273             this.trigger.removeAllListeners();
25274             this.trigger.remove();
25275         }
25276         if (this.wrap) {
25277             this.wrap.remove();
25278         }
25279         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25280     },
25281     // private
25282     checkStrength: function ()
25283     {
25284         var pwd = this.inputEl().getValue();
25285         if (pwd == this._lastPwd) {
25286             return;
25287         }
25288
25289         var strength;
25290         if (this.ClientSideStrongPassword(pwd)) {
25291             strength = 3;
25292         } else if (this.ClientSideMediumPassword(pwd)) {
25293             strength = 2;
25294         } else if (this.ClientSideWeakPassword(pwd)) {
25295             strength = 1;
25296         } else {
25297             strength = 0;
25298         }
25299         
25300         Roo.log('strength1: ' + strength);
25301         
25302         //var pm = this.trigger.child('div/div/div').dom;
25303         var pm = this.trigger.child('div/div');
25304         pm.removeClass(this.meterClass);
25305         pm.addClass(this.meterClass[strength]);
25306                 
25307         
25308         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25309                 
25310         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25311         
25312         this._lastPwd = pwd;
25313     },
25314     reset: function ()
25315     {
25316         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25317         
25318         this._lastPwd = '';
25319         
25320         var pm = this.trigger.child('div/div');
25321         pm.removeClass(this.meterClass);
25322         pm.addClass('roo-password-meter-grey');        
25323         
25324         
25325         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25326         
25327         pt.innerHTML = '';
25328         this.inputEl().dom.type='password';
25329     },
25330     // private
25331     validateValue: function (value)
25332     {
25333         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25334             return false;
25335         }
25336         if (value.length == 0) {
25337             if (this.allowBlank) {
25338                 this.clearInvalid();
25339                 return true;
25340             }
25341
25342             this.markInvalid(this.errors.PwdEmpty);
25343             this.errorMsg = this.errors.PwdEmpty;
25344             return false;
25345         }
25346         
25347         if(this.insecure){
25348             return true;
25349         }
25350         
25351         if (!value.match(/[\x21-\x7e]+/)) {
25352             this.markInvalid(this.errors.PwdBadChar);
25353             this.errorMsg = this.errors.PwdBadChar;
25354             return false;
25355         }
25356         if (value.length < 6) {
25357             this.markInvalid(this.errors.PwdShort);
25358             this.errorMsg = this.errors.PwdShort;
25359             return false;
25360         }
25361         if (value.length > 16) {
25362             this.markInvalid(this.errors.PwdLong);
25363             this.errorMsg = this.errors.PwdLong;
25364             return false;
25365         }
25366         var strength;
25367         if (this.ClientSideStrongPassword(value)) {
25368             strength = 3;
25369         } else if (this.ClientSideMediumPassword(value)) {
25370             strength = 2;
25371         } else if (this.ClientSideWeakPassword(value)) {
25372             strength = 1;
25373         } else {
25374             strength = 0;
25375         }
25376
25377         
25378         if (strength < 2) {
25379             //this.markInvalid(this.errors.TooWeak);
25380             this.errorMsg = this.errors.TooWeak;
25381             //return false;
25382         }
25383         
25384         
25385         console.log('strength2: ' + strength);
25386         
25387         //var pm = this.trigger.child('div/div/div').dom;
25388         
25389         var pm = this.trigger.child('div/div');
25390         pm.removeClass(this.meterClass);
25391         pm.addClass(this.meterClass[strength]);
25392                 
25393         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25394                 
25395         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25396         
25397         this.errorMsg = ''; 
25398         return true;
25399     },
25400     // private
25401     CharacterSetChecks: function (type)
25402     {
25403         this.type = type;
25404         this.fResult = false;
25405     },
25406     // private
25407     isctype: function (character, type)
25408     {
25409         switch (type) {  
25410             case this.kCapitalLetter:
25411                 if (character >= 'A' && character <= 'Z') {
25412                     return true;
25413                 }
25414                 break;
25415             
25416             case this.kSmallLetter:
25417                 if (character >= 'a' && character <= 'z') {
25418                     return true;
25419                 }
25420                 break;
25421             
25422             case this.kDigit:
25423                 if (character >= '0' && character <= '9') {
25424                     return true;
25425                 }
25426                 break;
25427             
25428             case this.kPunctuation:
25429                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25430                     return true;
25431                 }
25432                 break;
25433             
25434             default:
25435                 return false;
25436         }
25437
25438     },
25439     // private
25440     IsLongEnough: function (pwd, size)
25441     {
25442         return !(pwd == null || isNaN(size) || pwd.length < size);
25443     },
25444     // private
25445     SpansEnoughCharacterSets: function (word, nb)
25446     {
25447         if (!this.IsLongEnough(word, nb))
25448         {
25449             return false;
25450         }
25451
25452         var characterSetChecks = new Array(
25453             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25454             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25455         );
25456         
25457         for (var index = 0; index < word.length; ++index) {
25458             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25459                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25460                     characterSetChecks[nCharSet].fResult = true;
25461                     break;
25462                 }
25463             }
25464         }
25465
25466         var nCharSets = 0;
25467         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25468             if (characterSetChecks[nCharSet].fResult) {
25469                 ++nCharSets;
25470             }
25471         }
25472
25473         if (nCharSets < nb) {
25474             return false;
25475         }
25476         return true;
25477     },
25478     // private
25479     ClientSideStrongPassword: function (pwd)
25480     {
25481         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25482     },
25483     // private
25484     ClientSideMediumPassword: function (pwd)
25485     {
25486         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25487     },
25488     // private
25489     ClientSideWeakPassword: function (pwd)
25490     {
25491         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25492     }
25493           
25494 })//<script type="text/javascript">
25495
25496 /*
25497  * Based  Ext JS Library 1.1.1
25498  * Copyright(c) 2006-2007, Ext JS, LLC.
25499  * LGPL
25500  *
25501  */
25502  
25503 /**
25504  * @class Roo.HtmlEditorCore
25505  * @extends Roo.Component
25506  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25507  *
25508  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25509  */
25510
25511 Roo.HtmlEditorCore = function(config){
25512     
25513     
25514     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25515     
25516     
25517     this.addEvents({
25518         /**
25519          * @event initialize
25520          * Fires when the editor is fully initialized (including the iframe)
25521          * @param {Roo.HtmlEditorCore} this
25522          */
25523         initialize: true,
25524         /**
25525          * @event activate
25526          * Fires when the editor is first receives the focus. Any insertion must wait
25527          * until after this event.
25528          * @param {Roo.HtmlEditorCore} this
25529          */
25530         activate: true,
25531          /**
25532          * @event beforesync
25533          * Fires before the textarea is updated with content from the editor iframe. Return false
25534          * to cancel the sync.
25535          * @param {Roo.HtmlEditorCore} this
25536          * @param {String} html
25537          */
25538         beforesync: true,
25539          /**
25540          * @event beforepush
25541          * Fires before the iframe editor is updated with content from the textarea. Return false
25542          * to cancel the push.
25543          * @param {Roo.HtmlEditorCore} this
25544          * @param {String} html
25545          */
25546         beforepush: true,
25547          /**
25548          * @event sync
25549          * Fires when the textarea is updated with content from the editor iframe.
25550          * @param {Roo.HtmlEditorCore} this
25551          * @param {String} html
25552          */
25553         sync: true,
25554          /**
25555          * @event push
25556          * Fires when the iframe editor is updated with content from the textarea.
25557          * @param {Roo.HtmlEditorCore} this
25558          * @param {String} html
25559          */
25560         push: true,
25561         
25562         /**
25563          * @event editorevent
25564          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25565          * @param {Roo.HtmlEditorCore} this
25566          */
25567         editorevent: true
25568         
25569     });
25570     
25571     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25572     
25573     // defaults : white / black...
25574     this.applyBlacklists();
25575     
25576     
25577     
25578 };
25579
25580
25581 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25582
25583
25584      /**
25585      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25586      */
25587     
25588     owner : false,
25589     
25590      /**
25591      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25592      *                        Roo.resizable.
25593      */
25594     resizable : false,
25595      /**
25596      * @cfg {Number} height (in pixels)
25597      */   
25598     height: 300,
25599    /**
25600      * @cfg {Number} width (in pixels)
25601      */   
25602     width: 500,
25603     
25604     /**
25605      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25606      * 
25607      */
25608     stylesheets: false,
25609     
25610     // id of frame..
25611     frameId: false,
25612     
25613     // private properties
25614     validationEvent : false,
25615     deferHeight: true,
25616     initialized : false,
25617     activated : false,
25618     sourceEditMode : false,
25619     onFocus : Roo.emptyFn,
25620     iframePad:3,
25621     hideMode:'offsets',
25622     
25623     clearUp: true,
25624     
25625     // blacklist + whitelisted elements..
25626     black: false,
25627     white: false,
25628      
25629     bodyCls : '',
25630
25631     /**
25632      * Protected method that will not generally be called directly. It
25633      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25634      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25635      */
25636     getDocMarkup : function(){
25637         // body styles..
25638         var st = '';
25639         
25640         // inherit styels from page...?? 
25641         if (this.stylesheets === false) {
25642             
25643             Roo.get(document.head).select('style').each(function(node) {
25644                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25645             });
25646             
25647             Roo.get(document.head).select('link').each(function(node) { 
25648                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25649             });
25650             
25651         } else if (!this.stylesheets.length) {
25652                 // simple..
25653                 st = '<style type="text/css">' +
25654                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25655                    '</style>';
25656         } else {
25657             for (var i in this.stylesheets) { 
25658                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25659             }
25660             
25661         }
25662         
25663         st +=  '<style type="text/css">' +
25664             'IMG { cursor: pointer } ' +
25665         '</style>';
25666
25667         var cls = 'roo-htmleditor-body';
25668         
25669         if(this.bodyCls.length){
25670             cls += ' ' + this.bodyCls;
25671         }
25672         
25673         return '<html><head>' + st  +
25674             //<style type="text/css">' +
25675             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25676             //'</style>' +
25677             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25678     },
25679
25680     // private
25681     onRender : function(ct, position)
25682     {
25683         var _t = this;
25684         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25685         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25686         
25687         
25688         this.el.dom.style.border = '0 none';
25689         this.el.dom.setAttribute('tabIndex', -1);
25690         this.el.addClass('x-hidden hide');
25691         
25692         
25693         
25694         if(Roo.isIE){ // fix IE 1px bogus margin
25695             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25696         }
25697        
25698         
25699         this.frameId = Roo.id();
25700         
25701          
25702         
25703         var iframe = this.owner.wrap.createChild({
25704             tag: 'iframe',
25705             cls: 'form-control', // bootstrap..
25706             id: this.frameId,
25707             name: this.frameId,
25708             frameBorder : 'no',
25709             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25710         }, this.el
25711         );
25712         
25713         
25714         this.iframe = iframe.dom;
25715
25716          this.assignDocWin();
25717         
25718         this.doc.designMode = 'on';
25719        
25720         this.doc.open();
25721         this.doc.write(this.getDocMarkup());
25722         this.doc.close();
25723
25724         
25725         var task = { // must defer to wait for browser to be ready
25726             run : function(){
25727                 //console.log("run task?" + this.doc.readyState);
25728                 this.assignDocWin();
25729                 if(this.doc.body || this.doc.readyState == 'complete'){
25730                     try {
25731                         this.doc.designMode="on";
25732                     } catch (e) {
25733                         return;
25734                     }
25735                     Roo.TaskMgr.stop(task);
25736                     this.initEditor.defer(10, this);
25737                 }
25738             },
25739             interval : 10,
25740             duration: 10000,
25741             scope: this
25742         };
25743         Roo.TaskMgr.start(task);
25744
25745     },
25746
25747     // private
25748     onResize : function(w, h)
25749     {
25750          Roo.log('resize: ' +w + ',' + h );
25751         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25752         if(!this.iframe){
25753             return;
25754         }
25755         if(typeof w == 'number'){
25756             
25757             this.iframe.style.width = w + 'px';
25758         }
25759         if(typeof h == 'number'){
25760             
25761             this.iframe.style.height = h + 'px';
25762             if(this.doc){
25763                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25764             }
25765         }
25766         
25767     },
25768
25769     /**
25770      * Toggles the editor between standard and source edit mode.
25771      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25772      */
25773     toggleSourceEdit : function(sourceEditMode){
25774         
25775         this.sourceEditMode = sourceEditMode === true;
25776         
25777         if(this.sourceEditMode){
25778  
25779             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25780             
25781         }else{
25782             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25783             //this.iframe.className = '';
25784             this.deferFocus();
25785         }
25786         //this.setSize(this.owner.wrap.getSize());
25787         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25788     },
25789
25790     
25791   
25792
25793     /**
25794      * Protected method that will not generally be called directly. If you need/want
25795      * custom HTML cleanup, this is the method you should override.
25796      * @param {String} html The HTML to be cleaned
25797      * return {String} The cleaned HTML
25798      */
25799     cleanHtml : function(html){
25800         html = String(html);
25801         if(html.length > 5){
25802             if(Roo.isSafari){ // strip safari nonsense
25803                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25804             }
25805         }
25806         if(html == '&nbsp;'){
25807             html = '';
25808         }
25809         return html;
25810     },
25811
25812     /**
25813      * HTML Editor -> Textarea
25814      * Protected method that will not generally be called directly. Syncs the contents
25815      * of the editor iframe with the textarea.
25816      */
25817     syncValue : function(){
25818         if(this.initialized){
25819             var bd = (this.doc.body || this.doc.documentElement);
25820             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25821             var html = bd.innerHTML;
25822             if(Roo.isSafari){
25823                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25824                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25825                 if(m && m[1]){
25826                     html = '<div style="'+m[0]+'">' + html + '</div>';
25827                 }
25828             }
25829             html = this.cleanHtml(html);
25830             // fix up the special chars.. normaly like back quotes in word...
25831             // however we do not want to do this with chinese..
25832             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25833                 
25834                 var cc = match.charCodeAt();
25835
25836                 // Get the character value, handling surrogate pairs
25837                 if (match.length == 2) {
25838                     // It's a surrogate pair, calculate the Unicode code point
25839                     var high = match.charCodeAt(0) - 0xD800;
25840                     var low  = match.charCodeAt(1) - 0xDC00;
25841                     cc = (high * 0x400) + low + 0x10000;
25842                 }  else if (
25843                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25844                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25845                     (cc >= 0xf900 && cc < 0xfb00 )
25846                 ) {
25847                         return match;
25848                 }  
25849          
25850                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25851                 return "&#" + cc + ";";
25852                 
25853                 
25854             });
25855             
25856             
25857              
25858             if(this.owner.fireEvent('beforesync', this, html) !== false){
25859                 this.el.dom.value = html;
25860                 this.owner.fireEvent('sync', this, html);
25861             }
25862         }
25863     },
25864
25865     /**
25866      * Protected method that will not generally be called directly. Pushes the value of the textarea
25867      * into the iframe editor.
25868      */
25869     pushValue : function(){
25870         if(this.initialized){
25871             var v = this.el.dom.value.trim();
25872             
25873 //            if(v.length < 1){
25874 //                v = '&#160;';
25875 //            }
25876             
25877             if(this.owner.fireEvent('beforepush', this, v) !== false){
25878                 var d = (this.doc.body || this.doc.documentElement);
25879                 d.innerHTML = v;
25880                 this.cleanUpPaste();
25881                 this.el.dom.value = d.innerHTML;
25882                 this.owner.fireEvent('push', this, v);
25883             }
25884         }
25885     },
25886
25887     // private
25888     deferFocus : function(){
25889         this.focus.defer(10, this);
25890     },
25891
25892     // doc'ed in Field
25893     focus : function(){
25894         if(this.win && !this.sourceEditMode){
25895             this.win.focus();
25896         }else{
25897             this.el.focus();
25898         }
25899     },
25900     
25901     assignDocWin: function()
25902     {
25903         var iframe = this.iframe;
25904         
25905          if(Roo.isIE){
25906             this.doc = iframe.contentWindow.document;
25907             this.win = iframe.contentWindow;
25908         } else {
25909 //            if (!Roo.get(this.frameId)) {
25910 //                return;
25911 //            }
25912 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25913 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25914             
25915             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25916                 return;
25917             }
25918             
25919             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25920             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25921         }
25922     },
25923     
25924     // private
25925     initEditor : function(){
25926         //console.log("INIT EDITOR");
25927         this.assignDocWin();
25928         
25929         
25930         
25931         this.doc.designMode="on";
25932         this.doc.open();
25933         this.doc.write(this.getDocMarkup());
25934         this.doc.close();
25935         
25936         var dbody = (this.doc.body || this.doc.documentElement);
25937         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25938         // this copies styles from the containing element into thsi one..
25939         // not sure why we need all of this..
25940         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25941         
25942         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25943         //ss['background-attachment'] = 'fixed'; // w3c
25944         dbody.bgProperties = 'fixed'; // ie
25945         //Roo.DomHelper.applyStyles(dbody, ss);
25946         Roo.EventManager.on(this.doc, {
25947             //'mousedown': this.onEditorEvent,
25948             'mouseup': this.onEditorEvent,
25949             'dblclick': this.onEditorEvent,
25950             'click': this.onEditorEvent,
25951             'keyup': this.onEditorEvent,
25952             buffer:100,
25953             scope: this
25954         });
25955         if(Roo.isGecko){
25956             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25957         }
25958         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25959             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25960         }
25961         this.initialized = true;
25962
25963         this.owner.fireEvent('initialize', this);
25964         this.pushValue();
25965     },
25966
25967     // private
25968     onDestroy : function(){
25969         
25970         
25971         
25972         if(this.rendered){
25973             
25974             //for (var i =0; i < this.toolbars.length;i++) {
25975             //    // fixme - ask toolbars for heights?
25976             //    this.toolbars[i].onDestroy();
25977            // }
25978             
25979             //this.wrap.dom.innerHTML = '';
25980             //this.wrap.remove();
25981         }
25982     },
25983
25984     // private
25985     onFirstFocus : function(){
25986         
25987         this.assignDocWin();
25988         
25989         
25990         this.activated = true;
25991          
25992     
25993         if(Roo.isGecko){ // prevent silly gecko errors
25994             this.win.focus();
25995             var s = this.win.getSelection();
25996             if(!s.focusNode || s.focusNode.nodeType != 3){
25997                 var r = s.getRangeAt(0);
25998                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25999                 r.collapse(true);
26000                 this.deferFocus();
26001             }
26002             try{
26003                 this.execCmd('useCSS', true);
26004                 this.execCmd('styleWithCSS', false);
26005             }catch(e){}
26006         }
26007         this.owner.fireEvent('activate', this);
26008     },
26009
26010     // private
26011     adjustFont: function(btn){
26012         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26013         //if(Roo.isSafari){ // safari
26014         //    adjust *= 2;
26015        // }
26016         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26017         if(Roo.isSafari){ // safari
26018             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26019             v =  (v < 10) ? 10 : v;
26020             v =  (v > 48) ? 48 : v;
26021             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26022             
26023         }
26024         
26025         
26026         v = Math.max(1, v+adjust);
26027         
26028         this.execCmd('FontSize', v  );
26029     },
26030
26031     onEditorEvent : function(e)
26032     {
26033         this.owner.fireEvent('editorevent', this, e);
26034       //  this.updateToolbar();
26035         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26036     },
26037
26038     insertTag : function(tg)
26039     {
26040         // could be a bit smarter... -> wrap the current selected tRoo..
26041         if (tg.toLowerCase() == 'span' ||
26042             tg.toLowerCase() == 'code' ||
26043             tg.toLowerCase() == 'sup' ||
26044             tg.toLowerCase() == 'sub' 
26045             ) {
26046             
26047             range = this.createRange(this.getSelection());
26048             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26049             wrappingNode.appendChild(range.extractContents());
26050             range.insertNode(wrappingNode);
26051
26052             return;
26053             
26054             
26055             
26056         }
26057         this.execCmd("formatblock",   tg);
26058         
26059     },
26060     
26061     insertText : function(txt)
26062     {
26063         
26064         
26065         var range = this.createRange();
26066         range.deleteContents();
26067                //alert(Sender.getAttribute('label'));
26068                
26069         range.insertNode(this.doc.createTextNode(txt));
26070     } ,
26071     
26072      
26073
26074     /**
26075      * Executes a Midas editor command on the editor document and performs necessary focus and
26076      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26077      * @param {String} cmd The Midas command
26078      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26079      */
26080     relayCmd : function(cmd, value){
26081         this.win.focus();
26082         this.execCmd(cmd, value);
26083         this.owner.fireEvent('editorevent', this);
26084         //this.updateToolbar();
26085         this.owner.deferFocus();
26086     },
26087
26088     /**
26089      * Executes a Midas editor command directly on the editor document.
26090      * For visual commands, you should use {@link #relayCmd} instead.
26091      * <b>This should only be called after the editor is initialized.</b>
26092      * @param {String} cmd The Midas command
26093      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26094      */
26095     execCmd : function(cmd, value){
26096         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26097         this.syncValue();
26098     },
26099  
26100  
26101    
26102     /**
26103      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26104      * to insert tRoo.
26105      * @param {String} text | dom node.. 
26106      */
26107     insertAtCursor : function(text)
26108     {
26109         
26110         if(!this.activated){
26111             return;
26112         }
26113         /*
26114         if(Roo.isIE){
26115             this.win.focus();
26116             var r = this.doc.selection.createRange();
26117             if(r){
26118                 r.collapse(true);
26119                 r.pasteHTML(text);
26120                 this.syncValue();
26121                 this.deferFocus();
26122             
26123             }
26124             return;
26125         }
26126         */
26127         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26128             this.win.focus();
26129             
26130             
26131             // from jquery ui (MIT licenced)
26132             var range, node;
26133             var win = this.win;
26134             
26135             if (win.getSelection && win.getSelection().getRangeAt) {
26136                 range = win.getSelection().getRangeAt(0);
26137                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26138                 range.insertNode(node);
26139             } else if (win.document.selection && win.document.selection.createRange) {
26140                 // no firefox support
26141                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26142                 win.document.selection.createRange().pasteHTML(txt);
26143             } else {
26144                 // no firefox support
26145                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26146                 this.execCmd('InsertHTML', txt);
26147             } 
26148             
26149             this.syncValue();
26150             
26151             this.deferFocus();
26152         }
26153     },
26154  // private
26155     mozKeyPress : function(e){
26156         if(e.ctrlKey){
26157             var c = e.getCharCode(), cmd;
26158           
26159             if(c > 0){
26160                 c = String.fromCharCode(c).toLowerCase();
26161                 switch(c){
26162                     case 'b':
26163                         cmd = 'bold';
26164                         break;
26165                     case 'i':
26166                         cmd = 'italic';
26167                         break;
26168                     
26169                     case 'u':
26170                         cmd = 'underline';
26171                         break;
26172                     
26173                     case 'v':
26174                         this.cleanUpPaste.defer(100, this);
26175                         return;
26176                         
26177                 }
26178                 if(cmd){
26179                     this.win.focus();
26180                     this.execCmd(cmd);
26181                     this.deferFocus();
26182                     e.preventDefault();
26183                 }
26184                 
26185             }
26186         }
26187     },
26188
26189     // private
26190     fixKeys : function(){ // load time branching for fastest keydown performance
26191         if(Roo.isIE){
26192             return function(e){
26193                 var k = e.getKey(), r;
26194                 if(k == e.TAB){
26195                     e.stopEvent();
26196                     r = this.doc.selection.createRange();
26197                     if(r){
26198                         r.collapse(true);
26199                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26200                         this.deferFocus();
26201                     }
26202                     return;
26203                 }
26204                 
26205                 if(k == e.ENTER){
26206                     r = this.doc.selection.createRange();
26207                     if(r){
26208                         var target = r.parentElement();
26209                         if(!target || target.tagName.toLowerCase() != 'li'){
26210                             e.stopEvent();
26211                             r.pasteHTML('<br />');
26212                             r.collapse(false);
26213                             r.select();
26214                         }
26215                     }
26216                 }
26217                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26218                     this.cleanUpPaste.defer(100, this);
26219                     return;
26220                 }
26221                 
26222                 
26223             };
26224         }else if(Roo.isOpera){
26225             return function(e){
26226                 var k = e.getKey();
26227                 if(k == e.TAB){
26228                     e.stopEvent();
26229                     this.win.focus();
26230                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26231                     this.deferFocus();
26232                 }
26233                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26234                     this.cleanUpPaste.defer(100, this);
26235                     return;
26236                 }
26237                 
26238             };
26239         }else if(Roo.isSafari){
26240             return function(e){
26241                 var k = e.getKey();
26242                 
26243                 if(k == e.TAB){
26244                     e.stopEvent();
26245                     this.execCmd('InsertText','\t');
26246                     this.deferFocus();
26247                     return;
26248                 }
26249                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26250                     this.cleanUpPaste.defer(100, this);
26251                     return;
26252                 }
26253                 
26254              };
26255         }
26256     }(),
26257     
26258     getAllAncestors: function()
26259     {
26260         var p = this.getSelectedNode();
26261         var a = [];
26262         if (!p) {
26263             a.push(p); // push blank onto stack..
26264             p = this.getParentElement();
26265         }
26266         
26267         
26268         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26269             a.push(p);
26270             p = p.parentNode;
26271         }
26272         a.push(this.doc.body);
26273         return a;
26274     },
26275     lastSel : false,
26276     lastSelNode : false,
26277     
26278     
26279     getSelection : function() 
26280     {
26281         this.assignDocWin();
26282         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26283     },
26284     
26285     getSelectedNode: function() 
26286     {
26287         // this may only work on Gecko!!!
26288         
26289         // should we cache this!!!!
26290         
26291         
26292         
26293          
26294         var range = this.createRange(this.getSelection()).cloneRange();
26295         
26296         if (Roo.isIE) {
26297             var parent = range.parentElement();
26298             while (true) {
26299                 var testRange = range.duplicate();
26300                 testRange.moveToElementText(parent);
26301                 if (testRange.inRange(range)) {
26302                     break;
26303                 }
26304                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26305                     break;
26306                 }
26307                 parent = parent.parentElement;
26308             }
26309             return parent;
26310         }
26311         
26312         // is ancestor a text element.
26313         var ac =  range.commonAncestorContainer;
26314         if (ac.nodeType == 3) {
26315             ac = ac.parentNode;
26316         }
26317         
26318         var ar = ac.childNodes;
26319          
26320         var nodes = [];
26321         var other_nodes = [];
26322         var has_other_nodes = false;
26323         for (var i=0;i<ar.length;i++) {
26324             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26325                 continue;
26326             }
26327             // fullly contained node.
26328             
26329             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26330                 nodes.push(ar[i]);
26331                 continue;
26332             }
26333             
26334             // probably selected..
26335             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26336                 other_nodes.push(ar[i]);
26337                 continue;
26338             }
26339             // outer..
26340             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26341                 continue;
26342             }
26343             
26344             
26345             has_other_nodes = true;
26346         }
26347         if (!nodes.length && other_nodes.length) {
26348             nodes= other_nodes;
26349         }
26350         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26351             return false;
26352         }
26353         
26354         return nodes[0];
26355     },
26356     createRange: function(sel)
26357     {
26358         // this has strange effects when using with 
26359         // top toolbar - not sure if it's a great idea.
26360         //this.editor.contentWindow.focus();
26361         if (typeof sel != "undefined") {
26362             try {
26363                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26364             } catch(e) {
26365                 return this.doc.createRange();
26366             }
26367         } else {
26368             return this.doc.createRange();
26369         }
26370     },
26371     getParentElement: function()
26372     {
26373         
26374         this.assignDocWin();
26375         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26376         
26377         var range = this.createRange(sel);
26378          
26379         try {
26380             var p = range.commonAncestorContainer;
26381             while (p.nodeType == 3) { // text node
26382                 p = p.parentNode;
26383             }
26384             return p;
26385         } catch (e) {
26386             return null;
26387         }
26388     
26389     },
26390     /***
26391      *
26392      * Range intersection.. the hard stuff...
26393      *  '-1' = before
26394      *  '0' = hits..
26395      *  '1' = after.
26396      *         [ -- selected range --- ]
26397      *   [fail]                        [fail]
26398      *
26399      *    basically..
26400      *      if end is before start or  hits it. fail.
26401      *      if start is after end or hits it fail.
26402      *
26403      *   if either hits (but other is outside. - then it's not 
26404      *   
26405      *    
26406      **/
26407     
26408     
26409     // @see http://www.thismuchiknow.co.uk/?p=64.
26410     rangeIntersectsNode : function(range, node)
26411     {
26412         var nodeRange = node.ownerDocument.createRange();
26413         try {
26414             nodeRange.selectNode(node);
26415         } catch (e) {
26416             nodeRange.selectNodeContents(node);
26417         }
26418     
26419         var rangeStartRange = range.cloneRange();
26420         rangeStartRange.collapse(true);
26421     
26422         var rangeEndRange = range.cloneRange();
26423         rangeEndRange.collapse(false);
26424     
26425         var nodeStartRange = nodeRange.cloneRange();
26426         nodeStartRange.collapse(true);
26427     
26428         var nodeEndRange = nodeRange.cloneRange();
26429         nodeEndRange.collapse(false);
26430     
26431         return rangeStartRange.compareBoundaryPoints(
26432                  Range.START_TO_START, nodeEndRange) == -1 &&
26433                rangeEndRange.compareBoundaryPoints(
26434                  Range.START_TO_START, nodeStartRange) == 1;
26435         
26436          
26437     },
26438     rangeCompareNode : function(range, node)
26439     {
26440         var nodeRange = node.ownerDocument.createRange();
26441         try {
26442             nodeRange.selectNode(node);
26443         } catch (e) {
26444             nodeRange.selectNodeContents(node);
26445         }
26446         
26447         
26448         range.collapse(true);
26449     
26450         nodeRange.collapse(true);
26451      
26452         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26453         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26454          
26455         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26456         
26457         var nodeIsBefore   =  ss == 1;
26458         var nodeIsAfter    = ee == -1;
26459         
26460         if (nodeIsBefore && nodeIsAfter) {
26461             return 0; // outer
26462         }
26463         if (!nodeIsBefore && nodeIsAfter) {
26464             return 1; //right trailed.
26465         }
26466         
26467         if (nodeIsBefore && !nodeIsAfter) {
26468             return 2;  // left trailed.
26469         }
26470         // fully contined.
26471         return 3;
26472     },
26473
26474     // private? - in a new class?
26475     cleanUpPaste :  function()
26476     {
26477         // cleans up the whole document..
26478         Roo.log('cleanuppaste');
26479         
26480         this.cleanUpChildren(this.doc.body);
26481         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26482         if (clean != this.doc.body.innerHTML) {
26483             this.doc.body.innerHTML = clean;
26484         }
26485         
26486     },
26487     
26488     cleanWordChars : function(input) {// change the chars to hex code
26489         var he = Roo.HtmlEditorCore;
26490         
26491         var output = input;
26492         Roo.each(he.swapCodes, function(sw) { 
26493             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26494             
26495             output = output.replace(swapper, sw[1]);
26496         });
26497         
26498         return output;
26499     },
26500     
26501     
26502     cleanUpChildren : function (n)
26503     {
26504         if (!n.childNodes.length) {
26505             return;
26506         }
26507         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26508            this.cleanUpChild(n.childNodes[i]);
26509         }
26510     },
26511     
26512     
26513         
26514     
26515     cleanUpChild : function (node)
26516     {
26517         var ed = this;
26518         //console.log(node);
26519         if (node.nodeName == "#text") {
26520             // clean up silly Windows -- stuff?
26521             return; 
26522         }
26523         if (node.nodeName == "#comment") {
26524             node.parentNode.removeChild(node);
26525             // clean up silly Windows -- stuff?
26526             return; 
26527         }
26528         var lcname = node.tagName.toLowerCase();
26529         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26530         // whitelist of tags..
26531         
26532         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26533             // remove node.
26534             node.parentNode.removeChild(node);
26535             return;
26536             
26537         }
26538         
26539         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26540         
26541         // spans with no attributes - just remove them..
26542         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26543             remove_keep_children = true;
26544         }
26545         
26546         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26547         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26548         
26549         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26550         //    remove_keep_children = true;
26551         //}
26552         
26553         if (remove_keep_children) {
26554             this.cleanUpChildren(node);
26555             // inserts everything just before this node...
26556             while (node.childNodes.length) {
26557                 var cn = node.childNodes[0];
26558                 node.removeChild(cn);
26559                 node.parentNode.insertBefore(cn, node);
26560             }
26561             node.parentNode.removeChild(node);
26562             return;
26563         }
26564         
26565         if (!node.attributes || !node.attributes.length) {
26566             
26567           
26568             
26569             
26570             this.cleanUpChildren(node);
26571             return;
26572         }
26573         
26574         function cleanAttr(n,v)
26575         {
26576             
26577             if (v.match(/^\./) || v.match(/^\//)) {
26578                 return;
26579             }
26580             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26581                 return;
26582             }
26583             if (v.match(/^#/)) {
26584                 return;
26585             }
26586             if (v.match(/^\{/)) { // allow template editing.
26587                 return;
26588             }
26589 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26590             node.removeAttribute(n);
26591             
26592         }
26593         
26594         var cwhite = this.cwhite;
26595         var cblack = this.cblack;
26596             
26597         function cleanStyle(n,v)
26598         {
26599             if (v.match(/expression/)) { //XSS?? should we even bother..
26600                 node.removeAttribute(n);
26601                 return;
26602             }
26603             
26604             var parts = v.split(/;/);
26605             var clean = [];
26606             
26607             Roo.each(parts, function(p) {
26608                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26609                 if (!p.length) {
26610                     return true;
26611                 }
26612                 var l = p.split(':').shift().replace(/\s+/g,'');
26613                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26614                 
26615                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26616 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26617                     //node.removeAttribute(n);
26618                     return true;
26619                 }
26620                 //Roo.log()
26621                 // only allow 'c whitelisted system attributes'
26622                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26623 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26624                     //node.removeAttribute(n);
26625                     return true;
26626                 }
26627                 
26628                 
26629                  
26630                 
26631                 clean.push(p);
26632                 return true;
26633             });
26634             if (clean.length) { 
26635                 node.setAttribute(n, clean.join(';'));
26636             } else {
26637                 node.removeAttribute(n);
26638             }
26639             
26640         }
26641         
26642         
26643         for (var i = node.attributes.length-1; i > -1 ; i--) {
26644             var a = node.attributes[i];
26645             //console.log(a);
26646             
26647             if (a.name.toLowerCase().substr(0,2)=='on')  {
26648                 node.removeAttribute(a.name);
26649                 continue;
26650             }
26651             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26652                 node.removeAttribute(a.name);
26653                 continue;
26654             }
26655             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26656                 cleanAttr(a.name,a.value); // fixme..
26657                 continue;
26658             }
26659             if (a.name == 'style') {
26660                 cleanStyle(a.name,a.value);
26661                 continue;
26662             }
26663             /// clean up MS crap..
26664             // tecnically this should be a list of valid class'es..
26665             
26666             
26667             if (a.name == 'class') {
26668                 if (a.value.match(/^Mso/)) {
26669                     node.removeAttribute('class');
26670                 }
26671                 
26672                 if (a.value.match(/^body$/)) {
26673                     node.removeAttribute('class');
26674                 }
26675                 continue;
26676             }
26677             
26678             // style cleanup!?
26679             // class cleanup?
26680             
26681         }
26682         
26683         
26684         this.cleanUpChildren(node);
26685         
26686         
26687     },
26688     
26689     /**
26690      * Clean up MS wordisms...
26691      */
26692     cleanWord : function(node)
26693     {
26694         if (!node) {
26695             this.cleanWord(this.doc.body);
26696             return;
26697         }
26698         
26699         if(
26700                 node.nodeName == 'SPAN' &&
26701                 !node.hasAttributes() &&
26702                 node.childNodes.length == 1 &&
26703                 node.firstChild.nodeName == "#text"  
26704         ) {
26705             var textNode = node.firstChild;
26706             node.removeChild(textNode);
26707             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26708                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26709             }
26710             node.parentNode.insertBefore(textNode, node);
26711             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26712                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26713             }
26714             node.parentNode.removeChild(node);
26715         }
26716         
26717         if (node.nodeName == "#text") {
26718             // clean up silly Windows -- stuff?
26719             return; 
26720         }
26721         if (node.nodeName == "#comment") {
26722             node.parentNode.removeChild(node);
26723             // clean up silly Windows -- stuff?
26724             return; 
26725         }
26726         
26727         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26728             node.parentNode.removeChild(node);
26729             return;
26730         }
26731         //Roo.log(node.tagName);
26732         // remove - but keep children..
26733         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26734             //Roo.log('-- removed');
26735             while (node.childNodes.length) {
26736                 var cn = node.childNodes[0];
26737                 node.removeChild(cn);
26738                 node.parentNode.insertBefore(cn, node);
26739                 // move node to parent - and clean it..
26740                 this.cleanWord(cn);
26741             }
26742             node.parentNode.removeChild(node);
26743             /// no need to iterate chidlren = it's got none..
26744             //this.iterateChildren(node, this.cleanWord);
26745             return;
26746         }
26747         // clean styles
26748         if (node.className.length) {
26749             
26750             var cn = node.className.split(/\W+/);
26751             var cna = [];
26752             Roo.each(cn, function(cls) {
26753                 if (cls.match(/Mso[a-zA-Z]+/)) {
26754                     return;
26755                 }
26756                 cna.push(cls);
26757             });
26758             node.className = cna.length ? cna.join(' ') : '';
26759             if (!cna.length) {
26760                 node.removeAttribute("class");
26761             }
26762         }
26763         
26764         if (node.hasAttribute("lang")) {
26765             node.removeAttribute("lang");
26766         }
26767         
26768         if (node.hasAttribute("style")) {
26769             
26770             var styles = node.getAttribute("style").split(";");
26771             var nstyle = [];
26772             Roo.each(styles, function(s) {
26773                 if (!s.match(/:/)) {
26774                     return;
26775                 }
26776                 var kv = s.split(":");
26777                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26778                     return;
26779                 }
26780                 // what ever is left... we allow.
26781                 nstyle.push(s);
26782             });
26783             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26784             if (!nstyle.length) {
26785                 node.removeAttribute('style');
26786             }
26787         }
26788         this.iterateChildren(node, this.cleanWord);
26789         
26790         
26791         
26792     },
26793     /**
26794      * iterateChildren of a Node, calling fn each time, using this as the scole..
26795      * @param {DomNode} node node to iterate children of.
26796      * @param {Function} fn method of this class to call on each item.
26797      */
26798     iterateChildren : function(node, fn)
26799     {
26800         if (!node.childNodes.length) {
26801                 return;
26802         }
26803         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26804            fn.call(this, node.childNodes[i])
26805         }
26806     },
26807     
26808     
26809     /**
26810      * cleanTableWidths.
26811      *
26812      * Quite often pasting from word etc.. results in tables with column and widths.
26813      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26814      *
26815      */
26816     cleanTableWidths : function(node)
26817     {
26818          
26819          
26820         if (!node) {
26821             this.cleanTableWidths(this.doc.body);
26822             return;
26823         }
26824         
26825         // ignore list...
26826         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26827             return; 
26828         }
26829         Roo.log(node.tagName);
26830         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26831             this.iterateChildren(node, this.cleanTableWidths);
26832             return;
26833         }
26834         if (node.hasAttribute('width')) {
26835             node.removeAttribute('width');
26836         }
26837         
26838          
26839         if (node.hasAttribute("style")) {
26840             // pretty basic...
26841             
26842             var styles = node.getAttribute("style").split(";");
26843             var nstyle = [];
26844             Roo.each(styles, function(s) {
26845                 if (!s.match(/:/)) {
26846                     return;
26847                 }
26848                 var kv = s.split(":");
26849                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26850                     return;
26851                 }
26852                 // what ever is left... we allow.
26853                 nstyle.push(s);
26854             });
26855             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26856             if (!nstyle.length) {
26857                 node.removeAttribute('style');
26858             }
26859         }
26860         
26861         this.iterateChildren(node, this.cleanTableWidths);
26862         
26863         
26864     },
26865     
26866     
26867     
26868     
26869     domToHTML : function(currentElement, depth, nopadtext) {
26870         
26871         depth = depth || 0;
26872         nopadtext = nopadtext || false;
26873     
26874         if (!currentElement) {
26875             return this.domToHTML(this.doc.body);
26876         }
26877         
26878         //Roo.log(currentElement);
26879         var j;
26880         var allText = false;
26881         var nodeName = currentElement.nodeName;
26882         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26883         
26884         if  (nodeName == '#text') {
26885             
26886             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26887         }
26888         
26889         
26890         var ret = '';
26891         if (nodeName != 'BODY') {
26892              
26893             var i = 0;
26894             // Prints the node tagName, such as <A>, <IMG>, etc
26895             if (tagName) {
26896                 var attr = [];
26897                 for(i = 0; i < currentElement.attributes.length;i++) {
26898                     // quoting?
26899                     var aname = currentElement.attributes.item(i).name;
26900                     if (!currentElement.attributes.item(i).value.length) {
26901                         continue;
26902                     }
26903                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26904                 }
26905                 
26906                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26907             } 
26908             else {
26909                 
26910                 // eack
26911             }
26912         } else {
26913             tagName = false;
26914         }
26915         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26916             return ret;
26917         }
26918         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26919             nopadtext = true;
26920         }
26921         
26922         
26923         // Traverse the tree
26924         i = 0;
26925         var currentElementChild = currentElement.childNodes.item(i);
26926         var allText = true;
26927         var innerHTML  = '';
26928         lastnode = '';
26929         while (currentElementChild) {
26930             // Formatting code (indent the tree so it looks nice on the screen)
26931             var nopad = nopadtext;
26932             if (lastnode == 'SPAN') {
26933                 nopad  = true;
26934             }
26935             // text
26936             if  (currentElementChild.nodeName == '#text') {
26937                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26938                 toadd = nopadtext ? toadd : toadd.trim();
26939                 if (!nopad && toadd.length > 80) {
26940                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26941                 }
26942                 innerHTML  += toadd;
26943                 
26944                 i++;
26945                 currentElementChild = currentElement.childNodes.item(i);
26946                 lastNode = '';
26947                 continue;
26948             }
26949             allText = false;
26950             
26951             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26952                 
26953             // Recursively traverse the tree structure of the child node
26954             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26955             lastnode = currentElementChild.nodeName;
26956             i++;
26957             currentElementChild=currentElement.childNodes.item(i);
26958         }
26959         
26960         ret += innerHTML;
26961         
26962         if (!allText) {
26963                 // The remaining code is mostly for formatting the tree
26964             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26965         }
26966         
26967         
26968         if (tagName) {
26969             ret+= "</"+tagName+">";
26970         }
26971         return ret;
26972         
26973     },
26974         
26975     applyBlacklists : function()
26976     {
26977         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26978         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26979         
26980         this.white = [];
26981         this.black = [];
26982         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26983             if (b.indexOf(tag) > -1) {
26984                 return;
26985             }
26986             this.white.push(tag);
26987             
26988         }, this);
26989         
26990         Roo.each(w, function(tag) {
26991             if (b.indexOf(tag) > -1) {
26992                 return;
26993             }
26994             if (this.white.indexOf(tag) > -1) {
26995                 return;
26996             }
26997             this.white.push(tag);
26998             
26999         }, this);
27000         
27001         
27002         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27003             if (w.indexOf(tag) > -1) {
27004                 return;
27005             }
27006             this.black.push(tag);
27007             
27008         }, this);
27009         
27010         Roo.each(b, function(tag) {
27011             if (w.indexOf(tag) > -1) {
27012                 return;
27013             }
27014             if (this.black.indexOf(tag) > -1) {
27015                 return;
27016             }
27017             this.black.push(tag);
27018             
27019         }, this);
27020         
27021         
27022         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27023         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27024         
27025         this.cwhite = [];
27026         this.cblack = [];
27027         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27028             if (b.indexOf(tag) > -1) {
27029                 return;
27030             }
27031             this.cwhite.push(tag);
27032             
27033         }, this);
27034         
27035         Roo.each(w, function(tag) {
27036             if (b.indexOf(tag) > -1) {
27037                 return;
27038             }
27039             if (this.cwhite.indexOf(tag) > -1) {
27040                 return;
27041             }
27042             this.cwhite.push(tag);
27043             
27044         }, this);
27045         
27046         
27047         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27048             if (w.indexOf(tag) > -1) {
27049                 return;
27050             }
27051             this.cblack.push(tag);
27052             
27053         }, this);
27054         
27055         Roo.each(b, function(tag) {
27056             if (w.indexOf(tag) > -1) {
27057                 return;
27058             }
27059             if (this.cblack.indexOf(tag) > -1) {
27060                 return;
27061             }
27062             this.cblack.push(tag);
27063             
27064         }, this);
27065     },
27066     
27067     setStylesheets : function(stylesheets)
27068     {
27069         if(typeof(stylesheets) == 'string'){
27070             Roo.get(this.iframe.contentDocument.head).createChild({
27071                 tag : 'link',
27072                 rel : 'stylesheet',
27073                 type : 'text/css',
27074                 href : stylesheets
27075             });
27076             
27077             return;
27078         }
27079         var _this = this;
27080      
27081         Roo.each(stylesheets, function(s) {
27082             if(!s.length){
27083                 return;
27084             }
27085             
27086             Roo.get(_this.iframe.contentDocument.head).createChild({
27087                 tag : 'link',
27088                 rel : 'stylesheet',
27089                 type : 'text/css',
27090                 href : s
27091             });
27092         });
27093
27094         
27095     },
27096     
27097     removeStylesheets : function()
27098     {
27099         var _this = this;
27100         
27101         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27102             s.remove();
27103         });
27104     },
27105     
27106     setStyle : function(style)
27107     {
27108         Roo.get(this.iframe.contentDocument.head).createChild({
27109             tag : 'style',
27110             type : 'text/css',
27111             html : style
27112         });
27113
27114         return;
27115     }
27116     
27117     // hide stuff that is not compatible
27118     /**
27119      * @event blur
27120      * @hide
27121      */
27122     /**
27123      * @event change
27124      * @hide
27125      */
27126     /**
27127      * @event focus
27128      * @hide
27129      */
27130     /**
27131      * @event specialkey
27132      * @hide
27133      */
27134     /**
27135      * @cfg {String} fieldClass @hide
27136      */
27137     /**
27138      * @cfg {String} focusClass @hide
27139      */
27140     /**
27141      * @cfg {String} autoCreate @hide
27142      */
27143     /**
27144      * @cfg {String} inputType @hide
27145      */
27146     /**
27147      * @cfg {String} invalidClass @hide
27148      */
27149     /**
27150      * @cfg {String} invalidText @hide
27151      */
27152     /**
27153      * @cfg {String} msgFx @hide
27154      */
27155     /**
27156      * @cfg {String} validateOnBlur @hide
27157      */
27158 });
27159
27160 Roo.HtmlEditorCore.white = [
27161         'area', 'br', 'img', 'input', 'hr', 'wbr',
27162         
27163        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27164        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27165        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27166        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27167        'table',   'ul',         'xmp', 
27168        
27169        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27170       'thead',   'tr', 
27171      
27172       'dir', 'menu', 'ol', 'ul', 'dl',
27173        
27174       'embed',  'object'
27175 ];
27176
27177
27178 Roo.HtmlEditorCore.black = [
27179     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27180         'applet', // 
27181         'base',   'basefont', 'bgsound', 'blink',  'body', 
27182         'frame',  'frameset', 'head',    'html',   'ilayer', 
27183         'iframe', 'layer',  'link',     'meta',    'object',   
27184         'script', 'style' ,'title',  'xml' // clean later..
27185 ];
27186 Roo.HtmlEditorCore.clean = [
27187     'script', 'style', 'title', 'xml'
27188 ];
27189 Roo.HtmlEditorCore.remove = [
27190     'font'
27191 ];
27192 // attributes..
27193
27194 Roo.HtmlEditorCore.ablack = [
27195     'on'
27196 ];
27197     
27198 Roo.HtmlEditorCore.aclean = [ 
27199     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27200 ];
27201
27202 // protocols..
27203 Roo.HtmlEditorCore.pwhite= [
27204         'http',  'https',  'mailto'
27205 ];
27206
27207 // white listed style attributes.
27208 Roo.HtmlEditorCore.cwhite= [
27209       //  'text-align', /// default is to allow most things..
27210       
27211          
27212 //        'font-size'//??
27213 ];
27214
27215 // black listed style attributes.
27216 Roo.HtmlEditorCore.cblack= [
27217       //  'font-size' -- this can be set by the project 
27218 ];
27219
27220
27221 Roo.HtmlEditorCore.swapCodes   =[ 
27222     [    8211, "&#8211;" ], 
27223     [    8212, "&#8212;" ], 
27224     [    8216,  "'" ],  
27225     [    8217, "'" ],  
27226     [    8220, '"' ],  
27227     [    8221, '"' ],  
27228     [    8226, "*" ],  
27229     [    8230, "..." ]
27230 ]; 
27231
27232     /*
27233  * - LGPL
27234  *
27235  * HtmlEditor
27236  * 
27237  */
27238
27239 /**
27240  * @class Roo.bootstrap.HtmlEditor
27241  * @extends Roo.bootstrap.TextArea
27242  * Bootstrap HtmlEditor class
27243
27244  * @constructor
27245  * Create a new HtmlEditor
27246  * @param {Object} config The config object
27247  */
27248
27249 Roo.bootstrap.HtmlEditor = function(config){
27250     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27251     if (!this.toolbars) {
27252         this.toolbars = [];
27253     }
27254     
27255     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27256     this.addEvents({
27257             /**
27258              * @event initialize
27259              * Fires when the editor is fully initialized (including the iframe)
27260              * @param {HtmlEditor} this
27261              */
27262             initialize: true,
27263             /**
27264              * @event activate
27265              * Fires when the editor is first receives the focus. Any insertion must wait
27266              * until after this event.
27267              * @param {HtmlEditor} this
27268              */
27269             activate: true,
27270              /**
27271              * @event beforesync
27272              * Fires before the textarea is updated with content from the editor iframe. Return false
27273              * to cancel the sync.
27274              * @param {HtmlEditor} this
27275              * @param {String} html
27276              */
27277             beforesync: true,
27278              /**
27279              * @event beforepush
27280              * Fires before the iframe editor is updated with content from the textarea. Return false
27281              * to cancel the push.
27282              * @param {HtmlEditor} this
27283              * @param {String} html
27284              */
27285             beforepush: true,
27286              /**
27287              * @event sync
27288              * Fires when the textarea is updated with content from the editor iframe.
27289              * @param {HtmlEditor} this
27290              * @param {String} html
27291              */
27292             sync: true,
27293              /**
27294              * @event push
27295              * Fires when the iframe editor is updated with content from the textarea.
27296              * @param {HtmlEditor} this
27297              * @param {String} html
27298              */
27299             push: true,
27300              /**
27301              * @event editmodechange
27302              * Fires when the editor switches edit modes
27303              * @param {HtmlEditor} this
27304              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27305              */
27306             editmodechange: true,
27307             /**
27308              * @event editorevent
27309              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27310              * @param {HtmlEditor} this
27311              */
27312             editorevent: true,
27313             /**
27314              * @event firstfocus
27315              * Fires when on first focus - needed by toolbars..
27316              * @param {HtmlEditor} this
27317              */
27318             firstfocus: true,
27319             /**
27320              * @event autosave
27321              * Auto save the htmlEditor value as a file into Events
27322              * @param {HtmlEditor} this
27323              */
27324             autosave: true,
27325             /**
27326              * @event savedpreview
27327              * preview the saved version of htmlEditor
27328              * @param {HtmlEditor} this
27329              */
27330             savedpreview: true
27331         });
27332 };
27333
27334
27335 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27336     
27337     
27338       /**
27339      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27340      */
27341     toolbars : false,
27342     
27343      /**
27344     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27345     */
27346     btns : [],
27347    
27348      /**
27349      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27350      *                        Roo.resizable.
27351      */
27352     resizable : false,
27353      /**
27354      * @cfg {Number} height (in pixels)
27355      */   
27356     height: 300,
27357    /**
27358      * @cfg {Number} width (in pixels)
27359      */   
27360     width: false,
27361     
27362     /**
27363      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27364      * 
27365      */
27366     stylesheets: false,
27367     
27368     // id of frame..
27369     frameId: false,
27370     
27371     // private properties
27372     validationEvent : false,
27373     deferHeight: true,
27374     initialized : false,
27375     activated : false,
27376     
27377     onFocus : Roo.emptyFn,
27378     iframePad:3,
27379     hideMode:'offsets',
27380     
27381     tbContainer : false,
27382     
27383     bodyCls : '',
27384     
27385     toolbarContainer :function() {
27386         return this.wrap.select('.x-html-editor-tb',true).first();
27387     },
27388
27389     /**
27390      * Protected method that will not generally be called directly. It
27391      * is called when the editor creates its toolbar. Override this method if you need to
27392      * add custom toolbar buttons.
27393      * @param {HtmlEditor} editor
27394      */
27395     createToolbar : function(){
27396         Roo.log('renewing');
27397         Roo.log("create toolbars");
27398         
27399         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27400         this.toolbars[0].render(this.toolbarContainer());
27401         
27402         return;
27403         
27404 //        if (!editor.toolbars || !editor.toolbars.length) {
27405 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27406 //        }
27407 //        
27408 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27409 //            editor.toolbars[i] = Roo.factory(
27410 //                    typeof(editor.toolbars[i]) == 'string' ?
27411 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27412 //                Roo.bootstrap.HtmlEditor);
27413 //            editor.toolbars[i].init(editor);
27414 //        }
27415     },
27416
27417      
27418     // private
27419     onRender : function(ct, position)
27420     {
27421        // Roo.log("Call onRender: " + this.xtype);
27422         var _t = this;
27423         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27424       
27425         this.wrap = this.inputEl().wrap({
27426             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27427         });
27428         
27429         this.editorcore.onRender(ct, position);
27430          
27431         if (this.resizable) {
27432             this.resizeEl = new Roo.Resizable(this.wrap, {
27433                 pinned : true,
27434                 wrap: true,
27435                 dynamic : true,
27436                 minHeight : this.height,
27437                 height: this.height,
27438                 handles : this.resizable,
27439                 width: this.width,
27440                 listeners : {
27441                     resize : function(r, w, h) {
27442                         _t.onResize(w,h); // -something
27443                     }
27444                 }
27445             });
27446             
27447         }
27448         this.createToolbar(this);
27449        
27450         
27451         if(!this.width && this.resizable){
27452             this.setSize(this.wrap.getSize());
27453         }
27454         if (this.resizeEl) {
27455             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27456             // should trigger onReize..
27457         }
27458         
27459     },
27460
27461     // private
27462     onResize : function(w, h)
27463     {
27464         Roo.log('resize: ' +w + ',' + h );
27465         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27466         var ew = false;
27467         var eh = false;
27468         
27469         if(this.inputEl() ){
27470             if(typeof w == 'number'){
27471                 var aw = w - this.wrap.getFrameWidth('lr');
27472                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27473                 ew = aw;
27474             }
27475             if(typeof h == 'number'){
27476                  var tbh = -11;  // fixme it needs to tool bar size!
27477                 for (var i =0; i < this.toolbars.length;i++) {
27478                     // fixme - ask toolbars for heights?
27479                     tbh += this.toolbars[i].el.getHeight();
27480                     //if (this.toolbars[i].footer) {
27481                     //    tbh += this.toolbars[i].footer.el.getHeight();
27482                     //}
27483                 }
27484               
27485                 
27486                 
27487                 
27488                 
27489                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27490                 ah -= 5; // knock a few pixes off for look..
27491                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27492                 var eh = ah;
27493             }
27494         }
27495         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27496         this.editorcore.onResize(ew,eh);
27497         
27498     },
27499
27500     /**
27501      * Toggles the editor between standard and source edit mode.
27502      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27503      */
27504     toggleSourceEdit : function(sourceEditMode)
27505     {
27506         this.editorcore.toggleSourceEdit(sourceEditMode);
27507         
27508         if(this.editorcore.sourceEditMode){
27509             Roo.log('editor - showing textarea');
27510             
27511 //            Roo.log('in');
27512 //            Roo.log(this.syncValue());
27513             this.syncValue();
27514             this.inputEl().removeClass(['hide', 'x-hidden']);
27515             this.inputEl().dom.removeAttribute('tabIndex');
27516             this.inputEl().focus();
27517         }else{
27518             Roo.log('editor - hiding textarea');
27519 //            Roo.log('out')
27520 //            Roo.log(this.pushValue()); 
27521             this.pushValue();
27522             
27523             this.inputEl().addClass(['hide', 'x-hidden']);
27524             this.inputEl().dom.setAttribute('tabIndex', -1);
27525             //this.deferFocus();
27526         }
27527          
27528         if(this.resizable){
27529             this.setSize(this.wrap.getSize());
27530         }
27531         
27532         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27533     },
27534  
27535     // private (for BoxComponent)
27536     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27537
27538     // private (for BoxComponent)
27539     getResizeEl : function(){
27540         return this.wrap;
27541     },
27542
27543     // private (for BoxComponent)
27544     getPositionEl : function(){
27545         return this.wrap;
27546     },
27547
27548     // private
27549     initEvents : function(){
27550         this.originalValue = this.getValue();
27551     },
27552
27553 //    /**
27554 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27555 //     * @method
27556 //     */
27557 //    markInvalid : Roo.emptyFn,
27558 //    /**
27559 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27560 //     * @method
27561 //     */
27562 //    clearInvalid : Roo.emptyFn,
27563
27564     setValue : function(v){
27565         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27566         this.editorcore.pushValue();
27567     },
27568
27569      
27570     // private
27571     deferFocus : function(){
27572         this.focus.defer(10, this);
27573     },
27574
27575     // doc'ed in Field
27576     focus : function(){
27577         this.editorcore.focus();
27578         
27579     },
27580       
27581
27582     // private
27583     onDestroy : function(){
27584         
27585         
27586         
27587         if(this.rendered){
27588             
27589             for (var i =0; i < this.toolbars.length;i++) {
27590                 // fixme - ask toolbars for heights?
27591                 this.toolbars[i].onDestroy();
27592             }
27593             
27594             this.wrap.dom.innerHTML = '';
27595             this.wrap.remove();
27596         }
27597     },
27598
27599     // private
27600     onFirstFocus : function(){
27601         //Roo.log("onFirstFocus");
27602         this.editorcore.onFirstFocus();
27603          for (var i =0; i < this.toolbars.length;i++) {
27604             this.toolbars[i].onFirstFocus();
27605         }
27606         
27607     },
27608     
27609     // private
27610     syncValue : function()
27611     {   
27612         this.editorcore.syncValue();
27613     },
27614     
27615     pushValue : function()
27616     {   
27617         this.editorcore.pushValue();
27618     }
27619      
27620     
27621     // hide stuff that is not compatible
27622     /**
27623      * @event blur
27624      * @hide
27625      */
27626     /**
27627      * @event change
27628      * @hide
27629      */
27630     /**
27631      * @event focus
27632      * @hide
27633      */
27634     /**
27635      * @event specialkey
27636      * @hide
27637      */
27638     /**
27639      * @cfg {String} fieldClass @hide
27640      */
27641     /**
27642      * @cfg {String} focusClass @hide
27643      */
27644     /**
27645      * @cfg {String} autoCreate @hide
27646      */
27647     /**
27648      * @cfg {String} inputType @hide
27649      */
27650      
27651     /**
27652      * @cfg {String} invalidText @hide
27653      */
27654     /**
27655      * @cfg {String} msgFx @hide
27656      */
27657     /**
27658      * @cfg {String} validateOnBlur @hide
27659      */
27660 });
27661  
27662     
27663    
27664    
27665    
27666       
27667 Roo.namespace('Roo.bootstrap.htmleditor');
27668 /**
27669  * @class Roo.bootstrap.HtmlEditorToolbar1
27670  * Basic Toolbar
27671  * 
27672  * @example
27673  * Usage:
27674  *
27675  new Roo.bootstrap.HtmlEditor({
27676     ....
27677     toolbars : [
27678         new Roo.bootstrap.HtmlEditorToolbar1({
27679             disable : { fonts: 1 , format: 1, ..., ... , ...],
27680             btns : [ .... ]
27681         })
27682     }
27683      
27684  * 
27685  * @cfg {Object} disable List of elements to disable..
27686  * @cfg {Array} btns List of additional buttons.
27687  * 
27688  * 
27689  * NEEDS Extra CSS? 
27690  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27691  */
27692  
27693 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27694 {
27695     
27696     Roo.apply(this, config);
27697     
27698     // default disabled, based on 'good practice'..
27699     this.disable = this.disable || {};
27700     Roo.applyIf(this.disable, {
27701         fontSize : true,
27702         colors : true,
27703         specialElements : true
27704     });
27705     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27706     
27707     this.editor = config.editor;
27708     this.editorcore = config.editor.editorcore;
27709     
27710     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27711     
27712     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27713     // dont call parent... till later.
27714 }
27715 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27716      
27717     bar : true,
27718     
27719     editor : false,
27720     editorcore : false,
27721     
27722     
27723     formats : [
27724         "p" ,  
27725         "h1","h2","h3","h4","h5","h6", 
27726         "pre", "code", 
27727         "abbr", "acronym", "address", "cite", "samp", "var",
27728         'div','span'
27729     ],
27730     
27731     onRender : function(ct, position)
27732     {
27733        // Roo.log("Call onRender: " + this.xtype);
27734         
27735        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27736        Roo.log(this.el);
27737        this.el.dom.style.marginBottom = '0';
27738        var _this = this;
27739        var editorcore = this.editorcore;
27740        var editor= this.editor;
27741        
27742        var children = [];
27743        var btn = function(id,cmd , toggle, handler, html){
27744        
27745             var  event = toggle ? 'toggle' : 'click';
27746        
27747             var a = {
27748                 size : 'sm',
27749                 xtype: 'Button',
27750                 xns: Roo.bootstrap,
27751                 //glyphicon : id,
27752                 fa: id,
27753                 cmd : id || cmd,
27754                 enableToggle:toggle !== false,
27755                 html : html || '',
27756                 pressed : toggle ? false : null,
27757                 listeners : {}
27758             };
27759             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27760                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27761             };
27762             children.push(a);
27763             return a;
27764        }
27765        
27766     //    var cb_box = function...
27767         
27768         var style = {
27769                 xtype: 'Button',
27770                 size : 'sm',
27771                 xns: Roo.bootstrap,
27772                 fa : 'font',
27773                 //html : 'submit'
27774                 menu : {
27775                     xtype: 'Menu',
27776                     xns: Roo.bootstrap,
27777                     items:  []
27778                 }
27779         };
27780         Roo.each(this.formats, function(f) {
27781             style.menu.items.push({
27782                 xtype :'MenuItem',
27783                 xns: Roo.bootstrap,
27784                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27785                 tagname : f,
27786                 listeners : {
27787                     click : function()
27788                     {
27789                         editorcore.insertTag(this.tagname);
27790                         editor.focus();
27791                     }
27792                 }
27793                 
27794             });
27795         });
27796         children.push(style);   
27797         
27798         btn('bold',false,true);
27799         btn('italic',false,true);
27800         btn('align-left', 'justifyleft',true);
27801         btn('align-center', 'justifycenter',true);
27802         btn('align-right' , 'justifyright',true);
27803         btn('link', false, false, function(btn) {
27804             //Roo.log("create link?");
27805             var url = prompt(this.createLinkText, this.defaultLinkValue);
27806             if(url && url != 'http:/'+'/'){
27807                 this.editorcore.relayCmd('createlink', url);
27808             }
27809         }),
27810         btn('list','insertunorderedlist',true);
27811         btn('pencil', false,true, function(btn){
27812                 Roo.log(this);
27813                 this.toggleSourceEdit(btn.pressed);
27814         });
27815         
27816         if (this.editor.btns.length > 0) {
27817             for (var i = 0; i<this.editor.btns.length; i++) {
27818                 children.push(this.editor.btns[i]);
27819             }
27820         }
27821         
27822         /*
27823         var cog = {
27824                 xtype: 'Button',
27825                 size : 'sm',
27826                 xns: Roo.bootstrap,
27827                 glyphicon : 'cog',
27828                 //html : 'submit'
27829                 menu : {
27830                     xtype: 'Menu',
27831                     xns: Roo.bootstrap,
27832                     items:  []
27833                 }
27834         };
27835         
27836         cog.menu.items.push({
27837             xtype :'MenuItem',
27838             xns: Roo.bootstrap,
27839             html : Clean styles,
27840             tagname : f,
27841             listeners : {
27842                 click : function()
27843                 {
27844                     editorcore.insertTag(this.tagname);
27845                     editor.focus();
27846                 }
27847             }
27848             
27849         });
27850        */
27851         
27852          
27853        this.xtype = 'NavSimplebar';
27854         
27855         for(var i=0;i< children.length;i++) {
27856             
27857             this.buttons.add(this.addxtypeChild(children[i]));
27858             
27859         }
27860         
27861         editor.on('editorevent', this.updateToolbar, this);
27862     },
27863     onBtnClick : function(id)
27864     {
27865        this.editorcore.relayCmd(id);
27866        this.editorcore.focus();
27867     },
27868     
27869     /**
27870      * Protected method that will not generally be called directly. It triggers
27871      * a toolbar update by reading the markup state of the current selection in the editor.
27872      */
27873     updateToolbar: function(){
27874
27875         if(!this.editorcore.activated){
27876             this.editor.onFirstFocus(); // is this neeed?
27877             return;
27878         }
27879
27880         var btns = this.buttons; 
27881         var doc = this.editorcore.doc;
27882         btns.get('bold').setActive(doc.queryCommandState('bold'));
27883         btns.get('italic').setActive(doc.queryCommandState('italic'));
27884         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27885         
27886         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27887         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27888         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27889         
27890         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27891         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27892          /*
27893         
27894         var ans = this.editorcore.getAllAncestors();
27895         if (this.formatCombo) {
27896             
27897             
27898             var store = this.formatCombo.store;
27899             this.formatCombo.setValue("");
27900             for (var i =0; i < ans.length;i++) {
27901                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27902                     // select it..
27903                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27904                     break;
27905                 }
27906             }
27907         }
27908         
27909         
27910         
27911         // hides menus... - so this cant be on a menu...
27912         Roo.bootstrap.MenuMgr.hideAll();
27913         */
27914         Roo.bootstrap.MenuMgr.hideAll();
27915         //this.editorsyncValue();
27916     },
27917     onFirstFocus: function() {
27918         this.buttons.each(function(item){
27919            item.enable();
27920         });
27921     },
27922     toggleSourceEdit : function(sourceEditMode){
27923         
27924           
27925         if(sourceEditMode){
27926             Roo.log("disabling buttons");
27927            this.buttons.each( function(item){
27928                 if(item.cmd != 'pencil'){
27929                     item.disable();
27930                 }
27931             });
27932           
27933         }else{
27934             Roo.log("enabling buttons");
27935             if(this.editorcore.initialized){
27936                 this.buttons.each( function(item){
27937                     item.enable();
27938                 });
27939             }
27940             
27941         }
27942         Roo.log("calling toggole on editor");
27943         // tell the editor that it's been pressed..
27944         this.editor.toggleSourceEdit(sourceEditMode);
27945        
27946     }
27947 });
27948
27949
27950
27951
27952  
27953 /*
27954  * - LGPL
27955  */
27956
27957 /**
27958  * @class Roo.bootstrap.Markdown
27959  * @extends Roo.bootstrap.TextArea
27960  * Bootstrap Showdown editable area
27961  * @cfg {string} content
27962  * 
27963  * @constructor
27964  * Create a new Showdown
27965  */
27966
27967 Roo.bootstrap.Markdown = function(config){
27968     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27969    
27970 };
27971
27972 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27973     
27974     editing :false,
27975     
27976     initEvents : function()
27977     {
27978         
27979         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27980         this.markdownEl = this.el.createChild({
27981             cls : 'roo-markdown-area'
27982         });
27983         this.inputEl().addClass('d-none');
27984         if (this.getValue() == '') {
27985             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27986             
27987         } else {
27988             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27989         }
27990         this.markdownEl.on('click', this.toggleTextEdit, this);
27991         this.on('blur', this.toggleTextEdit, this);
27992         this.on('specialkey', this.resizeTextArea, this);
27993     },
27994     
27995     toggleTextEdit : function()
27996     {
27997         var sh = this.markdownEl.getHeight();
27998         this.inputEl().addClass('d-none');
27999         this.markdownEl.addClass('d-none');
28000         if (!this.editing) {
28001             // show editor?
28002             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28003             this.inputEl().removeClass('d-none');
28004             this.inputEl().focus();
28005             this.editing = true;
28006             return;
28007         }
28008         // show showdown...
28009         this.updateMarkdown();
28010         this.markdownEl.removeClass('d-none');
28011         this.editing = false;
28012         return;
28013     },
28014     updateMarkdown : function()
28015     {
28016         if (this.getValue() == '') {
28017             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28018             return;
28019         }
28020  
28021         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28022     },
28023     
28024     resizeTextArea: function () {
28025         
28026         var sh = 100;
28027         Roo.log([sh, this.getValue().split("\n").length * 30]);
28028         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28029     },
28030     setValue : function(val)
28031     {
28032         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28033         if (!this.editing) {
28034             this.updateMarkdown();
28035         }
28036         
28037     },
28038     focus : function()
28039     {
28040         if (!this.editing) {
28041             this.toggleTextEdit();
28042         }
28043         
28044     }
28045
28046
28047 });/*
28048  * Based on:
28049  * Ext JS Library 1.1.1
28050  * Copyright(c) 2006-2007, Ext JS, LLC.
28051  *
28052  * Originally Released Under LGPL - original licence link has changed is not relivant.
28053  *
28054  * Fork - LGPL
28055  * <script type="text/javascript">
28056  */
28057  
28058 /**
28059  * @class Roo.bootstrap.PagingToolbar
28060  * @extends Roo.bootstrap.NavSimplebar
28061  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28062  * @constructor
28063  * Create a new PagingToolbar
28064  * @param {Object} config The config object
28065  * @param {Roo.data.Store} store
28066  */
28067 Roo.bootstrap.PagingToolbar = function(config)
28068 {
28069     // old args format still supported... - xtype is prefered..
28070         // created from xtype...
28071     
28072     this.ds = config.dataSource;
28073     
28074     if (config.store && !this.ds) {
28075         this.store= Roo.factory(config.store, Roo.data);
28076         this.ds = this.store;
28077         this.ds.xmodule = this.xmodule || false;
28078     }
28079     
28080     this.toolbarItems = [];
28081     if (config.items) {
28082         this.toolbarItems = config.items;
28083     }
28084     
28085     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28086     
28087     this.cursor = 0;
28088     
28089     if (this.ds) { 
28090         this.bind(this.ds);
28091     }
28092     
28093     if (Roo.bootstrap.version == 4) {
28094         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28095     } else {
28096         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28097     }
28098     
28099 };
28100
28101 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28102     /**
28103      * @cfg {Roo.data.Store} dataSource
28104      * The underlying data store providing the paged data
28105      */
28106     /**
28107      * @cfg {String/HTMLElement/Element} container
28108      * container The id or element that will contain the toolbar
28109      */
28110     /**
28111      * @cfg {Boolean} displayInfo
28112      * True to display the displayMsg (defaults to false)
28113      */
28114     /**
28115      * @cfg {Number} pageSize
28116      * The number of records to display per page (defaults to 20)
28117      */
28118     pageSize: 20,
28119     /**
28120      * @cfg {String} displayMsg
28121      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28122      */
28123     displayMsg : 'Displaying {0} - {1} of {2}',
28124     /**
28125      * @cfg {String} emptyMsg
28126      * The message to display when no records are found (defaults to "No data to display")
28127      */
28128     emptyMsg : 'No data to display',
28129     /**
28130      * Customizable piece of the default paging text (defaults to "Page")
28131      * @type String
28132      */
28133     beforePageText : "Page",
28134     /**
28135      * Customizable piece of the default paging text (defaults to "of %0")
28136      * @type String
28137      */
28138     afterPageText : "of {0}",
28139     /**
28140      * Customizable piece of the default paging text (defaults to "First Page")
28141      * @type String
28142      */
28143     firstText : "First Page",
28144     /**
28145      * Customizable piece of the default paging text (defaults to "Previous Page")
28146      * @type String
28147      */
28148     prevText : "Previous Page",
28149     /**
28150      * Customizable piece of the default paging text (defaults to "Next Page")
28151      * @type String
28152      */
28153     nextText : "Next Page",
28154     /**
28155      * Customizable piece of the default paging text (defaults to "Last Page")
28156      * @type String
28157      */
28158     lastText : "Last Page",
28159     /**
28160      * Customizable piece of the default paging text (defaults to "Refresh")
28161      * @type String
28162      */
28163     refreshText : "Refresh",
28164
28165     buttons : false,
28166     // private
28167     onRender : function(ct, position) 
28168     {
28169         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28170         this.navgroup.parentId = this.id;
28171         this.navgroup.onRender(this.el, null);
28172         // add the buttons to the navgroup
28173         
28174         if(this.displayInfo){
28175             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28176             this.displayEl = this.el.select('.x-paging-info', true).first();
28177 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28178 //            this.displayEl = navel.el.select('span',true).first();
28179         }
28180         
28181         var _this = this;
28182         
28183         if(this.buttons){
28184             Roo.each(_this.buttons, function(e){ // this might need to use render????
28185                Roo.factory(e).render(_this.el);
28186             });
28187         }
28188             
28189         Roo.each(_this.toolbarItems, function(e) {
28190             _this.navgroup.addItem(e);
28191         });
28192         
28193         
28194         this.first = this.navgroup.addItem({
28195             tooltip: this.firstText,
28196             cls: "prev btn-outline-secondary",
28197             html : ' <i class="fa fa-step-backward"></i>',
28198             disabled: true,
28199             preventDefault: true,
28200             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28201         });
28202         
28203         this.prev =  this.navgroup.addItem({
28204             tooltip: this.prevText,
28205             cls: "prev btn-outline-secondary",
28206             html : ' <i class="fa fa-backward"></i>',
28207             disabled: true,
28208             preventDefault: true,
28209             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28210         });
28211     //this.addSeparator();
28212         
28213         
28214         var field = this.navgroup.addItem( {
28215             tagtype : 'span',
28216             cls : 'x-paging-position  btn-outline-secondary',
28217              disabled: true,
28218             html : this.beforePageText  +
28219                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28220                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28221          } ); //?? escaped?
28222         
28223         this.field = field.el.select('input', true).first();
28224         this.field.on("keydown", this.onPagingKeydown, this);
28225         this.field.on("focus", function(){this.dom.select();});
28226     
28227     
28228         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28229         //this.field.setHeight(18);
28230         //this.addSeparator();
28231         this.next = this.navgroup.addItem({
28232             tooltip: this.nextText,
28233             cls: "next btn-outline-secondary",
28234             html : ' <i class="fa fa-forward"></i>',
28235             disabled: true,
28236             preventDefault: true,
28237             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28238         });
28239         this.last = this.navgroup.addItem({
28240             tooltip: this.lastText,
28241             html : ' <i class="fa fa-step-forward"></i>',
28242             cls: "next btn-outline-secondary",
28243             disabled: true,
28244             preventDefault: true,
28245             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28246         });
28247     //this.addSeparator();
28248         this.loading = this.navgroup.addItem({
28249             tooltip: this.refreshText,
28250             cls: "btn-outline-secondary",
28251             html : ' <i class="fa fa-refresh"></i>',
28252             preventDefault: true,
28253             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28254         });
28255         
28256     },
28257
28258     // private
28259     updateInfo : function(){
28260         if(this.displayEl){
28261             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28262             var msg = count == 0 ?
28263                 this.emptyMsg :
28264                 String.format(
28265                     this.displayMsg,
28266                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28267                 );
28268             this.displayEl.update(msg);
28269         }
28270     },
28271
28272     // private
28273     onLoad : function(ds, r, o)
28274     {
28275         this.cursor = o.params && o.params.start ? o.params.start : 0;
28276         
28277         var d = this.getPageData(),
28278             ap = d.activePage,
28279             ps = d.pages;
28280         
28281         
28282         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28283         this.field.dom.value = ap;
28284         this.first.setDisabled(ap == 1);
28285         this.prev.setDisabled(ap == 1);
28286         this.next.setDisabled(ap == ps);
28287         this.last.setDisabled(ap == ps);
28288         this.loading.enable();
28289         this.updateInfo();
28290     },
28291
28292     // private
28293     getPageData : function(){
28294         var total = this.ds.getTotalCount();
28295         return {
28296             total : total,
28297             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28298             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28299         };
28300     },
28301
28302     // private
28303     onLoadError : function(){
28304         this.loading.enable();
28305     },
28306
28307     // private
28308     onPagingKeydown : function(e){
28309         var k = e.getKey();
28310         var d = this.getPageData();
28311         if(k == e.RETURN){
28312             var v = this.field.dom.value, pageNum;
28313             if(!v || isNaN(pageNum = parseInt(v, 10))){
28314                 this.field.dom.value = d.activePage;
28315                 return;
28316             }
28317             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28318             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28319             e.stopEvent();
28320         }
28321         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))
28322         {
28323           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28324           this.field.dom.value = pageNum;
28325           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28326           e.stopEvent();
28327         }
28328         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28329         {
28330           var v = this.field.dom.value, pageNum; 
28331           var increment = (e.shiftKey) ? 10 : 1;
28332           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28333                 increment *= -1;
28334           }
28335           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28336             this.field.dom.value = d.activePage;
28337             return;
28338           }
28339           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28340           {
28341             this.field.dom.value = parseInt(v, 10) + increment;
28342             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28343             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28344           }
28345           e.stopEvent();
28346         }
28347     },
28348
28349     // private
28350     beforeLoad : function(){
28351         if(this.loading){
28352             this.loading.disable();
28353         }
28354     },
28355
28356     // private
28357     onClick : function(which){
28358         
28359         var ds = this.ds;
28360         if (!ds) {
28361             return;
28362         }
28363         
28364         switch(which){
28365             case "first":
28366                 ds.load({params:{start: 0, limit: this.pageSize}});
28367             break;
28368             case "prev":
28369                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28370             break;
28371             case "next":
28372                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28373             break;
28374             case "last":
28375                 var total = ds.getTotalCount();
28376                 var extra = total % this.pageSize;
28377                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28378                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28379             break;
28380             case "refresh":
28381                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28382             break;
28383         }
28384     },
28385
28386     /**
28387      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28388      * @param {Roo.data.Store} store The data store to unbind
28389      */
28390     unbind : function(ds){
28391         ds.un("beforeload", this.beforeLoad, this);
28392         ds.un("load", this.onLoad, this);
28393         ds.un("loadexception", this.onLoadError, this);
28394         ds.un("remove", this.updateInfo, this);
28395         ds.un("add", this.updateInfo, this);
28396         this.ds = undefined;
28397     },
28398
28399     /**
28400      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28401      * @param {Roo.data.Store} store The data store to bind
28402      */
28403     bind : function(ds){
28404         ds.on("beforeload", this.beforeLoad, this);
28405         ds.on("load", this.onLoad, this);
28406         ds.on("loadexception", this.onLoadError, this);
28407         ds.on("remove", this.updateInfo, this);
28408         ds.on("add", this.updateInfo, this);
28409         this.ds = ds;
28410     }
28411 });/*
28412  * - LGPL
28413  *
28414  * element
28415  * 
28416  */
28417
28418 /**
28419  * @class Roo.bootstrap.MessageBar
28420  * @extends Roo.bootstrap.Component
28421  * Bootstrap MessageBar class
28422  * @cfg {String} html contents of the MessageBar
28423  * @cfg {String} weight (info | success | warning | danger) default info
28424  * @cfg {String} beforeClass insert the bar before the given class
28425  * @cfg {Boolean} closable (true | false) default false
28426  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28427  * 
28428  * @constructor
28429  * Create a new Element
28430  * @param {Object} config The config object
28431  */
28432
28433 Roo.bootstrap.MessageBar = function(config){
28434     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28435 };
28436
28437 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28438     
28439     html: '',
28440     weight: 'info',
28441     closable: false,
28442     fixed: false,
28443     beforeClass: 'bootstrap-sticky-wrap',
28444     
28445     getAutoCreate : function(){
28446         
28447         var cfg = {
28448             tag: 'div',
28449             cls: 'alert alert-dismissable alert-' + this.weight,
28450             cn: [
28451                 {
28452                     tag: 'span',
28453                     cls: 'message',
28454                     html: this.html || ''
28455                 }
28456             ]
28457         };
28458         
28459         if(this.fixed){
28460             cfg.cls += ' alert-messages-fixed';
28461         }
28462         
28463         if(this.closable){
28464             cfg.cn.push({
28465                 tag: 'button',
28466                 cls: 'close',
28467                 html: 'x'
28468             });
28469         }
28470         
28471         return cfg;
28472     },
28473     
28474     onRender : function(ct, position)
28475     {
28476         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28477         
28478         if(!this.el){
28479             var cfg = Roo.apply({},  this.getAutoCreate());
28480             cfg.id = Roo.id();
28481             
28482             if (this.cls) {
28483                 cfg.cls += ' ' + this.cls;
28484             }
28485             if (this.style) {
28486                 cfg.style = this.style;
28487             }
28488             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28489             
28490             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28491         }
28492         
28493         this.el.select('>button.close').on('click', this.hide, this);
28494         
28495     },
28496     
28497     show : function()
28498     {
28499         if (!this.rendered) {
28500             this.render();
28501         }
28502         
28503         this.el.show();
28504         
28505         this.fireEvent('show', this);
28506         
28507     },
28508     
28509     hide : function()
28510     {
28511         if (!this.rendered) {
28512             this.render();
28513         }
28514         
28515         this.el.hide();
28516         
28517         this.fireEvent('hide', this);
28518     },
28519     
28520     update : function()
28521     {
28522 //        var e = this.el.dom.firstChild;
28523 //        
28524 //        if(this.closable){
28525 //            e = e.nextSibling;
28526 //        }
28527 //        
28528 //        e.data = this.html || '';
28529
28530         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28531     }
28532    
28533 });
28534
28535  
28536
28537      /*
28538  * - LGPL
28539  *
28540  * Graph
28541  * 
28542  */
28543
28544
28545 /**
28546  * @class Roo.bootstrap.Graph
28547  * @extends Roo.bootstrap.Component
28548  * Bootstrap Graph class
28549 > Prameters
28550  -sm {number} sm 4
28551  -md {number} md 5
28552  @cfg {String} graphtype  bar | vbar | pie
28553  @cfg {number} g_x coodinator | centre x (pie)
28554  @cfg {number} g_y coodinator | centre y (pie)
28555  @cfg {number} g_r radius (pie)
28556  @cfg {number} g_height height of the chart (respected by all elements in the set)
28557  @cfg {number} g_width width of the chart (respected by all elements in the set)
28558  @cfg {Object} title The title of the chart
28559     
28560  -{Array}  values
28561  -opts (object) options for the chart 
28562      o {
28563      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28564      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28565      o vgutter (number)
28566      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.
28567      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28568      o to
28569      o stretch (boolean)
28570      o }
28571  -opts (object) options for the pie
28572      o{
28573      o cut
28574      o startAngle (number)
28575      o endAngle (number)
28576      } 
28577  *
28578  * @constructor
28579  * Create a new Input
28580  * @param {Object} config The config object
28581  */
28582
28583 Roo.bootstrap.Graph = function(config){
28584     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28585     
28586     this.addEvents({
28587         // img events
28588         /**
28589          * @event click
28590          * The img click event for the img.
28591          * @param {Roo.EventObject} e
28592          */
28593         "click" : true
28594     });
28595 };
28596
28597 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28598     
28599     sm: 4,
28600     md: 5,
28601     graphtype: 'bar',
28602     g_height: 250,
28603     g_width: 400,
28604     g_x: 50,
28605     g_y: 50,
28606     g_r: 30,
28607     opts:{
28608         //g_colors: this.colors,
28609         g_type: 'soft',
28610         g_gutter: '20%'
28611
28612     },
28613     title : false,
28614
28615     getAutoCreate : function(){
28616         
28617         var cfg = {
28618             tag: 'div',
28619             html : null
28620         };
28621         
28622         
28623         return  cfg;
28624     },
28625
28626     onRender : function(ct,position){
28627         
28628         
28629         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28630         
28631         if (typeof(Raphael) == 'undefined') {
28632             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28633             return;
28634         }
28635         
28636         this.raphael = Raphael(this.el.dom);
28637         
28638                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28639                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28640                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28641                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28642                 /*
28643                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28644                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28645                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28646                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28647                 
28648                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28649                 r.barchart(330, 10, 300, 220, data1);
28650                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28651                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28652                 */
28653                 
28654                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28655                 // r.barchart(30, 30, 560, 250,  xdata, {
28656                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28657                 //     axis : "0 0 1 1",
28658                 //     axisxlabels :  xdata
28659                 //     //yvalues : cols,
28660                    
28661                 // });
28662 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28663 //        
28664 //        this.load(null,xdata,{
28665 //                axis : "0 0 1 1",
28666 //                axisxlabels :  xdata
28667 //                });
28668
28669     },
28670
28671     load : function(graphtype,xdata,opts)
28672     {
28673         this.raphael.clear();
28674         if(!graphtype) {
28675             graphtype = this.graphtype;
28676         }
28677         if(!opts){
28678             opts = this.opts;
28679         }
28680         var r = this.raphael,
28681             fin = function () {
28682                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28683             },
28684             fout = function () {
28685                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28686             },
28687             pfin = function() {
28688                 this.sector.stop();
28689                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28690
28691                 if (this.label) {
28692                     this.label[0].stop();
28693                     this.label[0].attr({ r: 7.5 });
28694                     this.label[1].attr({ "font-weight": 800 });
28695                 }
28696             },
28697             pfout = function() {
28698                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28699
28700                 if (this.label) {
28701                     this.label[0].animate({ r: 5 }, 500, "bounce");
28702                     this.label[1].attr({ "font-weight": 400 });
28703                 }
28704             };
28705
28706         switch(graphtype){
28707             case 'bar':
28708                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28709                 break;
28710             case 'hbar':
28711                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28712                 break;
28713             case 'pie':
28714 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28715 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28716 //            
28717                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28718                 
28719                 break;
28720
28721         }
28722         
28723         if(this.title){
28724             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28725         }
28726         
28727     },
28728     
28729     setTitle: function(o)
28730     {
28731         this.title = o;
28732     },
28733     
28734     initEvents: function() {
28735         
28736         if(!this.href){
28737             this.el.on('click', this.onClick, this);
28738         }
28739     },
28740     
28741     onClick : function(e)
28742     {
28743         Roo.log('img onclick');
28744         this.fireEvent('click', this, e);
28745     }
28746    
28747 });
28748
28749  
28750 /*
28751  * - LGPL
28752  *
28753  * numberBox
28754  * 
28755  */
28756 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28757
28758 /**
28759  * @class Roo.bootstrap.dash.NumberBox
28760  * @extends Roo.bootstrap.Component
28761  * Bootstrap NumberBox class
28762  * @cfg {String} headline Box headline
28763  * @cfg {String} content Box content
28764  * @cfg {String} icon Box icon
28765  * @cfg {String} footer Footer text
28766  * @cfg {String} fhref Footer href
28767  * 
28768  * @constructor
28769  * Create a new NumberBox
28770  * @param {Object} config The config object
28771  */
28772
28773
28774 Roo.bootstrap.dash.NumberBox = function(config){
28775     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28776     
28777 };
28778
28779 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28780     
28781     headline : '',
28782     content : '',
28783     icon : '',
28784     footer : '',
28785     fhref : '',
28786     ficon : '',
28787     
28788     getAutoCreate : function(){
28789         
28790         var cfg = {
28791             tag : 'div',
28792             cls : 'small-box ',
28793             cn : [
28794                 {
28795                     tag : 'div',
28796                     cls : 'inner',
28797                     cn :[
28798                         {
28799                             tag : 'h3',
28800                             cls : 'roo-headline',
28801                             html : this.headline
28802                         },
28803                         {
28804                             tag : 'p',
28805                             cls : 'roo-content',
28806                             html : this.content
28807                         }
28808                     ]
28809                 }
28810             ]
28811         };
28812         
28813         if(this.icon){
28814             cfg.cn.push({
28815                 tag : 'div',
28816                 cls : 'icon',
28817                 cn :[
28818                     {
28819                         tag : 'i',
28820                         cls : 'ion ' + this.icon
28821                     }
28822                 ]
28823             });
28824         }
28825         
28826         if(this.footer){
28827             var footer = {
28828                 tag : 'a',
28829                 cls : 'small-box-footer',
28830                 href : this.fhref || '#',
28831                 html : this.footer
28832             };
28833             
28834             cfg.cn.push(footer);
28835             
28836         }
28837         
28838         return  cfg;
28839     },
28840
28841     onRender : function(ct,position){
28842         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28843
28844
28845        
28846                 
28847     },
28848
28849     setHeadline: function (value)
28850     {
28851         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28852     },
28853     
28854     setFooter: function (value, href)
28855     {
28856         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28857         
28858         if(href){
28859             this.el.select('a.small-box-footer',true).first().attr('href', href);
28860         }
28861         
28862     },
28863
28864     setContent: function (value)
28865     {
28866         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28867     },
28868
28869     initEvents: function() 
28870     {   
28871         
28872     }
28873     
28874 });
28875
28876  
28877 /*
28878  * - LGPL
28879  *
28880  * TabBox
28881  * 
28882  */
28883 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28884
28885 /**
28886  * @class Roo.bootstrap.dash.TabBox
28887  * @extends Roo.bootstrap.Component
28888  * Bootstrap TabBox class
28889  * @cfg {String} title Title of the TabBox
28890  * @cfg {String} icon Icon of the TabBox
28891  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28892  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28893  * 
28894  * @constructor
28895  * Create a new TabBox
28896  * @param {Object} config The config object
28897  */
28898
28899
28900 Roo.bootstrap.dash.TabBox = function(config){
28901     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28902     this.addEvents({
28903         // raw events
28904         /**
28905          * @event addpane
28906          * When a pane is added
28907          * @param {Roo.bootstrap.dash.TabPane} pane
28908          */
28909         "addpane" : true,
28910         /**
28911          * @event activatepane
28912          * When a pane is activated
28913          * @param {Roo.bootstrap.dash.TabPane} pane
28914          */
28915         "activatepane" : true
28916         
28917          
28918     });
28919     
28920     this.panes = [];
28921 };
28922
28923 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28924
28925     title : '',
28926     icon : false,
28927     showtabs : true,
28928     tabScrollable : false,
28929     
28930     getChildContainer : function()
28931     {
28932         return this.el.select('.tab-content', true).first();
28933     },
28934     
28935     getAutoCreate : function(){
28936         
28937         var header = {
28938             tag: 'li',
28939             cls: 'pull-left header',
28940             html: this.title,
28941             cn : []
28942         };
28943         
28944         if(this.icon){
28945             header.cn.push({
28946                 tag: 'i',
28947                 cls: 'fa ' + this.icon
28948             });
28949         }
28950         
28951         var h = {
28952             tag: 'ul',
28953             cls: 'nav nav-tabs pull-right',
28954             cn: [
28955                 header
28956             ]
28957         };
28958         
28959         if(this.tabScrollable){
28960             h = {
28961                 tag: 'div',
28962                 cls: 'tab-header',
28963                 cn: [
28964                     {
28965                         tag: 'ul',
28966                         cls: 'nav nav-tabs pull-right',
28967                         cn: [
28968                             header
28969                         ]
28970                     }
28971                 ]
28972             };
28973         }
28974         
28975         var cfg = {
28976             tag: 'div',
28977             cls: 'nav-tabs-custom',
28978             cn: [
28979                 h,
28980                 {
28981                     tag: 'div',
28982                     cls: 'tab-content no-padding',
28983                     cn: []
28984                 }
28985             ]
28986         };
28987
28988         return  cfg;
28989     },
28990     initEvents : function()
28991     {
28992         //Roo.log('add add pane handler');
28993         this.on('addpane', this.onAddPane, this);
28994     },
28995      /**
28996      * Updates the box title
28997      * @param {String} html to set the title to.
28998      */
28999     setTitle : function(value)
29000     {
29001         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29002     },
29003     onAddPane : function(pane)
29004     {
29005         this.panes.push(pane);
29006         //Roo.log('addpane');
29007         //Roo.log(pane);
29008         // tabs are rendere left to right..
29009         if(!this.showtabs){
29010             return;
29011         }
29012         
29013         var ctr = this.el.select('.nav-tabs', true).first();
29014          
29015          
29016         var existing = ctr.select('.nav-tab',true);
29017         var qty = existing.getCount();;
29018         
29019         
29020         var tab = ctr.createChild({
29021             tag : 'li',
29022             cls : 'nav-tab' + (qty ? '' : ' active'),
29023             cn : [
29024                 {
29025                     tag : 'a',
29026                     href:'#',
29027                     html : pane.title
29028                 }
29029             ]
29030         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29031         pane.tab = tab;
29032         
29033         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29034         if (!qty) {
29035             pane.el.addClass('active');
29036         }
29037         
29038                 
29039     },
29040     onTabClick : function(ev,un,ob,pane)
29041     {
29042         //Roo.log('tab - prev default');
29043         ev.preventDefault();
29044         
29045         
29046         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29047         pane.tab.addClass('active');
29048         //Roo.log(pane.title);
29049         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29050         // technically we should have a deactivate event.. but maybe add later.
29051         // and it should not de-activate the selected tab...
29052         this.fireEvent('activatepane', pane);
29053         pane.el.addClass('active');
29054         pane.fireEvent('activate');
29055         
29056         
29057     },
29058     
29059     getActivePane : function()
29060     {
29061         var r = false;
29062         Roo.each(this.panes, function(p) {
29063             if(p.el.hasClass('active')){
29064                 r = p;
29065                 return false;
29066             }
29067             
29068             return;
29069         });
29070         
29071         return r;
29072     }
29073     
29074     
29075 });
29076
29077  
29078 /*
29079  * - LGPL
29080  *
29081  * Tab pane
29082  * 
29083  */
29084 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29085 /**
29086  * @class Roo.bootstrap.TabPane
29087  * @extends Roo.bootstrap.Component
29088  * Bootstrap TabPane class
29089  * @cfg {Boolean} active (false | true) Default false
29090  * @cfg {String} title title of panel
29091
29092  * 
29093  * @constructor
29094  * Create a new TabPane
29095  * @param {Object} config The config object
29096  */
29097
29098 Roo.bootstrap.dash.TabPane = function(config){
29099     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29100     
29101     this.addEvents({
29102         // raw events
29103         /**
29104          * @event activate
29105          * When a pane is activated
29106          * @param {Roo.bootstrap.dash.TabPane} pane
29107          */
29108         "activate" : true
29109          
29110     });
29111 };
29112
29113 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29114     
29115     active : false,
29116     title : '',
29117     
29118     // the tabBox that this is attached to.
29119     tab : false,
29120      
29121     getAutoCreate : function() 
29122     {
29123         var cfg = {
29124             tag: 'div',
29125             cls: 'tab-pane'
29126         };
29127         
29128         if(this.active){
29129             cfg.cls += ' active';
29130         }
29131         
29132         return cfg;
29133     },
29134     initEvents  : function()
29135     {
29136         //Roo.log('trigger add pane handler');
29137         this.parent().fireEvent('addpane', this)
29138     },
29139     
29140      /**
29141      * Updates the tab title 
29142      * @param {String} html to set the title to.
29143      */
29144     setTitle: function(str)
29145     {
29146         if (!this.tab) {
29147             return;
29148         }
29149         this.title = str;
29150         this.tab.select('a', true).first().dom.innerHTML = str;
29151         
29152     }
29153     
29154     
29155     
29156 });
29157
29158  
29159
29160
29161  /*
29162  * - LGPL
29163  *
29164  * menu
29165  * 
29166  */
29167 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29168
29169 /**
29170  * @class Roo.bootstrap.menu.Menu
29171  * @extends Roo.bootstrap.Component
29172  * Bootstrap Menu class - container for Menu
29173  * @cfg {String} html Text of the menu
29174  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29175  * @cfg {String} icon Font awesome icon
29176  * @cfg {String} pos Menu align to (top | bottom) default bottom
29177  * 
29178  * 
29179  * @constructor
29180  * Create a new Menu
29181  * @param {Object} config The config object
29182  */
29183
29184
29185 Roo.bootstrap.menu.Menu = function(config){
29186     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29187     
29188     this.addEvents({
29189         /**
29190          * @event beforeshow
29191          * Fires before this menu is displayed
29192          * @param {Roo.bootstrap.menu.Menu} this
29193          */
29194         beforeshow : true,
29195         /**
29196          * @event beforehide
29197          * Fires before this menu is hidden
29198          * @param {Roo.bootstrap.menu.Menu} this
29199          */
29200         beforehide : true,
29201         /**
29202          * @event show
29203          * Fires after this menu is displayed
29204          * @param {Roo.bootstrap.menu.Menu} this
29205          */
29206         show : true,
29207         /**
29208          * @event hide
29209          * Fires after this menu is hidden
29210          * @param {Roo.bootstrap.menu.Menu} this
29211          */
29212         hide : true,
29213         /**
29214          * @event click
29215          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29216          * @param {Roo.bootstrap.menu.Menu} this
29217          * @param {Roo.EventObject} e
29218          */
29219         click : true
29220     });
29221     
29222 };
29223
29224 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29225     
29226     submenu : false,
29227     html : '',
29228     weight : 'default',
29229     icon : false,
29230     pos : 'bottom',
29231     
29232     
29233     getChildContainer : function() {
29234         if(this.isSubMenu){
29235             return this.el;
29236         }
29237         
29238         return this.el.select('ul.dropdown-menu', true).first();  
29239     },
29240     
29241     getAutoCreate : function()
29242     {
29243         var text = [
29244             {
29245                 tag : 'span',
29246                 cls : 'roo-menu-text',
29247                 html : this.html
29248             }
29249         ];
29250         
29251         if(this.icon){
29252             text.unshift({
29253                 tag : 'i',
29254                 cls : 'fa ' + this.icon
29255             })
29256         }
29257         
29258         
29259         var cfg = {
29260             tag : 'div',
29261             cls : 'btn-group',
29262             cn : [
29263                 {
29264                     tag : 'button',
29265                     cls : 'dropdown-button btn btn-' + this.weight,
29266                     cn : text
29267                 },
29268                 {
29269                     tag : 'button',
29270                     cls : 'dropdown-toggle btn btn-' + this.weight,
29271                     cn : [
29272                         {
29273                             tag : 'span',
29274                             cls : 'caret'
29275                         }
29276                     ]
29277                 },
29278                 {
29279                     tag : 'ul',
29280                     cls : 'dropdown-menu'
29281                 }
29282             ]
29283             
29284         };
29285         
29286         if(this.pos == 'top'){
29287             cfg.cls += ' dropup';
29288         }
29289         
29290         if(this.isSubMenu){
29291             cfg = {
29292                 tag : 'ul',
29293                 cls : 'dropdown-menu'
29294             }
29295         }
29296         
29297         return cfg;
29298     },
29299     
29300     onRender : function(ct, position)
29301     {
29302         this.isSubMenu = ct.hasClass('dropdown-submenu');
29303         
29304         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29305     },
29306     
29307     initEvents : function() 
29308     {
29309         if(this.isSubMenu){
29310             return;
29311         }
29312         
29313         this.hidden = true;
29314         
29315         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29316         this.triggerEl.on('click', this.onTriggerPress, this);
29317         
29318         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29319         this.buttonEl.on('click', this.onClick, this);
29320         
29321     },
29322     
29323     list : function()
29324     {
29325         if(this.isSubMenu){
29326             return this.el;
29327         }
29328         
29329         return this.el.select('ul.dropdown-menu', true).first();
29330     },
29331     
29332     onClick : function(e)
29333     {
29334         this.fireEvent("click", this, e);
29335     },
29336     
29337     onTriggerPress  : function(e)
29338     {   
29339         if (this.isVisible()) {
29340             this.hide();
29341         } else {
29342             this.show();
29343         }
29344     },
29345     
29346     isVisible : function(){
29347         return !this.hidden;
29348     },
29349     
29350     show : function()
29351     {
29352         this.fireEvent("beforeshow", this);
29353         
29354         this.hidden = false;
29355         this.el.addClass('open');
29356         
29357         Roo.get(document).on("mouseup", this.onMouseUp, this);
29358         
29359         this.fireEvent("show", this);
29360         
29361         
29362     },
29363     
29364     hide : function()
29365     {
29366         this.fireEvent("beforehide", this);
29367         
29368         this.hidden = true;
29369         this.el.removeClass('open');
29370         
29371         Roo.get(document).un("mouseup", this.onMouseUp);
29372         
29373         this.fireEvent("hide", this);
29374     },
29375     
29376     onMouseUp : function()
29377     {
29378         this.hide();
29379     }
29380     
29381 });
29382
29383  
29384  /*
29385  * - LGPL
29386  *
29387  * menu item
29388  * 
29389  */
29390 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29391
29392 /**
29393  * @class Roo.bootstrap.menu.Item
29394  * @extends Roo.bootstrap.Component
29395  * Bootstrap MenuItem class
29396  * @cfg {Boolean} submenu (true | false) default false
29397  * @cfg {String} html text of the item
29398  * @cfg {String} href the link
29399  * @cfg {Boolean} disable (true | false) default false
29400  * @cfg {Boolean} preventDefault (true | false) default true
29401  * @cfg {String} icon Font awesome icon
29402  * @cfg {String} pos Submenu align to (left | right) default right 
29403  * 
29404  * 
29405  * @constructor
29406  * Create a new Item
29407  * @param {Object} config The config object
29408  */
29409
29410
29411 Roo.bootstrap.menu.Item = function(config){
29412     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29413     this.addEvents({
29414         /**
29415          * @event mouseover
29416          * Fires when the mouse is hovering over this menu
29417          * @param {Roo.bootstrap.menu.Item} this
29418          * @param {Roo.EventObject} e
29419          */
29420         mouseover : true,
29421         /**
29422          * @event mouseout
29423          * Fires when the mouse exits this menu
29424          * @param {Roo.bootstrap.menu.Item} this
29425          * @param {Roo.EventObject} e
29426          */
29427         mouseout : true,
29428         // raw events
29429         /**
29430          * @event click
29431          * The raw click event for the entire grid.
29432          * @param {Roo.EventObject} e
29433          */
29434         click : true
29435     });
29436 };
29437
29438 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29439     
29440     submenu : false,
29441     href : '',
29442     html : '',
29443     preventDefault: true,
29444     disable : false,
29445     icon : false,
29446     pos : 'right',
29447     
29448     getAutoCreate : function()
29449     {
29450         var text = [
29451             {
29452                 tag : 'span',
29453                 cls : 'roo-menu-item-text',
29454                 html : this.html
29455             }
29456         ];
29457         
29458         if(this.icon){
29459             text.unshift({
29460                 tag : 'i',
29461                 cls : 'fa ' + this.icon
29462             })
29463         }
29464         
29465         var cfg = {
29466             tag : 'li',
29467             cn : [
29468                 {
29469                     tag : 'a',
29470                     href : this.href || '#',
29471                     cn : text
29472                 }
29473             ]
29474         };
29475         
29476         if(this.disable){
29477             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29478         }
29479         
29480         if(this.submenu){
29481             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29482             
29483             if(this.pos == 'left'){
29484                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29485             }
29486         }
29487         
29488         return cfg;
29489     },
29490     
29491     initEvents : function() 
29492     {
29493         this.el.on('mouseover', this.onMouseOver, this);
29494         this.el.on('mouseout', this.onMouseOut, this);
29495         
29496         this.el.select('a', true).first().on('click', this.onClick, this);
29497         
29498     },
29499     
29500     onClick : function(e)
29501     {
29502         if(this.preventDefault){
29503             e.preventDefault();
29504         }
29505         
29506         this.fireEvent("click", this, e);
29507     },
29508     
29509     onMouseOver : function(e)
29510     {
29511         if(this.submenu && this.pos == 'left'){
29512             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29513         }
29514         
29515         this.fireEvent("mouseover", this, e);
29516     },
29517     
29518     onMouseOut : function(e)
29519     {
29520         this.fireEvent("mouseout", this, e);
29521     }
29522 });
29523
29524  
29525
29526  /*
29527  * - LGPL
29528  *
29529  * menu separator
29530  * 
29531  */
29532 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29533
29534 /**
29535  * @class Roo.bootstrap.menu.Separator
29536  * @extends Roo.bootstrap.Component
29537  * Bootstrap Separator class
29538  * 
29539  * @constructor
29540  * Create a new Separator
29541  * @param {Object} config The config object
29542  */
29543
29544
29545 Roo.bootstrap.menu.Separator = function(config){
29546     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29547 };
29548
29549 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29550     
29551     getAutoCreate : function(){
29552         var cfg = {
29553             tag : 'li',
29554             cls: 'dropdown-divider divider'
29555         };
29556         
29557         return cfg;
29558     }
29559    
29560 });
29561
29562  
29563
29564  /*
29565  * - LGPL
29566  *
29567  * Tooltip
29568  * 
29569  */
29570
29571 /**
29572  * @class Roo.bootstrap.Tooltip
29573  * Bootstrap Tooltip class
29574  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29575  * to determine which dom element triggers the tooltip.
29576  * 
29577  * It needs to add support for additional attributes like tooltip-position
29578  * 
29579  * @constructor
29580  * Create a new Toolti
29581  * @param {Object} config The config object
29582  */
29583
29584 Roo.bootstrap.Tooltip = function(config){
29585     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29586     
29587     this.alignment = Roo.bootstrap.Tooltip.alignment;
29588     
29589     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29590         this.alignment = config.alignment;
29591     }
29592     
29593 };
29594
29595 Roo.apply(Roo.bootstrap.Tooltip, {
29596     /**
29597      * @function init initialize tooltip monitoring.
29598      * @static
29599      */
29600     currentEl : false,
29601     currentTip : false,
29602     currentRegion : false,
29603     
29604     //  init : delay?
29605     
29606     init : function()
29607     {
29608         Roo.get(document).on('mouseover', this.enter ,this);
29609         Roo.get(document).on('mouseout', this.leave, this);
29610          
29611         
29612         this.currentTip = new Roo.bootstrap.Tooltip();
29613     },
29614     
29615     enter : function(ev)
29616     {
29617         var dom = ev.getTarget();
29618         
29619         //Roo.log(['enter',dom]);
29620         var el = Roo.fly(dom);
29621         if (this.currentEl) {
29622             //Roo.log(dom);
29623             //Roo.log(this.currentEl);
29624             //Roo.log(this.currentEl.contains(dom));
29625             if (this.currentEl == el) {
29626                 return;
29627             }
29628             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29629                 return;
29630             }
29631
29632         }
29633         
29634         if (this.currentTip.el) {
29635             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29636         }    
29637         //Roo.log(ev);
29638         
29639         if(!el || el.dom == document){
29640             return;
29641         }
29642         
29643         var bindEl = el; 
29644         var pel = false;
29645         if (!el.attr('tooltip')) {
29646             pel = el.findParent("[tooltip]");
29647             if (pel) {
29648                 bindEl = Roo.get(pel);
29649             }
29650         }
29651         
29652        
29653         
29654         // you can not look for children, as if el is the body.. then everythign is the child..
29655         if (!pel && !el.attr('tooltip')) { //
29656             if (!el.select("[tooltip]").elements.length) {
29657                 return;
29658             }
29659             // is the mouse over this child...?
29660             bindEl = el.select("[tooltip]").first();
29661             var xy = ev.getXY();
29662             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29663                 //Roo.log("not in region.");
29664                 return;
29665             }
29666             //Roo.log("child element over..");
29667             
29668         }
29669         this.currentEl = el;
29670         this.currentTip.bind(bindEl);
29671         this.currentRegion = Roo.lib.Region.getRegion(dom);
29672         this.currentTip.enter();
29673         
29674     },
29675     leave : function(ev)
29676     {
29677         var dom = ev.getTarget();
29678         //Roo.log(['leave',dom]);
29679         if (!this.currentEl) {
29680             return;
29681         }
29682         
29683         
29684         if (dom != this.currentEl.dom) {
29685             return;
29686         }
29687         var xy = ev.getXY();
29688         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29689             return;
29690         }
29691         // only activate leave if mouse cursor is outside... bounding box..
29692         
29693         
29694         
29695         
29696         if (this.currentTip) {
29697             this.currentTip.leave();
29698         }
29699         //Roo.log('clear currentEl');
29700         this.currentEl = false;
29701         
29702         
29703     },
29704     alignment : {
29705         'left' : ['r-l', [-2,0], 'right'],
29706         'right' : ['l-r', [2,0], 'left'],
29707         'bottom' : ['t-b', [0,2], 'top'],
29708         'top' : [ 'b-t', [0,-2], 'bottom']
29709     }
29710     
29711 });
29712
29713
29714 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29715     
29716     
29717     bindEl : false,
29718     
29719     delay : null, // can be { show : 300 , hide: 500}
29720     
29721     timeout : null,
29722     
29723     hoverState : null, //???
29724     
29725     placement : 'bottom', 
29726     
29727     alignment : false,
29728     
29729     getAutoCreate : function(){
29730     
29731         var cfg = {
29732            cls : 'tooltip',   
29733            role : 'tooltip',
29734            cn : [
29735                 {
29736                     cls : 'tooltip-arrow arrow'
29737                 },
29738                 {
29739                     cls : 'tooltip-inner'
29740                 }
29741            ]
29742         };
29743         
29744         return cfg;
29745     },
29746     bind : function(el)
29747     {
29748         this.bindEl = el;
29749     },
29750     
29751     initEvents : function()
29752     {
29753         this.arrowEl = this.el.select('.arrow', true).first();
29754         this.innerEl = this.el.select('.tooltip-inner', true).first();
29755     },
29756     
29757     enter : function () {
29758        
29759         if (this.timeout != null) {
29760             clearTimeout(this.timeout);
29761         }
29762         
29763         this.hoverState = 'in';
29764          //Roo.log("enter - show");
29765         if (!this.delay || !this.delay.show) {
29766             this.show();
29767             return;
29768         }
29769         var _t = this;
29770         this.timeout = setTimeout(function () {
29771             if (_t.hoverState == 'in') {
29772                 _t.show();
29773             }
29774         }, this.delay.show);
29775     },
29776     leave : function()
29777     {
29778         clearTimeout(this.timeout);
29779     
29780         this.hoverState = 'out';
29781          if (!this.delay || !this.delay.hide) {
29782             this.hide();
29783             return;
29784         }
29785        
29786         var _t = this;
29787         this.timeout = setTimeout(function () {
29788             //Roo.log("leave - timeout");
29789             
29790             if (_t.hoverState == 'out') {
29791                 _t.hide();
29792                 Roo.bootstrap.Tooltip.currentEl = false;
29793             }
29794         }, delay);
29795     },
29796     
29797     show : function (msg)
29798     {
29799         if (!this.el) {
29800             this.render(document.body);
29801         }
29802         // set content.
29803         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29804         
29805         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29806         
29807         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29808         
29809         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29810                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29811         
29812         var placement = typeof this.placement == 'function' ?
29813             this.placement.call(this, this.el, on_el) :
29814             this.placement;
29815             
29816         var autoToken = /\s?auto?\s?/i;
29817         var autoPlace = autoToken.test(placement);
29818         if (autoPlace) {
29819             placement = placement.replace(autoToken, '') || 'top';
29820         }
29821         
29822         //this.el.detach()
29823         //this.el.setXY([0,0]);
29824         this.el.show();
29825         //this.el.dom.style.display='block';
29826         
29827         //this.el.appendTo(on_el);
29828         
29829         var p = this.getPosition();
29830         var box = this.el.getBox();
29831         
29832         if (autoPlace) {
29833             // fixme..
29834         }
29835         
29836         var align = this.alignment[placement];
29837         
29838         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29839         
29840         if(placement == 'top' || placement == 'bottom'){
29841             if(xy[0] < 0){
29842                 placement = 'right';
29843             }
29844             
29845             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29846                 placement = 'left';
29847             }
29848             
29849             var scroll = Roo.select('body', true).first().getScroll();
29850             
29851             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29852                 placement = 'top';
29853             }
29854             
29855             align = this.alignment[placement];
29856             
29857             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29858             
29859         }
29860         
29861         var elems = document.getElementsByTagName('div');
29862         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29863         for (var i = 0; i < elems.length; i++) {
29864           var zindex = Number.parseInt(
29865                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29866                 10
29867           );
29868           if (zindex > highest) {
29869             highest = zindex;
29870           }
29871         }
29872         
29873         
29874         
29875         this.el.dom.style.zIndex = highest;
29876         
29877         this.el.alignTo(this.bindEl, align[0],align[1]);
29878         //var arrow = this.el.select('.arrow',true).first();
29879         //arrow.set(align[2], 
29880         
29881         this.el.addClass(placement);
29882         this.el.addClass("bs-tooltip-"+ placement);
29883         
29884         this.el.addClass('in fade show');
29885         
29886         this.hoverState = null;
29887         
29888         if (this.el.hasClass('fade')) {
29889             // fade it?
29890         }
29891         
29892         
29893         
29894         
29895         
29896     },
29897     hide : function()
29898     {
29899          
29900         if (!this.el) {
29901             return;
29902         }
29903         //this.el.setXY([0,0]);
29904         this.el.removeClass(['show', 'in']);
29905         //this.el.hide();
29906         
29907     }
29908     
29909 });
29910  
29911
29912  /*
29913  * - LGPL
29914  *
29915  * Location Picker
29916  * 
29917  */
29918
29919 /**
29920  * @class Roo.bootstrap.LocationPicker
29921  * @extends Roo.bootstrap.Component
29922  * Bootstrap LocationPicker class
29923  * @cfg {Number} latitude Position when init default 0
29924  * @cfg {Number} longitude Position when init default 0
29925  * @cfg {Number} zoom default 15
29926  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29927  * @cfg {Boolean} mapTypeControl default false
29928  * @cfg {Boolean} disableDoubleClickZoom default false
29929  * @cfg {Boolean} scrollwheel default true
29930  * @cfg {Boolean} streetViewControl default false
29931  * @cfg {Number} radius default 0
29932  * @cfg {String} locationName
29933  * @cfg {Boolean} draggable default true
29934  * @cfg {Boolean} enableAutocomplete default false
29935  * @cfg {Boolean} enableReverseGeocode default true
29936  * @cfg {String} markerTitle
29937  * 
29938  * @constructor
29939  * Create a new LocationPicker
29940  * @param {Object} config The config object
29941  */
29942
29943
29944 Roo.bootstrap.LocationPicker = function(config){
29945     
29946     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29947     
29948     this.addEvents({
29949         /**
29950          * @event initial
29951          * Fires when the picker initialized.
29952          * @param {Roo.bootstrap.LocationPicker} this
29953          * @param {Google Location} location
29954          */
29955         initial : true,
29956         /**
29957          * @event positionchanged
29958          * Fires when the picker position changed.
29959          * @param {Roo.bootstrap.LocationPicker} this
29960          * @param {Google Location} location
29961          */
29962         positionchanged : true,
29963         /**
29964          * @event resize
29965          * Fires when the map resize.
29966          * @param {Roo.bootstrap.LocationPicker} this
29967          */
29968         resize : true,
29969         /**
29970          * @event show
29971          * Fires when the map show.
29972          * @param {Roo.bootstrap.LocationPicker} this
29973          */
29974         show : true,
29975         /**
29976          * @event hide
29977          * Fires when the map hide.
29978          * @param {Roo.bootstrap.LocationPicker} this
29979          */
29980         hide : true,
29981         /**
29982          * @event mapClick
29983          * Fires when click the map.
29984          * @param {Roo.bootstrap.LocationPicker} this
29985          * @param {Map event} e
29986          */
29987         mapClick : true,
29988         /**
29989          * @event mapRightClick
29990          * Fires when right click the map.
29991          * @param {Roo.bootstrap.LocationPicker} this
29992          * @param {Map event} e
29993          */
29994         mapRightClick : true,
29995         /**
29996          * @event markerClick
29997          * Fires when click the marker.
29998          * @param {Roo.bootstrap.LocationPicker} this
29999          * @param {Map event} e
30000          */
30001         markerClick : true,
30002         /**
30003          * @event markerRightClick
30004          * Fires when right click the marker.
30005          * @param {Roo.bootstrap.LocationPicker} this
30006          * @param {Map event} e
30007          */
30008         markerRightClick : true,
30009         /**
30010          * @event OverlayViewDraw
30011          * Fires when OverlayView Draw
30012          * @param {Roo.bootstrap.LocationPicker} this
30013          */
30014         OverlayViewDraw : true,
30015         /**
30016          * @event OverlayViewOnAdd
30017          * Fires when OverlayView Draw
30018          * @param {Roo.bootstrap.LocationPicker} this
30019          */
30020         OverlayViewOnAdd : true,
30021         /**
30022          * @event OverlayViewOnRemove
30023          * Fires when OverlayView Draw
30024          * @param {Roo.bootstrap.LocationPicker} this
30025          */
30026         OverlayViewOnRemove : true,
30027         /**
30028          * @event OverlayViewShow
30029          * Fires when OverlayView Draw
30030          * @param {Roo.bootstrap.LocationPicker} this
30031          * @param {Pixel} cpx
30032          */
30033         OverlayViewShow : true,
30034         /**
30035          * @event OverlayViewHide
30036          * Fires when OverlayView Draw
30037          * @param {Roo.bootstrap.LocationPicker} this
30038          */
30039         OverlayViewHide : true,
30040         /**
30041          * @event loadexception
30042          * Fires when load google lib failed.
30043          * @param {Roo.bootstrap.LocationPicker} this
30044          */
30045         loadexception : true
30046     });
30047         
30048 };
30049
30050 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30051     
30052     gMapContext: false,
30053     
30054     latitude: 0,
30055     longitude: 0,
30056     zoom: 15,
30057     mapTypeId: false,
30058     mapTypeControl: false,
30059     disableDoubleClickZoom: false,
30060     scrollwheel: true,
30061     streetViewControl: false,
30062     radius: 0,
30063     locationName: '',
30064     draggable: true,
30065     enableAutocomplete: false,
30066     enableReverseGeocode: true,
30067     markerTitle: '',
30068     
30069     getAutoCreate: function()
30070     {
30071
30072         var cfg = {
30073             tag: 'div',
30074             cls: 'roo-location-picker'
30075         };
30076         
30077         return cfg
30078     },
30079     
30080     initEvents: function(ct, position)
30081     {       
30082         if(!this.el.getWidth() || this.isApplied()){
30083             return;
30084         }
30085         
30086         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30087         
30088         this.initial();
30089     },
30090     
30091     initial: function()
30092     {
30093         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30094             this.fireEvent('loadexception', this);
30095             return;
30096         }
30097         
30098         if(!this.mapTypeId){
30099             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30100         }
30101         
30102         this.gMapContext = this.GMapContext();
30103         
30104         this.initOverlayView();
30105         
30106         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30107         
30108         var _this = this;
30109                 
30110         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30111             _this.setPosition(_this.gMapContext.marker.position);
30112         });
30113         
30114         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30115             _this.fireEvent('mapClick', this, event);
30116             
30117         });
30118
30119         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30120             _this.fireEvent('mapRightClick', this, event);
30121             
30122         });
30123         
30124         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30125             _this.fireEvent('markerClick', this, event);
30126             
30127         });
30128
30129         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30130             _this.fireEvent('markerRightClick', this, event);
30131             
30132         });
30133         
30134         this.setPosition(this.gMapContext.location);
30135         
30136         this.fireEvent('initial', this, this.gMapContext.location);
30137     },
30138     
30139     initOverlayView: function()
30140     {
30141         var _this = this;
30142         
30143         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30144             
30145             draw: function()
30146             {
30147                 _this.fireEvent('OverlayViewDraw', _this);
30148             },
30149             
30150             onAdd: function()
30151             {
30152                 _this.fireEvent('OverlayViewOnAdd', _this);
30153             },
30154             
30155             onRemove: function()
30156             {
30157                 _this.fireEvent('OverlayViewOnRemove', _this);
30158             },
30159             
30160             show: function(cpx)
30161             {
30162                 _this.fireEvent('OverlayViewShow', _this, cpx);
30163             },
30164             
30165             hide: function()
30166             {
30167                 _this.fireEvent('OverlayViewHide', _this);
30168             }
30169             
30170         });
30171     },
30172     
30173     fromLatLngToContainerPixel: function(event)
30174     {
30175         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30176     },
30177     
30178     isApplied: function() 
30179     {
30180         return this.getGmapContext() == false ? false : true;
30181     },
30182     
30183     getGmapContext: function() 
30184     {
30185         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30186     },
30187     
30188     GMapContext: function() 
30189     {
30190         var position = new google.maps.LatLng(this.latitude, this.longitude);
30191         
30192         var _map = new google.maps.Map(this.el.dom, {
30193             center: position,
30194             zoom: this.zoom,
30195             mapTypeId: this.mapTypeId,
30196             mapTypeControl: this.mapTypeControl,
30197             disableDoubleClickZoom: this.disableDoubleClickZoom,
30198             scrollwheel: this.scrollwheel,
30199             streetViewControl: this.streetViewControl,
30200             locationName: this.locationName,
30201             draggable: this.draggable,
30202             enableAutocomplete: this.enableAutocomplete,
30203             enableReverseGeocode: this.enableReverseGeocode
30204         });
30205         
30206         var _marker = new google.maps.Marker({
30207             position: position,
30208             map: _map,
30209             title: this.markerTitle,
30210             draggable: this.draggable
30211         });
30212         
30213         return {
30214             map: _map,
30215             marker: _marker,
30216             circle: null,
30217             location: position,
30218             radius: this.radius,
30219             locationName: this.locationName,
30220             addressComponents: {
30221                 formatted_address: null,
30222                 addressLine1: null,
30223                 addressLine2: null,
30224                 streetName: null,
30225                 streetNumber: null,
30226                 city: null,
30227                 district: null,
30228                 state: null,
30229                 stateOrProvince: null
30230             },
30231             settings: this,
30232             domContainer: this.el.dom,
30233             geodecoder: new google.maps.Geocoder()
30234         };
30235     },
30236     
30237     drawCircle: function(center, radius, options) 
30238     {
30239         if (this.gMapContext.circle != null) {
30240             this.gMapContext.circle.setMap(null);
30241         }
30242         if (radius > 0) {
30243             radius *= 1;
30244             options = Roo.apply({}, options, {
30245                 strokeColor: "#0000FF",
30246                 strokeOpacity: .35,
30247                 strokeWeight: 2,
30248                 fillColor: "#0000FF",
30249                 fillOpacity: .2
30250             });
30251             
30252             options.map = this.gMapContext.map;
30253             options.radius = radius;
30254             options.center = center;
30255             this.gMapContext.circle = new google.maps.Circle(options);
30256             return this.gMapContext.circle;
30257         }
30258         
30259         return null;
30260     },
30261     
30262     setPosition: function(location) 
30263     {
30264         this.gMapContext.location = location;
30265         this.gMapContext.marker.setPosition(location);
30266         this.gMapContext.map.panTo(location);
30267         this.drawCircle(location, this.gMapContext.radius, {});
30268         
30269         var _this = this;
30270         
30271         if (this.gMapContext.settings.enableReverseGeocode) {
30272             this.gMapContext.geodecoder.geocode({
30273                 latLng: this.gMapContext.location
30274             }, function(results, status) {
30275                 
30276                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30277                     _this.gMapContext.locationName = results[0].formatted_address;
30278                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30279                     
30280                     _this.fireEvent('positionchanged', this, location);
30281                 }
30282             });
30283             
30284             return;
30285         }
30286         
30287         this.fireEvent('positionchanged', this, location);
30288     },
30289     
30290     resize: function()
30291     {
30292         google.maps.event.trigger(this.gMapContext.map, "resize");
30293         
30294         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30295         
30296         this.fireEvent('resize', this);
30297     },
30298     
30299     setPositionByLatLng: function(latitude, longitude)
30300     {
30301         this.setPosition(new google.maps.LatLng(latitude, longitude));
30302     },
30303     
30304     getCurrentPosition: function() 
30305     {
30306         return {
30307             latitude: this.gMapContext.location.lat(),
30308             longitude: this.gMapContext.location.lng()
30309         };
30310     },
30311     
30312     getAddressName: function() 
30313     {
30314         return this.gMapContext.locationName;
30315     },
30316     
30317     getAddressComponents: function() 
30318     {
30319         return this.gMapContext.addressComponents;
30320     },
30321     
30322     address_component_from_google_geocode: function(address_components) 
30323     {
30324         var result = {};
30325         
30326         for (var i = 0; i < address_components.length; i++) {
30327             var component = address_components[i];
30328             if (component.types.indexOf("postal_code") >= 0) {
30329                 result.postalCode = component.short_name;
30330             } else if (component.types.indexOf("street_number") >= 0) {
30331                 result.streetNumber = component.short_name;
30332             } else if (component.types.indexOf("route") >= 0) {
30333                 result.streetName = component.short_name;
30334             } else if (component.types.indexOf("neighborhood") >= 0) {
30335                 result.city = component.short_name;
30336             } else if (component.types.indexOf("locality") >= 0) {
30337                 result.city = component.short_name;
30338             } else if (component.types.indexOf("sublocality") >= 0) {
30339                 result.district = component.short_name;
30340             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30341                 result.stateOrProvince = component.short_name;
30342             } else if (component.types.indexOf("country") >= 0) {
30343                 result.country = component.short_name;
30344             }
30345         }
30346         
30347         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30348         result.addressLine2 = "";
30349         return result;
30350     },
30351     
30352     setZoomLevel: function(zoom)
30353     {
30354         this.gMapContext.map.setZoom(zoom);
30355     },
30356     
30357     show: function()
30358     {
30359         if(!this.el){
30360             return;
30361         }
30362         
30363         this.el.show();
30364         
30365         this.resize();
30366         
30367         this.fireEvent('show', this);
30368     },
30369     
30370     hide: function()
30371     {
30372         if(!this.el){
30373             return;
30374         }
30375         
30376         this.el.hide();
30377         
30378         this.fireEvent('hide', this);
30379     }
30380     
30381 });
30382
30383 Roo.apply(Roo.bootstrap.LocationPicker, {
30384     
30385     OverlayView : function(map, options)
30386     {
30387         options = options || {};
30388         
30389         this.setMap(map);
30390     }
30391     
30392     
30393 });/**
30394  * @class Roo.bootstrap.Alert
30395  * @extends Roo.bootstrap.Component
30396  * Bootstrap Alert class - shows an alert area box
30397  * eg
30398  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30399   Enter a valid email address
30400 </div>
30401  * @licence LGPL
30402  * @cfg {String} title The title of alert
30403  * @cfg {String} html The content of alert
30404  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30405  * @cfg {String} fa font-awesomeicon
30406  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30407  * @cfg {Boolean} close true to show a x closer
30408  * 
30409  * 
30410  * @constructor
30411  * Create a new alert
30412  * @param {Object} config The config object
30413  */
30414
30415
30416 Roo.bootstrap.Alert = function(config){
30417     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30418     
30419 };
30420
30421 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30422     
30423     title: '',
30424     html: '',
30425     weight: false,
30426     fa: false,
30427     faicon: false, // BC
30428     close : false,
30429     
30430     
30431     getAutoCreate : function()
30432     {
30433         
30434         var cfg = {
30435             tag : 'div',
30436             cls : 'alert',
30437             cn : [
30438                 {
30439                     tag: 'button',
30440                     type :  "button",
30441                     cls: "close",
30442                     html : '×',
30443                     style : this.close ? '' : 'display:none'
30444                 },
30445                 {
30446                     tag : 'i',
30447                     cls : 'roo-alert-icon'
30448                     
30449                 },
30450                 {
30451                     tag : 'b',
30452                     cls : 'roo-alert-title',
30453                     html : this.title
30454                 },
30455                 {
30456                     tag : 'span',
30457                     cls : 'roo-alert-text',
30458                     html : this.html
30459                 }
30460             ]
30461         };
30462         
30463         if(this.faicon){
30464             cfg.cn[0].cls += ' fa ' + this.faicon;
30465         }
30466         if(this.fa){
30467             cfg.cn[0].cls += ' fa ' + this.fa;
30468         }
30469         
30470         if(this.weight){
30471             cfg.cls += ' alert-' + this.weight;
30472         }
30473         
30474         return cfg;
30475     },
30476     
30477     initEvents: function() 
30478     {
30479         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30480         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30481         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30482         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30483         if (this.seconds > 0) {
30484             this.hide.defer(this.seconds, this);
30485         }
30486     },
30487     /**
30488      * Set the Title Message HTML
30489      * @param {String} html
30490      */
30491     setTitle : function(str)
30492     {
30493         this.titleEl.dom.innerHTML = str;
30494     },
30495      
30496      /**
30497      * Set the Body Message HTML
30498      * @param {String} html
30499      */
30500     setHtml : function(str)
30501     {
30502         this.htmlEl.dom.innerHTML = str;
30503     },
30504     /**
30505      * Set the Weight of the alert
30506      * @param {String} (success|info|warning|danger) weight
30507      */
30508     
30509     setWeight : function(weight)
30510     {
30511         if(this.weight){
30512             this.el.removeClass('alert-' + this.weight);
30513         }
30514         
30515         this.weight = weight;
30516         
30517         this.el.addClass('alert-' + this.weight);
30518     },
30519       /**
30520      * Set the Icon of the alert
30521      * @param {String} see fontawsome names (name without the 'fa-' bit)
30522      */
30523     setIcon : function(icon)
30524     {
30525         if(this.faicon){
30526             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30527         }
30528         
30529         this.faicon = icon;
30530         
30531         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30532     },
30533     /**
30534      * Hide the Alert
30535      */
30536     hide: function() 
30537     {
30538         this.el.hide();   
30539     },
30540     /**
30541      * Show the Alert
30542      */
30543     show: function() 
30544     {  
30545         this.el.show();   
30546     }
30547     
30548 });
30549
30550  
30551 /*
30552 * Licence: LGPL
30553 */
30554
30555 /**
30556  * @class Roo.bootstrap.UploadCropbox
30557  * @extends Roo.bootstrap.Component
30558  * Bootstrap UploadCropbox class
30559  * @cfg {String} emptyText show when image has been loaded
30560  * @cfg {String} rotateNotify show when image too small to rotate
30561  * @cfg {Number} errorTimeout default 3000
30562  * @cfg {Number} minWidth default 300
30563  * @cfg {Number} minHeight default 300
30564  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30565  * @cfg {Boolean} isDocument (true|false) default false
30566  * @cfg {String} url action url
30567  * @cfg {String} paramName default 'imageUpload'
30568  * @cfg {String} method default POST
30569  * @cfg {Boolean} loadMask (true|false) default true
30570  * @cfg {Boolean} loadingText default 'Loading...'
30571  * 
30572  * @constructor
30573  * Create a new UploadCropbox
30574  * @param {Object} config The config object
30575  */
30576
30577 Roo.bootstrap.UploadCropbox = function(config){
30578     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30579     
30580     this.addEvents({
30581         /**
30582          * @event beforeselectfile
30583          * Fire before select file
30584          * @param {Roo.bootstrap.UploadCropbox} this
30585          */
30586         "beforeselectfile" : true,
30587         /**
30588          * @event initial
30589          * Fire after initEvent
30590          * @param {Roo.bootstrap.UploadCropbox} this
30591          */
30592         "initial" : true,
30593         /**
30594          * @event crop
30595          * Fire after initEvent
30596          * @param {Roo.bootstrap.UploadCropbox} this
30597          * @param {String} data
30598          */
30599         "crop" : true,
30600         /**
30601          * @event prepare
30602          * Fire when preparing the file data
30603          * @param {Roo.bootstrap.UploadCropbox} this
30604          * @param {Object} file
30605          */
30606         "prepare" : true,
30607         /**
30608          * @event exception
30609          * Fire when get exception
30610          * @param {Roo.bootstrap.UploadCropbox} this
30611          * @param {XMLHttpRequest} xhr
30612          */
30613         "exception" : true,
30614         /**
30615          * @event beforeloadcanvas
30616          * Fire before load the canvas
30617          * @param {Roo.bootstrap.UploadCropbox} this
30618          * @param {String} src
30619          */
30620         "beforeloadcanvas" : true,
30621         /**
30622          * @event trash
30623          * Fire when trash image
30624          * @param {Roo.bootstrap.UploadCropbox} this
30625          */
30626         "trash" : true,
30627         /**
30628          * @event download
30629          * Fire when download the image
30630          * @param {Roo.bootstrap.UploadCropbox} this
30631          */
30632         "download" : true,
30633         /**
30634          * @event footerbuttonclick
30635          * Fire when footerbuttonclick
30636          * @param {Roo.bootstrap.UploadCropbox} this
30637          * @param {String} type
30638          */
30639         "footerbuttonclick" : true,
30640         /**
30641          * @event resize
30642          * Fire when resize
30643          * @param {Roo.bootstrap.UploadCropbox} this
30644          */
30645         "resize" : true,
30646         /**
30647          * @event rotate
30648          * Fire when rotate the image
30649          * @param {Roo.bootstrap.UploadCropbox} this
30650          * @param {String} pos
30651          */
30652         "rotate" : true,
30653         /**
30654          * @event inspect
30655          * Fire when inspect the file
30656          * @param {Roo.bootstrap.UploadCropbox} this
30657          * @param {Object} file
30658          */
30659         "inspect" : true,
30660         /**
30661          * @event upload
30662          * Fire when xhr upload the file
30663          * @param {Roo.bootstrap.UploadCropbox} this
30664          * @param {Object} data
30665          */
30666         "upload" : true,
30667         /**
30668          * @event arrange
30669          * Fire when arrange the file data
30670          * @param {Roo.bootstrap.UploadCropbox} this
30671          * @param {Object} formData
30672          */
30673         "arrange" : true
30674     });
30675     
30676     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30677 };
30678
30679 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30680     
30681     emptyText : 'Click to upload image',
30682     rotateNotify : 'Image is too small to rotate',
30683     errorTimeout : 3000,
30684     scale : 0,
30685     baseScale : 1,
30686     rotate : 0,
30687     dragable : false,
30688     pinching : false,
30689     mouseX : 0,
30690     mouseY : 0,
30691     cropData : false,
30692     minWidth : 300,
30693     minHeight : 300,
30694     file : false,
30695     exif : {},
30696     baseRotate : 1,
30697     cropType : 'image/jpeg',
30698     buttons : false,
30699     canvasLoaded : false,
30700     isDocument : false,
30701     method : 'POST',
30702     paramName : 'imageUpload',
30703     loadMask : true,
30704     loadingText : 'Loading...',
30705     maskEl : false,
30706     
30707     getAutoCreate : function()
30708     {
30709         var cfg = {
30710             tag : 'div',
30711             cls : 'roo-upload-cropbox',
30712             cn : [
30713                 {
30714                     tag : 'input',
30715                     cls : 'roo-upload-cropbox-selector',
30716                     type : 'file'
30717                 },
30718                 {
30719                     tag : 'div',
30720                     cls : 'roo-upload-cropbox-body',
30721                     style : 'cursor:pointer',
30722                     cn : [
30723                         {
30724                             tag : 'div',
30725                             cls : 'roo-upload-cropbox-preview'
30726                         },
30727                         {
30728                             tag : 'div',
30729                             cls : 'roo-upload-cropbox-thumb'
30730                         },
30731                         {
30732                             tag : 'div',
30733                             cls : 'roo-upload-cropbox-empty-notify',
30734                             html : this.emptyText
30735                         },
30736                         {
30737                             tag : 'div',
30738                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30739                             html : this.rotateNotify
30740                         }
30741                     ]
30742                 },
30743                 {
30744                     tag : 'div',
30745                     cls : 'roo-upload-cropbox-footer',
30746                     cn : {
30747                         tag : 'div',
30748                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30749                         cn : []
30750                     }
30751                 }
30752             ]
30753         };
30754         
30755         return cfg;
30756     },
30757     
30758     onRender : function(ct, position)
30759     {
30760         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30761         
30762         if (this.buttons.length) {
30763             
30764             Roo.each(this.buttons, function(bb) {
30765                 
30766                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30767                 
30768                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30769                 
30770             }, this);
30771         }
30772         
30773         if(this.loadMask){
30774             this.maskEl = this.el;
30775         }
30776     },
30777     
30778     initEvents : function()
30779     {
30780         this.urlAPI = (window.createObjectURL && window) || 
30781                                 (window.URL && URL.revokeObjectURL && URL) || 
30782                                 (window.webkitURL && webkitURL);
30783                         
30784         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30785         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30786         
30787         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30788         this.selectorEl.hide();
30789         
30790         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30791         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30792         
30793         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30794         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30795         this.thumbEl.hide();
30796         
30797         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30798         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30799         
30800         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30801         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30802         this.errorEl.hide();
30803         
30804         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30805         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30806         this.footerEl.hide();
30807         
30808         this.setThumbBoxSize();
30809         
30810         this.bind();
30811         
30812         this.resize();
30813         
30814         this.fireEvent('initial', this);
30815     },
30816
30817     bind : function()
30818     {
30819         var _this = this;
30820         
30821         window.addEventListener("resize", function() { _this.resize(); } );
30822         
30823         this.bodyEl.on('click', this.beforeSelectFile, this);
30824         
30825         if(Roo.isTouch){
30826             this.bodyEl.on('touchstart', this.onTouchStart, this);
30827             this.bodyEl.on('touchmove', this.onTouchMove, this);
30828             this.bodyEl.on('touchend', this.onTouchEnd, this);
30829         }
30830         
30831         if(!Roo.isTouch){
30832             this.bodyEl.on('mousedown', this.onMouseDown, this);
30833             this.bodyEl.on('mousemove', this.onMouseMove, this);
30834             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30835             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30836             Roo.get(document).on('mouseup', this.onMouseUp, this);
30837         }
30838         
30839         this.selectorEl.on('change', this.onFileSelected, this);
30840     },
30841     
30842     reset : function()
30843     {    
30844         this.scale = 0;
30845         this.baseScale = 1;
30846         this.rotate = 0;
30847         this.baseRotate = 1;
30848         this.dragable = false;
30849         this.pinching = false;
30850         this.mouseX = 0;
30851         this.mouseY = 0;
30852         this.cropData = false;
30853         this.notifyEl.dom.innerHTML = this.emptyText;
30854         
30855         this.selectorEl.dom.value = '';
30856         
30857     },
30858     
30859     resize : function()
30860     {
30861         if(this.fireEvent('resize', this) != false){
30862             this.setThumbBoxPosition();
30863             this.setCanvasPosition();
30864         }
30865     },
30866     
30867     onFooterButtonClick : function(e, el, o, type)
30868     {
30869         switch (type) {
30870             case 'rotate-left' :
30871                 this.onRotateLeft(e);
30872                 break;
30873             case 'rotate-right' :
30874                 this.onRotateRight(e);
30875                 break;
30876             case 'picture' :
30877                 this.beforeSelectFile(e);
30878                 break;
30879             case 'trash' :
30880                 this.trash(e);
30881                 break;
30882             case 'crop' :
30883                 this.crop(e);
30884                 break;
30885             case 'download' :
30886                 this.download(e);
30887                 break;
30888             default :
30889                 break;
30890         }
30891         
30892         this.fireEvent('footerbuttonclick', this, type);
30893     },
30894     
30895     beforeSelectFile : function(e)
30896     {
30897         e.preventDefault();
30898         
30899         if(this.fireEvent('beforeselectfile', this) != false){
30900             this.selectorEl.dom.click();
30901         }
30902     },
30903     
30904     onFileSelected : function(e)
30905     {
30906         e.preventDefault();
30907         
30908         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30909             return;
30910         }
30911         
30912         var file = this.selectorEl.dom.files[0];
30913         
30914         if(this.fireEvent('inspect', this, file) != false){
30915             this.prepare(file);
30916         }
30917         
30918     },
30919     
30920     trash : function(e)
30921     {
30922         this.fireEvent('trash', this);
30923     },
30924     
30925     download : function(e)
30926     {
30927         this.fireEvent('download', this);
30928     },
30929     
30930     loadCanvas : function(src)
30931     {   
30932         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30933             
30934             this.reset();
30935             
30936             this.imageEl = document.createElement('img');
30937             
30938             var _this = this;
30939             
30940             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30941             
30942             this.imageEl.src = src;
30943         }
30944     },
30945     
30946     onLoadCanvas : function()
30947     {   
30948         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30949         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30950         
30951         this.bodyEl.un('click', this.beforeSelectFile, this);
30952         
30953         this.notifyEl.hide();
30954         this.thumbEl.show();
30955         this.footerEl.show();
30956         
30957         this.baseRotateLevel();
30958         
30959         if(this.isDocument){
30960             this.setThumbBoxSize();
30961         }
30962         
30963         this.setThumbBoxPosition();
30964         
30965         this.baseScaleLevel();
30966         
30967         this.draw();
30968         
30969         this.resize();
30970         
30971         this.canvasLoaded = true;
30972         
30973         if(this.loadMask){
30974             this.maskEl.unmask();
30975         }
30976         
30977     },
30978     
30979     setCanvasPosition : function()
30980     {   
30981         if(!this.canvasEl){
30982             return;
30983         }
30984         
30985         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30986         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30987         
30988         this.previewEl.setLeft(pw);
30989         this.previewEl.setTop(ph);
30990         
30991     },
30992     
30993     onMouseDown : function(e)
30994     {   
30995         e.stopEvent();
30996         
30997         this.dragable = true;
30998         this.pinching = false;
30999         
31000         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31001             this.dragable = false;
31002             return;
31003         }
31004         
31005         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31006         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31007         
31008     },
31009     
31010     onMouseMove : function(e)
31011     {   
31012         e.stopEvent();
31013         
31014         if(!this.canvasLoaded){
31015             return;
31016         }
31017         
31018         if (!this.dragable){
31019             return;
31020         }
31021         
31022         var minX = Math.ceil(this.thumbEl.getLeft(true));
31023         var minY = Math.ceil(this.thumbEl.getTop(true));
31024         
31025         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31026         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31027         
31028         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31029         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31030         
31031         x = x - this.mouseX;
31032         y = y - this.mouseY;
31033         
31034         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31035         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31036         
31037         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31038         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31039         
31040         this.previewEl.setLeft(bgX);
31041         this.previewEl.setTop(bgY);
31042         
31043         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31044         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31045     },
31046     
31047     onMouseUp : function(e)
31048     {   
31049         e.stopEvent();
31050         
31051         this.dragable = false;
31052     },
31053     
31054     onMouseWheel : function(e)
31055     {   
31056         e.stopEvent();
31057         
31058         this.startScale = this.scale;
31059         
31060         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31061         
31062         if(!this.zoomable()){
31063             this.scale = this.startScale;
31064             return;
31065         }
31066         
31067         this.draw();
31068         
31069         return;
31070     },
31071     
31072     zoomable : function()
31073     {
31074         var minScale = this.thumbEl.getWidth() / this.minWidth;
31075         
31076         if(this.minWidth < this.minHeight){
31077             minScale = this.thumbEl.getHeight() / this.minHeight;
31078         }
31079         
31080         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31081         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31082         
31083         if(
31084                 this.isDocument &&
31085                 (this.rotate == 0 || this.rotate == 180) && 
31086                 (
31087                     width > this.imageEl.OriginWidth || 
31088                     height > this.imageEl.OriginHeight ||
31089                     (width < this.minWidth && height < this.minHeight)
31090                 )
31091         ){
31092             return false;
31093         }
31094         
31095         if(
31096                 this.isDocument &&
31097                 (this.rotate == 90 || this.rotate == 270) && 
31098                 (
31099                     width > this.imageEl.OriginWidth || 
31100                     height > this.imageEl.OriginHeight ||
31101                     (width < this.minHeight && height < this.minWidth)
31102                 )
31103         ){
31104             return false;
31105         }
31106         
31107         if(
31108                 !this.isDocument &&
31109                 (this.rotate == 0 || this.rotate == 180) && 
31110                 (
31111                     width < this.minWidth || 
31112                     width > this.imageEl.OriginWidth || 
31113                     height < this.minHeight || 
31114                     height > this.imageEl.OriginHeight
31115                 )
31116         ){
31117             return false;
31118         }
31119         
31120         if(
31121                 !this.isDocument &&
31122                 (this.rotate == 90 || this.rotate == 270) && 
31123                 (
31124                     width < this.minHeight || 
31125                     width > this.imageEl.OriginWidth || 
31126                     height < this.minWidth || 
31127                     height > this.imageEl.OriginHeight
31128                 )
31129         ){
31130             return false;
31131         }
31132         
31133         return true;
31134         
31135     },
31136     
31137     onRotateLeft : function(e)
31138     {   
31139         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31140             
31141             var minScale = this.thumbEl.getWidth() / this.minWidth;
31142             
31143             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31144             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31145             
31146             this.startScale = this.scale;
31147             
31148             while (this.getScaleLevel() < minScale){
31149             
31150                 this.scale = this.scale + 1;
31151                 
31152                 if(!this.zoomable()){
31153                     break;
31154                 }
31155                 
31156                 if(
31157                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31158                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31159                 ){
31160                     continue;
31161                 }
31162                 
31163                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31164
31165                 this.draw();
31166                 
31167                 return;
31168             }
31169             
31170             this.scale = this.startScale;
31171             
31172             this.onRotateFail();
31173             
31174             return false;
31175         }
31176         
31177         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31178
31179         if(this.isDocument){
31180             this.setThumbBoxSize();
31181             this.setThumbBoxPosition();
31182             this.setCanvasPosition();
31183         }
31184         
31185         this.draw();
31186         
31187         this.fireEvent('rotate', this, 'left');
31188         
31189     },
31190     
31191     onRotateRight : function(e)
31192     {
31193         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31194             
31195             var minScale = this.thumbEl.getWidth() / this.minWidth;
31196         
31197             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31198             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31199             
31200             this.startScale = this.scale;
31201             
31202             while (this.getScaleLevel() < minScale){
31203             
31204                 this.scale = this.scale + 1;
31205                 
31206                 if(!this.zoomable()){
31207                     break;
31208                 }
31209                 
31210                 if(
31211                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31212                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31213                 ){
31214                     continue;
31215                 }
31216                 
31217                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31218
31219                 this.draw();
31220                 
31221                 return;
31222             }
31223             
31224             this.scale = this.startScale;
31225             
31226             this.onRotateFail();
31227             
31228             return false;
31229         }
31230         
31231         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31232
31233         if(this.isDocument){
31234             this.setThumbBoxSize();
31235             this.setThumbBoxPosition();
31236             this.setCanvasPosition();
31237         }
31238         
31239         this.draw();
31240         
31241         this.fireEvent('rotate', this, 'right');
31242     },
31243     
31244     onRotateFail : function()
31245     {
31246         this.errorEl.show(true);
31247         
31248         var _this = this;
31249         
31250         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31251     },
31252     
31253     draw : function()
31254     {
31255         this.previewEl.dom.innerHTML = '';
31256         
31257         var canvasEl = document.createElement("canvas");
31258         
31259         var contextEl = canvasEl.getContext("2d");
31260         
31261         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31262         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31263         var center = this.imageEl.OriginWidth / 2;
31264         
31265         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31266             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31267             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31268             center = this.imageEl.OriginHeight / 2;
31269         }
31270         
31271         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31272         
31273         contextEl.translate(center, center);
31274         contextEl.rotate(this.rotate * Math.PI / 180);
31275
31276         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31277         
31278         this.canvasEl = document.createElement("canvas");
31279         
31280         this.contextEl = this.canvasEl.getContext("2d");
31281         
31282         switch (this.rotate) {
31283             case 0 :
31284                 
31285                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31286                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31287                 
31288                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31289                 
31290                 break;
31291             case 90 : 
31292                 
31293                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31294                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31295                 
31296                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31297                     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);
31298                     break;
31299                 }
31300                 
31301                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31302                 
31303                 break;
31304             case 180 :
31305                 
31306                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31307                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31308                 
31309                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31310                     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);
31311                     break;
31312                 }
31313                 
31314                 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);
31315                 
31316                 break;
31317             case 270 :
31318                 
31319                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31320                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31321         
31322                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31323                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31324                     break;
31325                 }
31326                 
31327                 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);
31328                 
31329                 break;
31330             default : 
31331                 break;
31332         }
31333         
31334         this.previewEl.appendChild(this.canvasEl);
31335         
31336         this.setCanvasPosition();
31337     },
31338     
31339     crop : function()
31340     {
31341         if(!this.canvasLoaded){
31342             return;
31343         }
31344         
31345         var imageCanvas = document.createElement("canvas");
31346         
31347         var imageContext = imageCanvas.getContext("2d");
31348         
31349         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31350         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31351         
31352         var center = imageCanvas.width / 2;
31353         
31354         imageContext.translate(center, center);
31355         
31356         imageContext.rotate(this.rotate * Math.PI / 180);
31357         
31358         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31359         
31360         var canvas = document.createElement("canvas");
31361         
31362         var context = canvas.getContext("2d");
31363                 
31364         canvas.width = this.minWidth;
31365         canvas.height = this.minHeight;
31366
31367         switch (this.rotate) {
31368             case 0 :
31369                 
31370                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31371                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31372                 
31373                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31374                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31375                 
31376                 var targetWidth = this.minWidth - 2 * x;
31377                 var targetHeight = this.minHeight - 2 * y;
31378                 
31379                 var scale = 1;
31380                 
31381                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31382                     scale = targetWidth / width;
31383                 }
31384                 
31385                 if(x > 0 && y == 0){
31386                     scale = targetHeight / height;
31387                 }
31388                 
31389                 if(x > 0 && y > 0){
31390                     scale = targetWidth / width;
31391                     
31392                     if(width < height){
31393                         scale = targetHeight / height;
31394                     }
31395                 }
31396                 
31397                 context.scale(scale, scale);
31398                 
31399                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31400                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31401
31402                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31403                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31404
31405                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31406                 
31407                 break;
31408             case 90 : 
31409                 
31410                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31411                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31412                 
31413                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31414                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31415                 
31416                 var targetWidth = this.minWidth - 2 * x;
31417                 var targetHeight = this.minHeight - 2 * y;
31418                 
31419                 var scale = 1;
31420                 
31421                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31422                     scale = targetWidth / width;
31423                 }
31424                 
31425                 if(x > 0 && y == 0){
31426                     scale = targetHeight / height;
31427                 }
31428                 
31429                 if(x > 0 && y > 0){
31430                     scale = targetWidth / width;
31431                     
31432                     if(width < height){
31433                         scale = targetHeight / height;
31434                     }
31435                 }
31436                 
31437                 context.scale(scale, scale);
31438                 
31439                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31440                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31441
31442                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31443                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31444                 
31445                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31446                 
31447                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31448                 
31449                 break;
31450             case 180 :
31451                 
31452                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31453                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31454                 
31455                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31456                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31457                 
31458                 var targetWidth = this.minWidth - 2 * x;
31459                 var targetHeight = this.minHeight - 2 * y;
31460                 
31461                 var scale = 1;
31462                 
31463                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31464                     scale = targetWidth / width;
31465                 }
31466                 
31467                 if(x > 0 && y == 0){
31468                     scale = targetHeight / height;
31469                 }
31470                 
31471                 if(x > 0 && y > 0){
31472                     scale = targetWidth / width;
31473                     
31474                     if(width < height){
31475                         scale = targetHeight / height;
31476                     }
31477                 }
31478                 
31479                 context.scale(scale, scale);
31480                 
31481                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31482                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31483
31484                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31485                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31486
31487                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31488                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31489                 
31490                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31491                 
31492                 break;
31493             case 270 :
31494                 
31495                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31496                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31497                 
31498                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31499                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31500                 
31501                 var targetWidth = this.minWidth - 2 * x;
31502                 var targetHeight = this.minHeight - 2 * y;
31503                 
31504                 var scale = 1;
31505                 
31506                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31507                     scale = targetWidth / width;
31508                 }
31509                 
31510                 if(x > 0 && y == 0){
31511                     scale = targetHeight / height;
31512                 }
31513                 
31514                 if(x > 0 && y > 0){
31515                     scale = targetWidth / width;
31516                     
31517                     if(width < height){
31518                         scale = targetHeight / height;
31519                     }
31520                 }
31521                 
31522                 context.scale(scale, scale);
31523                 
31524                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31525                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31526
31527                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31528                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31529                 
31530                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31531                 
31532                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31533                 
31534                 break;
31535             default : 
31536                 break;
31537         }
31538         
31539         this.cropData = canvas.toDataURL(this.cropType);
31540         
31541         if(this.fireEvent('crop', this, this.cropData) !== false){
31542             this.process(this.file, this.cropData);
31543         }
31544         
31545         return;
31546         
31547     },
31548     
31549     setThumbBoxSize : function()
31550     {
31551         var width, height;
31552         
31553         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31554             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31555             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31556             
31557             this.minWidth = width;
31558             this.minHeight = height;
31559             
31560             if(this.rotate == 90 || this.rotate == 270){
31561                 this.minWidth = height;
31562                 this.minHeight = width;
31563             }
31564         }
31565         
31566         height = 300;
31567         width = Math.ceil(this.minWidth * height / this.minHeight);
31568         
31569         if(this.minWidth > this.minHeight){
31570             width = 300;
31571             height = Math.ceil(this.minHeight * width / this.minWidth);
31572         }
31573         
31574         this.thumbEl.setStyle({
31575             width : width + 'px',
31576             height : height + 'px'
31577         });
31578
31579         return;
31580             
31581     },
31582     
31583     setThumbBoxPosition : function()
31584     {
31585         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31586         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31587         
31588         this.thumbEl.setLeft(x);
31589         this.thumbEl.setTop(y);
31590         
31591     },
31592     
31593     baseRotateLevel : function()
31594     {
31595         this.baseRotate = 1;
31596         
31597         if(
31598                 typeof(this.exif) != 'undefined' &&
31599                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31600                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31601         ){
31602             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31603         }
31604         
31605         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31606         
31607     },
31608     
31609     baseScaleLevel : function()
31610     {
31611         var width, height;
31612         
31613         if(this.isDocument){
31614             
31615             if(this.baseRotate == 6 || this.baseRotate == 8){
31616             
31617                 height = this.thumbEl.getHeight();
31618                 this.baseScale = height / this.imageEl.OriginWidth;
31619
31620                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31621                     width = this.thumbEl.getWidth();
31622                     this.baseScale = width / this.imageEl.OriginHeight;
31623                 }
31624
31625                 return;
31626             }
31627
31628             height = this.thumbEl.getHeight();
31629             this.baseScale = height / this.imageEl.OriginHeight;
31630
31631             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31632                 width = this.thumbEl.getWidth();
31633                 this.baseScale = width / this.imageEl.OriginWidth;
31634             }
31635
31636             return;
31637         }
31638         
31639         if(this.baseRotate == 6 || this.baseRotate == 8){
31640             
31641             width = this.thumbEl.getHeight();
31642             this.baseScale = width / this.imageEl.OriginHeight;
31643             
31644             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31645                 height = this.thumbEl.getWidth();
31646                 this.baseScale = height / this.imageEl.OriginHeight;
31647             }
31648             
31649             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31650                 height = this.thumbEl.getWidth();
31651                 this.baseScale = height / this.imageEl.OriginHeight;
31652                 
31653                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31654                     width = this.thumbEl.getHeight();
31655                     this.baseScale = width / this.imageEl.OriginWidth;
31656                 }
31657             }
31658             
31659             return;
31660         }
31661         
31662         width = this.thumbEl.getWidth();
31663         this.baseScale = width / this.imageEl.OriginWidth;
31664         
31665         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31666             height = this.thumbEl.getHeight();
31667             this.baseScale = height / this.imageEl.OriginHeight;
31668         }
31669         
31670         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31671             
31672             height = this.thumbEl.getHeight();
31673             this.baseScale = height / this.imageEl.OriginHeight;
31674             
31675             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31676                 width = this.thumbEl.getWidth();
31677                 this.baseScale = width / this.imageEl.OriginWidth;
31678             }
31679             
31680         }
31681         
31682         return;
31683     },
31684     
31685     getScaleLevel : function()
31686     {
31687         return this.baseScale * Math.pow(1.1, this.scale);
31688     },
31689     
31690     onTouchStart : function(e)
31691     {
31692         if(!this.canvasLoaded){
31693             this.beforeSelectFile(e);
31694             return;
31695         }
31696         
31697         var touches = e.browserEvent.touches;
31698         
31699         if(!touches){
31700             return;
31701         }
31702         
31703         if(touches.length == 1){
31704             this.onMouseDown(e);
31705             return;
31706         }
31707         
31708         if(touches.length != 2){
31709             return;
31710         }
31711         
31712         var coords = [];
31713         
31714         for(var i = 0, finger; finger = touches[i]; i++){
31715             coords.push(finger.pageX, finger.pageY);
31716         }
31717         
31718         var x = Math.pow(coords[0] - coords[2], 2);
31719         var y = Math.pow(coords[1] - coords[3], 2);
31720         
31721         this.startDistance = Math.sqrt(x + y);
31722         
31723         this.startScale = this.scale;
31724         
31725         this.pinching = true;
31726         this.dragable = false;
31727         
31728     },
31729     
31730     onTouchMove : function(e)
31731     {
31732         if(!this.pinching && !this.dragable){
31733             return;
31734         }
31735         
31736         var touches = e.browserEvent.touches;
31737         
31738         if(!touches){
31739             return;
31740         }
31741         
31742         if(this.dragable){
31743             this.onMouseMove(e);
31744             return;
31745         }
31746         
31747         var coords = [];
31748         
31749         for(var i = 0, finger; finger = touches[i]; i++){
31750             coords.push(finger.pageX, finger.pageY);
31751         }
31752         
31753         var x = Math.pow(coords[0] - coords[2], 2);
31754         var y = Math.pow(coords[1] - coords[3], 2);
31755         
31756         this.endDistance = Math.sqrt(x + y);
31757         
31758         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31759         
31760         if(!this.zoomable()){
31761             this.scale = this.startScale;
31762             return;
31763         }
31764         
31765         this.draw();
31766         
31767     },
31768     
31769     onTouchEnd : function(e)
31770     {
31771         this.pinching = false;
31772         this.dragable = false;
31773         
31774     },
31775     
31776     process : function(file, crop)
31777     {
31778         if(this.loadMask){
31779             this.maskEl.mask(this.loadingText);
31780         }
31781         
31782         this.xhr = new XMLHttpRequest();
31783         
31784         file.xhr = this.xhr;
31785
31786         this.xhr.open(this.method, this.url, true);
31787         
31788         var headers = {
31789             "Accept": "application/json",
31790             "Cache-Control": "no-cache",
31791             "X-Requested-With": "XMLHttpRequest"
31792         };
31793         
31794         for (var headerName in headers) {
31795             var headerValue = headers[headerName];
31796             if (headerValue) {
31797                 this.xhr.setRequestHeader(headerName, headerValue);
31798             }
31799         }
31800         
31801         var _this = this;
31802         
31803         this.xhr.onload = function()
31804         {
31805             _this.xhrOnLoad(_this.xhr);
31806         }
31807         
31808         this.xhr.onerror = function()
31809         {
31810             _this.xhrOnError(_this.xhr);
31811         }
31812         
31813         var formData = new FormData();
31814
31815         formData.append('returnHTML', 'NO');
31816         
31817         if(crop){
31818             formData.append('crop', crop);
31819         }
31820         
31821         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31822             formData.append(this.paramName, file, file.name);
31823         }
31824         
31825         if(typeof(file.filename) != 'undefined'){
31826             formData.append('filename', file.filename);
31827         }
31828         
31829         if(typeof(file.mimetype) != 'undefined'){
31830             formData.append('mimetype', file.mimetype);
31831         }
31832         
31833         if(this.fireEvent('arrange', this, formData) != false){
31834             this.xhr.send(formData);
31835         };
31836     },
31837     
31838     xhrOnLoad : function(xhr)
31839     {
31840         if(this.loadMask){
31841             this.maskEl.unmask();
31842         }
31843         
31844         if (xhr.readyState !== 4) {
31845             this.fireEvent('exception', this, xhr);
31846             return;
31847         }
31848
31849         var response = Roo.decode(xhr.responseText);
31850         
31851         if(!response.success){
31852             this.fireEvent('exception', this, xhr);
31853             return;
31854         }
31855         
31856         var response = Roo.decode(xhr.responseText);
31857         
31858         this.fireEvent('upload', this, response);
31859         
31860     },
31861     
31862     xhrOnError : function()
31863     {
31864         if(this.loadMask){
31865             this.maskEl.unmask();
31866         }
31867         
31868         Roo.log('xhr on error');
31869         
31870         var response = Roo.decode(xhr.responseText);
31871           
31872         Roo.log(response);
31873         
31874     },
31875     
31876     prepare : function(file)
31877     {   
31878         if(this.loadMask){
31879             this.maskEl.mask(this.loadingText);
31880         }
31881         
31882         this.file = false;
31883         this.exif = {};
31884         
31885         if(typeof(file) === 'string'){
31886             this.loadCanvas(file);
31887             return;
31888         }
31889         
31890         if(!file || !this.urlAPI){
31891             return;
31892         }
31893         
31894         this.file = file;
31895         this.cropType = file.type;
31896         
31897         var _this = this;
31898         
31899         if(this.fireEvent('prepare', this, this.file) != false){
31900             
31901             var reader = new FileReader();
31902             
31903             reader.onload = function (e) {
31904                 if (e.target.error) {
31905                     Roo.log(e.target.error);
31906                     return;
31907                 }
31908                 
31909                 var buffer = e.target.result,
31910                     dataView = new DataView(buffer),
31911                     offset = 2,
31912                     maxOffset = dataView.byteLength - 4,
31913                     markerBytes,
31914                     markerLength;
31915                 
31916                 if (dataView.getUint16(0) === 0xffd8) {
31917                     while (offset < maxOffset) {
31918                         markerBytes = dataView.getUint16(offset);
31919                         
31920                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31921                             markerLength = dataView.getUint16(offset + 2) + 2;
31922                             if (offset + markerLength > dataView.byteLength) {
31923                                 Roo.log('Invalid meta data: Invalid segment size.');
31924                                 break;
31925                             }
31926                             
31927                             if(markerBytes == 0xffe1){
31928                                 _this.parseExifData(
31929                                     dataView,
31930                                     offset,
31931                                     markerLength
31932                                 );
31933                             }
31934                             
31935                             offset += markerLength;
31936                             
31937                             continue;
31938                         }
31939                         
31940                         break;
31941                     }
31942                     
31943                 }
31944                 
31945                 var url = _this.urlAPI.createObjectURL(_this.file);
31946                 
31947                 _this.loadCanvas(url);
31948                 
31949                 return;
31950             }
31951             
31952             reader.readAsArrayBuffer(this.file);
31953             
31954         }
31955         
31956     },
31957     
31958     parseExifData : function(dataView, offset, length)
31959     {
31960         var tiffOffset = offset + 10,
31961             littleEndian,
31962             dirOffset;
31963     
31964         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31965             // No Exif data, might be XMP data instead
31966             return;
31967         }
31968         
31969         // Check for the ASCII code for "Exif" (0x45786966):
31970         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31971             // No Exif data, might be XMP data instead
31972             return;
31973         }
31974         if (tiffOffset + 8 > dataView.byteLength) {
31975             Roo.log('Invalid Exif data: Invalid segment size.');
31976             return;
31977         }
31978         // Check for the two null bytes:
31979         if (dataView.getUint16(offset + 8) !== 0x0000) {
31980             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31981             return;
31982         }
31983         // Check the byte alignment:
31984         switch (dataView.getUint16(tiffOffset)) {
31985         case 0x4949:
31986             littleEndian = true;
31987             break;
31988         case 0x4D4D:
31989             littleEndian = false;
31990             break;
31991         default:
31992             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31993             return;
31994         }
31995         // Check for the TIFF tag marker (0x002A):
31996         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31997             Roo.log('Invalid Exif data: Missing TIFF marker.');
31998             return;
31999         }
32000         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32001         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32002         
32003         this.parseExifTags(
32004             dataView,
32005             tiffOffset,
32006             tiffOffset + dirOffset,
32007             littleEndian
32008         );
32009     },
32010     
32011     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32012     {
32013         var tagsNumber,
32014             dirEndOffset,
32015             i;
32016         if (dirOffset + 6 > dataView.byteLength) {
32017             Roo.log('Invalid Exif data: Invalid directory offset.');
32018             return;
32019         }
32020         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32021         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32022         if (dirEndOffset + 4 > dataView.byteLength) {
32023             Roo.log('Invalid Exif data: Invalid directory size.');
32024             return;
32025         }
32026         for (i = 0; i < tagsNumber; i += 1) {
32027             this.parseExifTag(
32028                 dataView,
32029                 tiffOffset,
32030                 dirOffset + 2 + 12 * i, // tag offset
32031                 littleEndian
32032             );
32033         }
32034         // Return the offset to the next directory:
32035         return dataView.getUint32(dirEndOffset, littleEndian);
32036     },
32037     
32038     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32039     {
32040         var tag = dataView.getUint16(offset, littleEndian);
32041         
32042         this.exif[tag] = this.getExifValue(
32043             dataView,
32044             tiffOffset,
32045             offset,
32046             dataView.getUint16(offset + 2, littleEndian), // tag type
32047             dataView.getUint32(offset + 4, littleEndian), // tag length
32048             littleEndian
32049         );
32050     },
32051     
32052     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32053     {
32054         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32055             tagSize,
32056             dataOffset,
32057             values,
32058             i,
32059             str,
32060             c;
32061     
32062         if (!tagType) {
32063             Roo.log('Invalid Exif data: Invalid tag type.');
32064             return;
32065         }
32066         
32067         tagSize = tagType.size * length;
32068         // Determine if the value is contained in the dataOffset bytes,
32069         // or if the value at the dataOffset is a pointer to the actual data:
32070         dataOffset = tagSize > 4 ?
32071                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32072         if (dataOffset + tagSize > dataView.byteLength) {
32073             Roo.log('Invalid Exif data: Invalid data offset.');
32074             return;
32075         }
32076         if (length === 1) {
32077             return tagType.getValue(dataView, dataOffset, littleEndian);
32078         }
32079         values = [];
32080         for (i = 0; i < length; i += 1) {
32081             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32082         }
32083         
32084         if (tagType.ascii) {
32085             str = '';
32086             // Concatenate the chars:
32087             for (i = 0; i < values.length; i += 1) {
32088                 c = values[i];
32089                 // Ignore the terminating NULL byte(s):
32090                 if (c === '\u0000') {
32091                     break;
32092                 }
32093                 str += c;
32094             }
32095             return str;
32096         }
32097         return values;
32098     }
32099     
32100 });
32101
32102 Roo.apply(Roo.bootstrap.UploadCropbox, {
32103     tags : {
32104         'Orientation': 0x0112
32105     },
32106     
32107     Orientation: {
32108             1: 0, //'top-left',
32109 //            2: 'top-right',
32110             3: 180, //'bottom-right',
32111 //            4: 'bottom-left',
32112 //            5: 'left-top',
32113             6: 90, //'right-top',
32114 //            7: 'right-bottom',
32115             8: 270 //'left-bottom'
32116     },
32117     
32118     exifTagTypes : {
32119         // byte, 8-bit unsigned int:
32120         1: {
32121             getValue: function (dataView, dataOffset) {
32122                 return dataView.getUint8(dataOffset);
32123             },
32124             size: 1
32125         },
32126         // ascii, 8-bit byte:
32127         2: {
32128             getValue: function (dataView, dataOffset) {
32129                 return String.fromCharCode(dataView.getUint8(dataOffset));
32130             },
32131             size: 1,
32132             ascii: true
32133         },
32134         // short, 16 bit int:
32135         3: {
32136             getValue: function (dataView, dataOffset, littleEndian) {
32137                 return dataView.getUint16(dataOffset, littleEndian);
32138             },
32139             size: 2
32140         },
32141         // long, 32 bit int:
32142         4: {
32143             getValue: function (dataView, dataOffset, littleEndian) {
32144                 return dataView.getUint32(dataOffset, littleEndian);
32145             },
32146             size: 4
32147         },
32148         // rational = two long values, first is numerator, second is denominator:
32149         5: {
32150             getValue: function (dataView, dataOffset, littleEndian) {
32151                 return dataView.getUint32(dataOffset, littleEndian) /
32152                     dataView.getUint32(dataOffset + 4, littleEndian);
32153             },
32154             size: 8
32155         },
32156         // slong, 32 bit signed int:
32157         9: {
32158             getValue: function (dataView, dataOffset, littleEndian) {
32159                 return dataView.getInt32(dataOffset, littleEndian);
32160             },
32161             size: 4
32162         },
32163         // srational, two slongs, first is numerator, second is denominator:
32164         10: {
32165             getValue: function (dataView, dataOffset, littleEndian) {
32166                 return dataView.getInt32(dataOffset, littleEndian) /
32167                     dataView.getInt32(dataOffset + 4, littleEndian);
32168             },
32169             size: 8
32170         }
32171     },
32172     
32173     footer : {
32174         STANDARD : [
32175             {
32176                 tag : 'div',
32177                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32178                 action : 'rotate-left',
32179                 cn : [
32180                     {
32181                         tag : 'button',
32182                         cls : 'btn btn-default',
32183                         html : '<i class="fa fa-undo"></i>'
32184                     }
32185                 ]
32186             },
32187             {
32188                 tag : 'div',
32189                 cls : 'btn-group roo-upload-cropbox-picture',
32190                 action : 'picture',
32191                 cn : [
32192                     {
32193                         tag : 'button',
32194                         cls : 'btn btn-default',
32195                         html : '<i class="fa fa-picture-o"></i>'
32196                     }
32197                 ]
32198             },
32199             {
32200                 tag : 'div',
32201                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32202                 action : 'rotate-right',
32203                 cn : [
32204                     {
32205                         tag : 'button',
32206                         cls : 'btn btn-default',
32207                         html : '<i class="fa fa-repeat"></i>'
32208                     }
32209                 ]
32210             }
32211         ],
32212         DOCUMENT : [
32213             {
32214                 tag : 'div',
32215                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32216                 action : 'rotate-left',
32217                 cn : [
32218                     {
32219                         tag : 'button',
32220                         cls : 'btn btn-default',
32221                         html : '<i class="fa fa-undo"></i>'
32222                     }
32223                 ]
32224             },
32225             {
32226                 tag : 'div',
32227                 cls : 'btn-group roo-upload-cropbox-download',
32228                 action : 'download',
32229                 cn : [
32230                     {
32231                         tag : 'button',
32232                         cls : 'btn btn-default',
32233                         html : '<i class="fa fa-download"></i>'
32234                     }
32235                 ]
32236             },
32237             {
32238                 tag : 'div',
32239                 cls : 'btn-group roo-upload-cropbox-crop',
32240                 action : 'crop',
32241                 cn : [
32242                     {
32243                         tag : 'button',
32244                         cls : 'btn btn-default',
32245                         html : '<i class="fa fa-crop"></i>'
32246                     }
32247                 ]
32248             },
32249             {
32250                 tag : 'div',
32251                 cls : 'btn-group roo-upload-cropbox-trash',
32252                 action : 'trash',
32253                 cn : [
32254                     {
32255                         tag : 'button',
32256                         cls : 'btn btn-default',
32257                         html : '<i class="fa fa-trash"></i>'
32258                     }
32259                 ]
32260             },
32261             {
32262                 tag : 'div',
32263                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32264                 action : 'rotate-right',
32265                 cn : [
32266                     {
32267                         tag : 'button',
32268                         cls : 'btn btn-default',
32269                         html : '<i class="fa fa-repeat"></i>'
32270                     }
32271                 ]
32272             }
32273         ],
32274         ROTATOR : [
32275             {
32276                 tag : 'div',
32277                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32278                 action : 'rotate-left',
32279                 cn : [
32280                     {
32281                         tag : 'button',
32282                         cls : 'btn btn-default',
32283                         html : '<i class="fa fa-undo"></i>'
32284                     }
32285                 ]
32286             },
32287             {
32288                 tag : 'div',
32289                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32290                 action : 'rotate-right',
32291                 cn : [
32292                     {
32293                         tag : 'button',
32294                         cls : 'btn btn-default',
32295                         html : '<i class="fa fa-repeat"></i>'
32296                     }
32297                 ]
32298             }
32299         ]
32300     }
32301 });
32302
32303 /*
32304 * Licence: LGPL
32305 */
32306
32307 /**
32308  * @class Roo.bootstrap.DocumentManager
32309  * @extends Roo.bootstrap.Component
32310  * Bootstrap DocumentManager class
32311  * @cfg {String} paramName default 'imageUpload'
32312  * @cfg {String} toolTipName default 'filename'
32313  * @cfg {String} method default POST
32314  * @cfg {String} url action url
32315  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32316  * @cfg {Boolean} multiple multiple upload default true
32317  * @cfg {Number} thumbSize default 300
32318  * @cfg {String} fieldLabel
32319  * @cfg {Number} labelWidth default 4
32320  * @cfg {String} labelAlign (left|top) default left
32321  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32322 * @cfg {Number} labellg set the width of label (1-12)
32323  * @cfg {Number} labelmd set the width of label (1-12)
32324  * @cfg {Number} labelsm set the width of label (1-12)
32325  * @cfg {Number} labelxs set the width of label (1-12)
32326  * 
32327  * @constructor
32328  * Create a new DocumentManager
32329  * @param {Object} config The config object
32330  */
32331
32332 Roo.bootstrap.DocumentManager = function(config){
32333     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32334     
32335     this.files = [];
32336     this.delegates = [];
32337     
32338     this.addEvents({
32339         /**
32340          * @event initial
32341          * Fire when initial the DocumentManager
32342          * @param {Roo.bootstrap.DocumentManager} this
32343          */
32344         "initial" : true,
32345         /**
32346          * @event inspect
32347          * inspect selected file
32348          * @param {Roo.bootstrap.DocumentManager} this
32349          * @param {File} file
32350          */
32351         "inspect" : true,
32352         /**
32353          * @event exception
32354          * Fire when xhr load exception
32355          * @param {Roo.bootstrap.DocumentManager} this
32356          * @param {XMLHttpRequest} xhr
32357          */
32358         "exception" : true,
32359         /**
32360          * @event afterupload
32361          * Fire when xhr load exception
32362          * @param {Roo.bootstrap.DocumentManager} this
32363          * @param {XMLHttpRequest} xhr
32364          */
32365         "afterupload" : true,
32366         /**
32367          * @event prepare
32368          * prepare the form data
32369          * @param {Roo.bootstrap.DocumentManager} this
32370          * @param {Object} formData
32371          */
32372         "prepare" : true,
32373         /**
32374          * @event remove
32375          * Fire when remove the file
32376          * @param {Roo.bootstrap.DocumentManager} this
32377          * @param {Object} file
32378          */
32379         "remove" : true,
32380         /**
32381          * @event refresh
32382          * Fire after refresh the file
32383          * @param {Roo.bootstrap.DocumentManager} this
32384          */
32385         "refresh" : true,
32386         /**
32387          * @event click
32388          * Fire after click the image
32389          * @param {Roo.bootstrap.DocumentManager} this
32390          * @param {Object} file
32391          */
32392         "click" : true,
32393         /**
32394          * @event edit
32395          * Fire when upload a image and editable set to true
32396          * @param {Roo.bootstrap.DocumentManager} this
32397          * @param {Object} file
32398          */
32399         "edit" : true,
32400         /**
32401          * @event beforeselectfile
32402          * Fire before select file
32403          * @param {Roo.bootstrap.DocumentManager} this
32404          */
32405         "beforeselectfile" : true,
32406         /**
32407          * @event process
32408          * Fire before process file
32409          * @param {Roo.bootstrap.DocumentManager} this
32410          * @param {Object} file
32411          */
32412         "process" : true,
32413         /**
32414          * @event previewrendered
32415          * Fire when preview rendered
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          * @param {Object} file
32418          */
32419         "previewrendered" : true,
32420         /**
32421          */
32422         "previewResize" : true
32423         
32424     });
32425 };
32426
32427 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32428     
32429     boxes : 0,
32430     inputName : '',
32431     thumbSize : 300,
32432     multiple : true,
32433     files : false,
32434     method : 'POST',
32435     url : '',
32436     paramName : 'imageUpload',
32437     toolTipName : 'filename',
32438     fieldLabel : '',
32439     labelWidth : 4,
32440     labelAlign : 'left',
32441     editable : true,
32442     delegates : false,
32443     xhr : false, 
32444     
32445     labellg : 0,
32446     labelmd : 0,
32447     labelsm : 0,
32448     labelxs : 0,
32449     
32450     getAutoCreate : function()
32451     {   
32452         var managerWidget = {
32453             tag : 'div',
32454             cls : 'roo-document-manager',
32455             cn : [
32456                 {
32457                     tag : 'input',
32458                     cls : 'roo-document-manager-selector',
32459                     type : 'file'
32460                 },
32461                 {
32462                     tag : 'div',
32463                     cls : 'roo-document-manager-uploader',
32464                     cn : [
32465                         {
32466                             tag : 'div',
32467                             cls : 'roo-document-manager-upload-btn',
32468                             html : '<i class="fa fa-plus"></i>'
32469                         }
32470                     ]
32471                     
32472                 }
32473             ]
32474         };
32475         
32476         var content = [
32477             {
32478                 tag : 'div',
32479                 cls : 'column col-md-12',
32480                 cn : managerWidget
32481             }
32482         ];
32483         
32484         if(this.fieldLabel.length){
32485             
32486             content = [
32487                 {
32488                     tag : 'div',
32489                     cls : 'column col-md-12',
32490                     html : this.fieldLabel
32491                 },
32492                 {
32493                     tag : 'div',
32494                     cls : 'column col-md-12',
32495                     cn : managerWidget
32496                 }
32497             ];
32498
32499             if(this.labelAlign == 'left'){
32500                 content = [
32501                     {
32502                         tag : 'div',
32503                         cls : 'column',
32504                         html : this.fieldLabel
32505                     },
32506                     {
32507                         tag : 'div',
32508                         cls : 'column',
32509                         cn : managerWidget
32510                     }
32511                 ];
32512                 
32513                 if(this.labelWidth > 12){
32514                     content[0].style = "width: " + this.labelWidth + 'px';
32515                 }
32516
32517                 if(this.labelWidth < 13 && this.labelmd == 0){
32518                     this.labelmd = this.labelWidth;
32519                 }
32520
32521                 if(this.labellg > 0){
32522                     content[0].cls += ' col-lg-' + this.labellg;
32523                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32524                 }
32525
32526                 if(this.labelmd > 0){
32527                     content[0].cls += ' col-md-' + this.labelmd;
32528                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32529                 }
32530
32531                 if(this.labelsm > 0){
32532                     content[0].cls += ' col-sm-' + this.labelsm;
32533                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32534                 }
32535
32536                 if(this.labelxs > 0){
32537                     content[0].cls += ' col-xs-' + this.labelxs;
32538                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32539                 }
32540                 
32541             }
32542         }
32543         
32544         var cfg = {
32545             tag : 'div',
32546             cls : 'row clearfix',
32547             cn : content
32548         };
32549         
32550         return cfg;
32551         
32552     },
32553     
32554     initEvents : function()
32555     {
32556         this.managerEl = this.el.select('.roo-document-manager', true).first();
32557         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32558         
32559         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32560         this.selectorEl.hide();
32561         
32562         if(this.multiple){
32563             this.selectorEl.attr('multiple', 'multiple');
32564         }
32565         
32566         this.selectorEl.on('change', this.onFileSelected, this);
32567         
32568         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32569         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32570         
32571         this.uploader.on('click', this.onUploaderClick, this);
32572         
32573         this.renderProgressDialog();
32574         
32575         var _this = this;
32576         
32577         window.addEventListener("resize", function() { _this.refresh(); } );
32578         
32579         this.fireEvent('initial', this);
32580     },
32581     
32582     renderProgressDialog : function()
32583     {
32584         var _this = this;
32585         
32586         this.progressDialog = new Roo.bootstrap.Modal({
32587             cls : 'roo-document-manager-progress-dialog',
32588             allow_close : false,
32589             animate : false,
32590             title : '',
32591             buttons : [
32592                 {
32593                     name  :'cancel',
32594                     weight : 'danger',
32595                     html : 'Cancel'
32596                 }
32597             ], 
32598             listeners : { 
32599                 btnclick : function() {
32600                     _this.uploadCancel();
32601                     this.hide();
32602                 }
32603             }
32604         });
32605          
32606         this.progressDialog.render(Roo.get(document.body));
32607          
32608         this.progress = new Roo.bootstrap.Progress({
32609             cls : 'roo-document-manager-progress',
32610             active : true,
32611             striped : true
32612         });
32613         
32614         this.progress.render(this.progressDialog.getChildContainer());
32615         
32616         this.progressBar = new Roo.bootstrap.ProgressBar({
32617             cls : 'roo-document-manager-progress-bar',
32618             aria_valuenow : 0,
32619             aria_valuemin : 0,
32620             aria_valuemax : 12,
32621             panel : 'success'
32622         });
32623         
32624         this.progressBar.render(this.progress.getChildContainer());
32625     },
32626     
32627     onUploaderClick : function(e)
32628     {
32629         e.preventDefault();
32630      
32631         if(this.fireEvent('beforeselectfile', this) != false){
32632             this.selectorEl.dom.click();
32633         }
32634         
32635     },
32636     
32637     onFileSelected : function(e)
32638     {
32639         e.preventDefault();
32640         
32641         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32642             return;
32643         }
32644         
32645         Roo.each(this.selectorEl.dom.files, function(file){
32646             if(this.fireEvent('inspect', this, file) != false){
32647                 this.files.push(file);
32648             }
32649         }, this);
32650         
32651         this.queue();
32652         
32653     },
32654     
32655     queue : function()
32656     {
32657         this.selectorEl.dom.value = '';
32658         
32659         if(!this.files || !this.files.length){
32660             return;
32661         }
32662         
32663         if(this.boxes > 0 && this.files.length > this.boxes){
32664             this.files = this.files.slice(0, this.boxes);
32665         }
32666         
32667         this.uploader.show();
32668         
32669         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32670             this.uploader.hide();
32671         }
32672         
32673         var _this = this;
32674         
32675         var files = [];
32676         
32677         var docs = [];
32678         
32679         Roo.each(this.files, function(file){
32680             
32681             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32682                 var f = this.renderPreview(file);
32683                 files.push(f);
32684                 return;
32685             }
32686             
32687             if(file.type.indexOf('image') != -1){
32688                 this.delegates.push(
32689                     (function(){
32690                         _this.process(file);
32691                     }).createDelegate(this)
32692                 );
32693         
32694                 return;
32695             }
32696             
32697             docs.push(
32698                 (function(){
32699                     _this.process(file);
32700                 }).createDelegate(this)
32701             );
32702             
32703         }, this);
32704         
32705         this.files = files;
32706         
32707         this.delegates = this.delegates.concat(docs);
32708         
32709         if(!this.delegates.length){
32710             this.refresh();
32711             return;
32712         }
32713         
32714         this.progressBar.aria_valuemax = this.delegates.length;
32715         
32716         this.arrange();
32717         
32718         return;
32719     },
32720     
32721     arrange : function()
32722     {
32723         if(!this.delegates.length){
32724             this.progressDialog.hide();
32725             this.refresh();
32726             return;
32727         }
32728         
32729         var delegate = this.delegates.shift();
32730         
32731         this.progressDialog.show();
32732         
32733         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32734         
32735         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32736         
32737         delegate();
32738     },
32739     
32740     refresh : function()
32741     {
32742         this.uploader.show();
32743         
32744         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32745             this.uploader.hide();
32746         }
32747         
32748         Roo.isTouch ? this.closable(false) : this.closable(true);
32749         
32750         this.fireEvent('refresh', this);
32751     },
32752     
32753     onRemove : function(e, el, o)
32754     {
32755         e.preventDefault();
32756         
32757         this.fireEvent('remove', this, o);
32758         
32759     },
32760     
32761     remove : function(o)
32762     {
32763         var files = [];
32764         
32765         Roo.each(this.files, function(file){
32766             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32767                 files.push(file);
32768                 return;
32769             }
32770
32771             o.target.remove();
32772
32773         }, this);
32774         
32775         this.files = files;
32776         
32777         this.refresh();
32778     },
32779     
32780     clear : function()
32781     {
32782         Roo.each(this.files, function(file){
32783             if(!file.target){
32784                 return;
32785             }
32786             
32787             file.target.remove();
32788
32789         }, this);
32790         
32791         this.files = [];
32792         
32793         this.refresh();
32794     },
32795     
32796     onClick : function(e, el, o)
32797     {
32798         e.preventDefault();
32799         
32800         this.fireEvent('click', this, o);
32801         
32802     },
32803     
32804     closable : function(closable)
32805     {
32806         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32807             
32808             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32809             
32810             if(closable){
32811                 el.show();
32812                 return;
32813             }
32814             
32815             el.hide();
32816             
32817         }, this);
32818     },
32819     
32820     xhrOnLoad : function(xhr)
32821     {
32822         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32823             el.remove();
32824         }, this);
32825         
32826         if (xhr.readyState !== 4) {
32827             this.arrange();
32828             this.fireEvent('exception', this, xhr);
32829             return;
32830         }
32831
32832         var response = Roo.decode(xhr.responseText);
32833         
32834         if(!response.success){
32835             this.arrange();
32836             this.fireEvent('exception', this, xhr);
32837             return;
32838         }
32839         
32840         var file = this.renderPreview(response.data);
32841         
32842         this.files.push(file);
32843         
32844         this.arrange();
32845         
32846         this.fireEvent('afterupload', this, xhr);
32847         
32848     },
32849     
32850     xhrOnError : function(xhr)
32851     {
32852         Roo.log('xhr on error');
32853         
32854         var response = Roo.decode(xhr.responseText);
32855           
32856         Roo.log(response);
32857         
32858         this.arrange();
32859     },
32860     
32861     process : function(file)
32862     {
32863         if(this.fireEvent('process', this, file) !== false){
32864             if(this.editable && file.type.indexOf('image') != -1){
32865                 this.fireEvent('edit', this, file);
32866                 return;
32867             }
32868
32869             this.uploadStart(file, false);
32870
32871             return;
32872         }
32873         
32874     },
32875     
32876     uploadStart : function(file, crop)
32877     {
32878         this.xhr = new XMLHttpRequest();
32879         
32880         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32881             this.arrange();
32882             return;
32883         }
32884         
32885         file.xhr = this.xhr;
32886             
32887         this.managerEl.createChild({
32888             tag : 'div',
32889             cls : 'roo-document-manager-loading',
32890             cn : [
32891                 {
32892                     tag : 'div',
32893                     tooltip : file.name,
32894                     cls : 'roo-document-manager-thumb',
32895                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32896                 }
32897             ]
32898
32899         });
32900
32901         this.xhr.open(this.method, this.url, true);
32902         
32903         var headers = {
32904             "Accept": "application/json",
32905             "Cache-Control": "no-cache",
32906             "X-Requested-With": "XMLHttpRequest"
32907         };
32908         
32909         for (var headerName in headers) {
32910             var headerValue = headers[headerName];
32911             if (headerValue) {
32912                 this.xhr.setRequestHeader(headerName, headerValue);
32913             }
32914         }
32915         
32916         var _this = this;
32917         
32918         this.xhr.onload = function()
32919         {
32920             _this.xhrOnLoad(_this.xhr);
32921         }
32922         
32923         this.xhr.onerror = function()
32924         {
32925             _this.xhrOnError(_this.xhr);
32926         }
32927         
32928         var formData = new FormData();
32929
32930         formData.append('returnHTML', 'NO');
32931         
32932         if(crop){
32933             formData.append('crop', crop);
32934         }
32935         
32936         formData.append(this.paramName, file, file.name);
32937         
32938         var options = {
32939             file : file, 
32940             manually : false
32941         };
32942         
32943         if(this.fireEvent('prepare', this, formData, options) != false){
32944             
32945             if(options.manually){
32946                 return;
32947             }
32948             
32949             this.xhr.send(formData);
32950             return;
32951         };
32952         
32953         this.uploadCancel();
32954     },
32955     
32956     uploadCancel : function()
32957     {
32958         if (this.xhr) {
32959             this.xhr.abort();
32960         }
32961         
32962         this.delegates = [];
32963         
32964         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32965             el.remove();
32966         }, this);
32967         
32968         this.arrange();
32969     },
32970     
32971     renderPreview : function(file)
32972     {
32973         if(typeof(file.target) != 'undefined' && file.target){
32974             return file;
32975         }
32976         
32977         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32978         
32979         var previewEl = this.managerEl.createChild({
32980             tag : 'div',
32981             cls : 'roo-document-manager-preview',
32982             cn : [
32983                 {
32984                     tag : 'div',
32985                     tooltip : file[this.toolTipName],
32986                     cls : 'roo-document-manager-thumb',
32987                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32988                 },
32989                 {
32990                     tag : 'button',
32991                     cls : 'close',
32992                     html : '<i class="fa fa-times-circle"></i>'
32993                 }
32994             ]
32995         });
32996
32997         var close = previewEl.select('button.close', true).first();
32998
32999         close.on('click', this.onRemove, this, file);
33000
33001         file.target = previewEl;
33002
33003         var image = previewEl.select('img', true).first();
33004         
33005         var _this = this;
33006         
33007         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33008         
33009         image.on('click', this.onClick, this, file);
33010         
33011         this.fireEvent('previewrendered', this, file);
33012         
33013         return file;
33014         
33015     },
33016     
33017     onPreviewLoad : function(file, image)
33018     {
33019         if(typeof(file.target) == 'undefined' || !file.target){
33020             return;
33021         }
33022         
33023         var width = image.dom.naturalWidth || image.dom.width;
33024         var height = image.dom.naturalHeight || image.dom.height;
33025         
33026         if(!this.previewResize) {
33027             return;
33028         }
33029         
33030         if(width > height){
33031             file.target.addClass('wide');
33032             return;
33033         }
33034         
33035         file.target.addClass('tall');
33036         return;
33037         
33038     },
33039     
33040     uploadFromSource : function(file, crop)
33041     {
33042         this.xhr = new XMLHttpRequest();
33043         
33044         this.managerEl.createChild({
33045             tag : 'div',
33046             cls : 'roo-document-manager-loading',
33047             cn : [
33048                 {
33049                     tag : 'div',
33050                     tooltip : file.name,
33051                     cls : 'roo-document-manager-thumb',
33052                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33053                 }
33054             ]
33055
33056         });
33057
33058         this.xhr.open(this.method, this.url, true);
33059         
33060         var headers = {
33061             "Accept": "application/json",
33062             "Cache-Control": "no-cache",
33063             "X-Requested-With": "XMLHttpRequest"
33064         };
33065         
33066         for (var headerName in headers) {
33067             var headerValue = headers[headerName];
33068             if (headerValue) {
33069                 this.xhr.setRequestHeader(headerName, headerValue);
33070             }
33071         }
33072         
33073         var _this = this;
33074         
33075         this.xhr.onload = function()
33076         {
33077             _this.xhrOnLoad(_this.xhr);
33078         }
33079         
33080         this.xhr.onerror = function()
33081         {
33082             _this.xhrOnError(_this.xhr);
33083         }
33084         
33085         var formData = new FormData();
33086
33087         formData.append('returnHTML', 'NO');
33088         
33089         formData.append('crop', crop);
33090         
33091         if(typeof(file.filename) != 'undefined'){
33092             formData.append('filename', file.filename);
33093         }
33094         
33095         if(typeof(file.mimetype) != 'undefined'){
33096             formData.append('mimetype', file.mimetype);
33097         }
33098         
33099         Roo.log(formData);
33100         
33101         if(this.fireEvent('prepare', this, formData) != false){
33102             this.xhr.send(formData);
33103         };
33104     }
33105 });
33106
33107 /*
33108 * Licence: LGPL
33109 */
33110
33111 /**
33112  * @class Roo.bootstrap.DocumentViewer
33113  * @extends Roo.bootstrap.Component
33114  * Bootstrap DocumentViewer class
33115  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33116  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33117  * 
33118  * @constructor
33119  * Create a new DocumentViewer
33120  * @param {Object} config The config object
33121  */
33122
33123 Roo.bootstrap.DocumentViewer = function(config){
33124     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33125     
33126     this.addEvents({
33127         /**
33128          * @event initial
33129          * Fire after initEvent
33130          * @param {Roo.bootstrap.DocumentViewer} this
33131          */
33132         "initial" : true,
33133         /**
33134          * @event click
33135          * Fire after click
33136          * @param {Roo.bootstrap.DocumentViewer} this
33137          */
33138         "click" : true,
33139         /**
33140          * @event download
33141          * Fire after download button
33142          * @param {Roo.bootstrap.DocumentViewer} this
33143          */
33144         "download" : true,
33145         /**
33146          * @event trash
33147          * Fire after trash button
33148          * @param {Roo.bootstrap.DocumentViewer} this
33149          */
33150         "trash" : true
33151         
33152     });
33153 };
33154
33155 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33156     
33157     showDownload : true,
33158     
33159     showTrash : true,
33160     
33161     getAutoCreate : function()
33162     {
33163         var cfg = {
33164             tag : 'div',
33165             cls : 'roo-document-viewer',
33166             cn : [
33167                 {
33168                     tag : 'div',
33169                     cls : 'roo-document-viewer-body',
33170                     cn : [
33171                         {
33172                             tag : 'div',
33173                             cls : 'roo-document-viewer-thumb',
33174                             cn : [
33175                                 {
33176                                     tag : 'img',
33177                                     cls : 'roo-document-viewer-image'
33178                                 }
33179                             ]
33180                         }
33181                     ]
33182                 },
33183                 {
33184                     tag : 'div',
33185                     cls : 'roo-document-viewer-footer',
33186                     cn : {
33187                         tag : 'div',
33188                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33189                         cn : [
33190                             {
33191                                 tag : 'div',
33192                                 cls : 'btn-group roo-document-viewer-download',
33193                                 cn : [
33194                                     {
33195                                         tag : 'button',
33196                                         cls : 'btn btn-default',
33197                                         html : '<i class="fa fa-download"></i>'
33198                                     }
33199                                 ]
33200                             },
33201                             {
33202                                 tag : 'div',
33203                                 cls : 'btn-group roo-document-viewer-trash',
33204                                 cn : [
33205                                     {
33206                                         tag : 'button',
33207                                         cls : 'btn btn-default',
33208                                         html : '<i class="fa fa-trash"></i>'
33209                                     }
33210                                 ]
33211                             }
33212                         ]
33213                     }
33214                 }
33215             ]
33216         };
33217         
33218         return cfg;
33219     },
33220     
33221     initEvents : function()
33222     {
33223         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33224         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33225         
33226         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33227         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33228         
33229         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33230         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33231         
33232         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33233         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33234         
33235         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33236         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33237         
33238         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33239         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33240         
33241         this.bodyEl.on('click', this.onClick, this);
33242         this.downloadBtn.on('click', this.onDownload, this);
33243         this.trashBtn.on('click', this.onTrash, this);
33244         
33245         this.downloadBtn.hide();
33246         this.trashBtn.hide();
33247         
33248         if(this.showDownload){
33249             this.downloadBtn.show();
33250         }
33251         
33252         if(this.showTrash){
33253             this.trashBtn.show();
33254         }
33255         
33256         if(!this.showDownload && !this.showTrash) {
33257             this.footerEl.hide();
33258         }
33259         
33260     },
33261     
33262     initial : function()
33263     {
33264         this.fireEvent('initial', this);
33265         
33266     },
33267     
33268     onClick : function(e)
33269     {
33270         e.preventDefault();
33271         
33272         this.fireEvent('click', this);
33273     },
33274     
33275     onDownload : function(e)
33276     {
33277         e.preventDefault();
33278         
33279         this.fireEvent('download', this);
33280     },
33281     
33282     onTrash : function(e)
33283     {
33284         e.preventDefault();
33285         
33286         this.fireEvent('trash', this);
33287     }
33288     
33289 });
33290 /*
33291  * - LGPL
33292  *
33293  * nav progress bar
33294  * 
33295  */
33296
33297 /**
33298  * @class Roo.bootstrap.NavProgressBar
33299  * @extends Roo.bootstrap.Component
33300  * Bootstrap NavProgressBar class
33301  * 
33302  * @constructor
33303  * Create a new nav progress bar
33304  * @param {Object} config The config object
33305  */
33306
33307 Roo.bootstrap.NavProgressBar = function(config){
33308     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33309
33310     this.bullets = this.bullets || [];
33311    
33312 //    Roo.bootstrap.NavProgressBar.register(this);
33313      this.addEvents({
33314         /**
33315              * @event changed
33316              * Fires when the active item changes
33317              * @param {Roo.bootstrap.NavProgressBar} this
33318              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33319              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33320          */
33321         'changed': true
33322      });
33323     
33324 };
33325
33326 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33327     
33328     bullets : [],
33329     barItems : [],
33330     
33331     getAutoCreate : function()
33332     {
33333         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33334         
33335         cfg = {
33336             tag : 'div',
33337             cls : 'roo-navigation-bar-group',
33338             cn : [
33339                 {
33340                     tag : 'div',
33341                     cls : 'roo-navigation-top-bar'
33342                 },
33343                 {
33344                     tag : 'div',
33345                     cls : 'roo-navigation-bullets-bar',
33346                     cn : [
33347                         {
33348                             tag : 'ul',
33349                             cls : 'roo-navigation-bar'
33350                         }
33351                     ]
33352                 },
33353                 
33354                 {
33355                     tag : 'div',
33356                     cls : 'roo-navigation-bottom-bar'
33357                 }
33358             ]
33359             
33360         };
33361         
33362         return cfg;
33363         
33364     },
33365     
33366     initEvents: function() 
33367     {
33368         
33369     },
33370     
33371     onRender : function(ct, position) 
33372     {
33373         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33374         
33375         if(this.bullets.length){
33376             Roo.each(this.bullets, function(b){
33377                this.addItem(b);
33378             }, this);
33379         }
33380         
33381         this.format();
33382         
33383     },
33384     
33385     addItem : function(cfg)
33386     {
33387         var item = new Roo.bootstrap.NavProgressItem(cfg);
33388         
33389         item.parentId = this.id;
33390         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33391         
33392         if(cfg.html){
33393             var top = new Roo.bootstrap.Element({
33394                 tag : 'div',
33395                 cls : 'roo-navigation-bar-text'
33396             });
33397             
33398             var bottom = new Roo.bootstrap.Element({
33399                 tag : 'div',
33400                 cls : 'roo-navigation-bar-text'
33401             });
33402             
33403             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33404             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33405             
33406             var topText = new Roo.bootstrap.Element({
33407                 tag : 'span',
33408                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33409             });
33410             
33411             var bottomText = new Roo.bootstrap.Element({
33412                 tag : 'span',
33413                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33414             });
33415             
33416             topText.onRender(top.el, null);
33417             bottomText.onRender(bottom.el, null);
33418             
33419             item.topEl = top;
33420             item.bottomEl = bottom;
33421         }
33422         
33423         this.barItems.push(item);
33424         
33425         return item;
33426     },
33427     
33428     getActive : function()
33429     {
33430         var active = false;
33431         
33432         Roo.each(this.barItems, function(v){
33433             
33434             if (!v.isActive()) {
33435                 return;
33436             }
33437             
33438             active = v;
33439             return false;
33440             
33441         });
33442         
33443         return active;
33444     },
33445     
33446     setActiveItem : function(item)
33447     {
33448         var prev = false;
33449         
33450         Roo.each(this.barItems, function(v){
33451             if (v.rid == item.rid) {
33452                 return ;
33453             }
33454             
33455             if (v.isActive()) {
33456                 v.setActive(false);
33457                 prev = v;
33458             }
33459         });
33460
33461         item.setActive(true);
33462         
33463         this.fireEvent('changed', this, item, prev);
33464     },
33465     
33466     getBarItem: function(rid)
33467     {
33468         var ret = false;
33469         
33470         Roo.each(this.barItems, function(e) {
33471             if (e.rid != rid) {
33472                 return;
33473             }
33474             
33475             ret =  e;
33476             return false;
33477         });
33478         
33479         return ret;
33480     },
33481     
33482     indexOfItem : function(item)
33483     {
33484         var index = false;
33485         
33486         Roo.each(this.barItems, function(v, i){
33487             
33488             if (v.rid != item.rid) {
33489                 return;
33490             }
33491             
33492             index = i;
33493             return false
33494         });
33495         
33496         return index;
33497     },
33498     
33499     setActiveNext : function()
33500     {
33501         var i = this.indexOfItem(this.getActive());
33502         
33503         if (i > this.barItems.length) {
33504             return;
33505         }
33506         
33507         this.setActiveItem(this.barItems[i+1]);
33508     },
33509     
33510     setActivePrev : function()
33511     {
33512         var i = this.indexOfItem(this.getActive());
33513         
33514         if (i  < 1) {
33515             return;
33516         }
33517         
33518         this.setActiveItem(this.barItems[i-1]);
33519     },
33520     
33521     format : function()
33522     {
33523         if(!this.barItems.length){
33524             return;
33525         }
33526      
33527         var width = 100 / this.barItems.length;
33528         
33529         Roo.each(this.barItems, function(i){
33530             i.el.setStyle('width', width + '%');
33531             i.topEl.el.setStyle('width', width + '%');
33532             i.bottomEl.el.setStyle('width', width + '%');
33533         }, this);
33534         
33535     }
33536     
33537 });
33538 /*
33539  * - LGPL
33540  *
33541  * Nav Progress Item
33542  * 
33543  */
33544
33545 /**
33546  * @class Roo.bootstrap.NavProgressItem
33547  * @extends Roo.bootstrap.Component
33548  * Bootstrap NavProgressItem class
33549  * @cfg {String} rid the reference id
33550  * @cfg {Boolean} active (true|false) Is item active default false
33551  * @cfg {Boolean} disabled (true|false) Is item active default false
33552  * @cfg {String} html
33553  * @cfg {String} position (top|bottom) text position default bottom
33554  * @cfg {String} icon show icon instead of number
33555  * 
33556  * @constructor
33557  * Create a new NavProgressItem
33558  * @param {Object} config The config object
33559  */
33560 Roo.bootstrap.NavProgressItem = function(config){
33561     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33562     this.addEvents({
33563         // raw events
33564         /**
33565          * @event click
33566          * The raw click event for the entire grid.
33567          * @param {Roo.bootstrap.NavProgressItem} this
33568          * @param {Roo.EventObject} e
33569          */
33570         "click" : true
33571     });
33572    
33573 };
33574
33575 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33576     
33577     rid : '',
33578     active : false,
33579     disabled : false,
33580     html : '',
33581     position : 'bottom',
33582     icon : false,
33583     
33584     getAutoCreate : function()
33585     {
33586         var iconCls = 'roo-navigation-bar-item-icon';
33587         
33588         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33589         
33590         var cfg = {
33591             tag: 'li',
33592             cls: 'roo-navigation-bar-item',
33593             cn : [
33594                 {
33595                     tag : 'i',
33596                     cls : iconCls
33597                 }
33598             ]
33599         };
33600         
33601         if(this.active){
33602             cfg.cls += ' active';
33603         }
33604         if(this.disabled){
33605             cfg.cls += ' disabled';
33606         }
33607         
33608         return cfg;
33609     },
33610     
33611     disable : function()
33612     {
33613         this.setDisabled(true);
33614     },
33615     
33616     enable : function()
33617     {
33618         this.setDisabled(false);
33619     },
33620     
33621     initEvents: function() 
33622     {
33623         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33624         
33625         this.iconEl.on('click', this.onClick, this);
33626     },
33627     
33628     onClick : function(e)
33629     {
33630         e.preventDefault();
33631         
33632         if(this.disabled){
33633             return;
33634         }
33635         
33636         if(this.fireEvent('click', this, e) === false){
33637             return;
33638         };
33639         
33640         this.parent().setActiveItem(this);
33641     },
33642     
33643     isActive: function () 
33644     {
33645         return this.active;
33646     },
33647     
33648     setActive : function(state)
33649     {
33650         if(this.active == state){
33651             return;
33652         }
33653         
33654         this.active = state;
33655         
33656         if (state) {
33657             this.el.addClass('active');
33658             return;
33659         }
33660         
33661         this.el.removeClass('active');
33662         
33663         return;
33664     },
33665     
33666     setDisabled : function(state)
33667     {
33668         if(this.disabled == state){
33669             return;
33670         }
33671         
33672         this.disabled = state;
33673         
33674         if (state) {
33675             this.el.addClass('disabled');
33676             return;
33677         }
33678         
33679         this.el.removeClass('disabled');
33680     },
33681     
33682     tooltipEl : function()
33683     {
33684         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33685     }
33686 });
33687  
33688
33689  /*
33690  * - LGPL
33691  *
33692  * FieldLabel
33693  * 
33694  */
33695
33696 /**
33697  * @class Roo.bootstrap.FieldLabel
33698  * @extends Roo.bootstrap.Component
33699  * Bootstrap FieldLabel class
33700  * @cfg {String} html contents of the element
33701  * @cfg {String} tag tag of the element default label
33702  * @cfg {String} cls class of the element
33703  * @cfg {String} target label target 
33704  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33705  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33706  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33707  * @cfg {String} iconTooltip default "This field is required"
33708  * @cfg {String} indicatorpos (left|right) default left
33709  * 
33710  * @constructor
33711  * Create a new FieldLabel
33712  * @param {Object} config The config object
33713  */
33714
33715 Roo.bootstrap.FieldLabel = function(config){
33716     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33717     
33718     this.addEvents({
33719             /**
33720              * @event invalid
33721              * Fires after the field has been marked as invalid.
33722              * @param {Roo.form.FieldLabel} this
33723              * @param {String} msg The validation message
33724              */
33725             invalid : true,
33726             /**
33727              * @event valid
33728              * Fires after the field has been validated with no errors.
33729              * @param {Roo.form.FieldLabel} this
33730              */
33731             valid : true
33732         });
33733 };
33734
33735 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33736     
33737     tag: 'label',
33738     cls: '',
33739     html: '',
33740     target: '',
33741     allowBlank : true,
33742     invalidClass : 'has-warning',
33743     validClass : 'has-success',
33744     iconTooltip : 'This field is required',
33745     indicatorpos : 'left',
33746     
33747     getAutoCreate : function(){
33748         
33749         var cls = "";
33750         if (!this.allowBlank) {
33751             cls  = "visible";
33752         }
33753         
33754         var cfg = {
33755             tag : this.tag,
33756             cls : 'roo-bootstrap-field-label ' + this.cls,
33757             for : this.target,
33758             cn : [
33759                 {
33760                     tag : 'i',
33761                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33762                     tooltip : this.iconTooltip
33763                 },
33764                 {
33765                     tag : 'span',
33766                     html : this.html
33767                 }
33768             ] 
33769         };
33770         
33771         if(this.indicatorpos == 'right'){
33772             var cfg = {
33773                 tag : this.tag,
33774                 cls : 'roo-bootstrap-field-label ' + this.cls,
33775                 for : this.target,
33776                 cn : [
33777                     {
33778                         tag : 'span',
33779                         html : this.html
33780                     },
33781                     {
33782                         tag : 'i',
33783                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33784                         tooltip : this.iconTooltip
33785                     }
33786                 ] 
33787             };
33788         }
33789         
33790         return cfg;
33791     },
33792     
33793     initEvents: function() 
33794     {
33795         Roo.bootstrap.Element.superclass.initEvents.call(this);
33796         
33797         this.indicator = this.indicatorEl();
33798         
33799         if(this.indicator){
33800             this.indicator.removeClass('visible');
33801             this.indicator.addClass('invisible');
33802         }
33803         
33804         Roo.bootstrap.FieldLabel.register(this);
33805     },
33806     
33807     indicatorEl : function()
33808     {
33809         var indicator = this.el.select('i.roo-required-indicator',true).first();
33810         
33811         if(!indicator){
33812             return false;
33813         }
33814         
33815         return indicator;
33816         
33817     },
33818     
33819     /**
33820      * Mark this field as valid
33821      */
33822     markValid : function()
33823     {
33824         if(this.indicator){
33825             this.indicator.removeClass('visible');
33826             this.indicator.addClass('invisible');
33827         }
33828         if (Roo.bootstrap.version == 3) {
33829             this.el.removeClass(this.invalidClass);
33830             this.el.addClass(this.validClass);
33831         } else {
33832             this.el.removeClass('is-invalid');
33833             this.el.addClass('is-valid');
33834         }
33835         
33836         
33837         this.fireEvent('valid', this);
33838     },
33839     
33840     /**
33841      * Mark this field as invalid
33842      * @param {String} msg The validation message
33843      */
33844     markInvalid : function(msg)
33845     {
33846         if(this.indicator){
33847             this.indicator.removeClass('invisible');
33848             this.indicator.addClass('visible');
33849         }
33850           if (Roo.bootstrap.version == 3) {
33851             this.el.removeClass(this.validClass);
33852             this.el.addClass(this.invalidClass);
33853         } else {
33854             this.el.removeClass('is-valid');
33855             this.el.addClass('is-invalid');
33856         }
33857         
33858         
33859         this.fireEvent('invalid', this, msg);
33860     }
33861     
33862    
33863 });
33864
33865 Roo.apply(Roo.bootstrap.FieldLabel, {
33866     
33867     groups: {},
33868     
33869      /**
33870     * register a FieldLabel Group
33871     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33872     */
33873     register : function(label)
33874     {
33875         if(this.groups.hasOwnProperty(label.target)){
33876             return;
33877         }
33878      
33879         this.groups[label.target] = label;
33880         
33881     },
33882     /**
33883     * fetch a FieldLabel Group based on the target
33884     * @param {string} target
33885     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33886     */
33887     get: function(target) {
33888         if (typeof(this.groups[target]) == 'undefined') {
33889             return false;
33890         }
33891         
33892         return this.groups[target] ;
33893     }
33894 });
33895
33896  
33897
33898  /*
33899  * - LGPL
33900  *
33901  * page DateSplitField.
33902  * 
33903  */
33904
33905
33906 /**
33907  * @class Roo.bootstrap.DateSplitField
33908  * @extends Roo.bootstrap.Component
33909  * Bootstrap DateSplitField class
33910  * @cfg {string} fieldLabel - the label associated
33911  * @cfg {Number} labelWidth set the width of label (0-12)
33912  * @cfg {String} labelAlign (top|left)
33913  * @cfg {Boolean} dayAllowBlank (true|false) default false
33914  * @cfg {Boolean} monthAllowBlank (true|false) default false
33915  * @cfg {Boolean} yearAllowBlank (true|false) default false
33916  * @cfg {string} dayPlaceholder 
33917  * @cfg {string} monthPlaceholder
33918  * @cfg {string} yearPlaceholder
33919  * @cfg {string} dayFormat default 'd'
33920  * @cfg {string} monthFormat default 'm'
33921  * @cfg {string} yearFormat default 'Y'
33922  * @cfg {Number} labellg set the width of label (1-12)
33923  * @cfg {Number} labelmd set the width of label (1-12)
33924  * @cfg {Number} labelsm set the width of label (1-12)
33925  * @cfg {Number} labelxs set the width of label (1-12)
33926
33927  *     
33928  * @constructor
33929  * Create a new DateSplitField
33930  * @param {Object} config The config object
33931  */
33932
33933 Roo.bootstrap.DateSplitField = function(config){
33934     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33935     
33936     this.addEvents({
33937         // raw events
33938          /**
33939          * @event years
33940          * getting the data of years
33941          * @param {Roo.bootstrap.DateSplitField} this
33942          * @param {Object} years
33943          */
33944         "years" : true,
33945         /**
33946          * @event days
33947          * getting the data of days
33948          * @param {Roo.bootstrap.DateSplitField} this
33949          * @param {Object} days
33950          */
33951         "days" : true,
33952         /**
33953          * @event invalid
33954          * Fires after the field has been marked as invalid.
33955          * @param {Roo.form.Field} this
33956          * @param {String} msg The validation message
33957          */
33958         invalid : true,
33959        /**
33960          * @event valid
33961          * Fires after the field has been validated with no errors.
33962          * @param {Roo.form.Field} this
33963          */
33964         valid : true
33965     });
33966 };
33967
33968 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33969     
33970     fieldLabel : '',
33971     labelAlign : 'top',
33972     labelWidth : 3,
33973     dayAllowBlank : false,
33974     monthAllowBlank : false,
33975     yearAllowBlank : false,
33976     dayPlaceholder : '',
33977     monthPlaceholder : '',
33978     yearPlaceholder : '',
33979     dayFormat : 'd',
33980     monthFormat : 'm',
33981     yearFormat : 'Y',
33982     isFormField : true,
33983     labellg : 0,
33984     labelmd : 0,
33985     labelsm : 0,
33986     labelxs : 0,
33987     
33988     getAutoCreate : function()
33989     {
33990         var cfg = {
33991             tag : 'div',
33992             cls : 'row roo-date-split-field-group',
33993             cn : [
33994                 {
33995                     tag : 'input',
33996                     type : 'hidden',
33997                     cls : 'form-hidden-field roo-date-split-field-group-value',
33998                     name : this.name
33999                 }
34000             ]
34001         };
34002         
34003         var labelCls = 'col-md-12';
34004         var contentCls = 'col-md-4';
34005         
34006         if(this.fieldLabel){
34007             
34008             var label = {
34009                 tag : 'div',
34010                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34011                 cn : [
34012                     {
34013                         tag : 'label',
34014                         html : this.fieldLabel
34015                     }
34016                 ]
34017             };
34018             
34019             if(this.labelAlign == 'left'){
34020             
34021                 if(this.labelWidth > 12){
34022                     label.style = "width: " + this.labelWidth + 'px';
34023                 }
34024
34025                 if(this.labelWidth < 13 && this.labelmd == 0){
34026                     this.labelmd = this.labelWidth;
34027                 }
34028
34029                 if(this.labellg > 0){
34030                     labelCls = ' col-lg-' + this.labellg;
34031                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34032                 }
34033
34034                 if(this.labelmd > 0){
34035                     labelCls = ' col-md-' + this.labelmd;
34036                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34037                 }
34038
34039                 if(this.labelsm > 0){
34040                     labelCls = ' col-sm-' + this.labelsm;
34041                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34042                 }
34043
34044                 if(this.labelxs > 0){
34045                     labelCls = ' col-xs-' + this.labelxs;
34046                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34047                 }
34048             }
34049             
34050             label.cls += ' ' + labelCls;
34051             
34052             cfg.cn.push(label);
34053         }
34054         
34055         Roo.each(['day', 'month', 'year'], function(t){
34056             cfg.cn.push({
34057                 tag : 'div',
34058                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34059             });
34060         }, this);
34061         
34062         return cfg;
34063     },
34064     
34065     inputEl: function ()
34066     {
34067         return this.el.select('.roo-date-split-field-group-value', true).first();
34068     },
34069     
34070     onRender : function(ct, position) 
34071     {
34072         var _this = this;
34073         
34074         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34075         
34076         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34077         
34078         this.dayField = new Roo.bootstrap.ComboBox({
34079             allowBlank : this.dayAllowBlank,
34080             alwaysQuery : true,
34081             displayField : 'value',
34082             editable : false,
34083             fieldLabel : '',
34084             forceSelection : true,
34085             mode : 'local',
34086             placeholder : this.dayPlaceholder,
34087             selectOnFocus : true,
34088             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34089             triggerAction : 'all',
34090             typeAhead : true,
34091             valueField : 'value',
34092             store : new Roo.data.SimpleStore({
34093                 data : (function() {    
34094                     var days = [];
34095                     _this.fireEvent('days', _this, days);
34096                     return days;
34097                 })(),
34098                 fields : [ 'value' ]
34099             }),
34100             listeners : {
34101                 select : function (_self, record, index)
34102                 {
34103                     _this.setValue(_this.getValue());
34104                 }
34105             }
34106         });
34107
34108         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34109         
34110         this.monthField = new Roo.bootstrap.MonthField({
34111             after : '<i class=\"fa fa-calendar\"></i>',
34112             allowBlank : this.monthAllowBlank,
34113             placeholder : this.monthPlaceholder,
34114             readOnly : true,
34115             listeners : {
34116                 render : function (_self)
34117                 {
34118                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34119                         e.preventDefault();
34120                         _self.focus();
34121                     });
34122                 },
34123                 select : function (_self, oldvalue, newvalue)
34124                 {
34125                     _this.setValue(_this.getValue());
34126                 }
34127             }
34128         });
34129         
34130         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34131         
34132         this.yearField = new Roo.bootstrap.ComboBox({
34133             allowBlank : this.yearAllowBlank,
34134             alwaysQuery : true,
34135             displayField : 'value',
34136             editable : false,
34137             fieldLabel : '',
34138             forceSelection : true,
34139             mode : 'local',
34140             placeholder : this.yearPlaceholder,
34141             selectOnFocus : true,
34142             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34143             triggerAction : 'all',
34144             typeAhead : true,
34145             valueField : 'value',
34146             store : new Roo.data.SimpleStore({
34147                 data : (function() {
34148                     var years = [];
34149                     _this.fireEvent('years', _this, years);
34150                     return years;
34151                 })(),
34152                 fields : [ 'value' ]
34153             }),
34154             listeners : {
34155                 select : function (_self, record, index)
34156                 {
34157                     _this.setValue(_this.getValue());
34158                 }
34159             }
34160         });
34161
34162         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34163     },
34164     
34165     setValue : function(v, format)
34166     {
34167         this.inputEl.dom.value = v;
34168         
34169         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34170         
34171         var d = Date.parseDate(v, f);
34172         
34173         if(!d){
34174             this.validate();
34175             return;
34176         }
34177         
34178         this.setDay(d.format(this.dayFormat));
34179         this.setMonth(d.format(this.monthFormat));
34180         this.setYear(d.format(this.yearFormat));
34181         
34182         this.validate();
34183         
34184         return;
34185     },
34186     
34187     setDay : function(v)
34188     {
34189         this.dayField.setValue(v);
34190         this.inputEl.dom.value = this.getValue();
34191         this.validate();
34192         return;
34193     },
34194     
34195     setMonth : function(v)
34196     {
34197         this.monthField.setValue(v, true);
34198         this.inputEl.dom.value = this.getValue();
34199         this.validate();
34200         return;
34201     },
34202     
34203     setYear : function(v)
34204     {
34205         this.yearField.setValue(v);
34206         this.inputEl.dom.value = this.getValue();
34207         this.validate();
34208         return;
34209     },
34210     
34211     getDay : function()
34212     {
34213         return this.dayField.getValue();
34214     },
34215     
34216     getMonth : function()
34217     {
34218         return this.monthField.getValue();
34219     },
34220     
34221     getYear : function()
34222     {
34223         return this.yearField.getValue();
34224     },
34225     
34226     getValue : function()
34227     {
34228         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34229         
34230         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34231         
34232         return date;
34233     },
34234     
34235     reset : function()
34236     {
34237         this.setDay('');
34238         this.setMonth('');
34239         this.setYear('');
34240         this.inputEl.dom.value = '';
34241         this.validate();
34242         return;
34243     },
34244     
34245     validate : function()
34246     {
34247         var d = this.dayField.validate();
34248         var m = this.monthField.validate();
34249         var y = this.yearField.validate();
34250         
34251         var valid = true;
34252         
34253         if(
34254                 (!this.dayAllowBlank && !d) ||
34255                 (!this.monthAllowBlank && !m) ||
34256                 (!this.yearAllowBlank && !y)
34257         ){
34258             valid = false;
34259         }
34260         
34261         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34262             return valid;
34263         }
34264         
34265         if(valid){
34266             this.markValid();
34267             return valid;
34268         }
34269         
34270         this.markInvalid();
34271         
34272         return valid;
34273     },
34274     
34275     markValid : function()
34276     {
34277         
34278         var label = this.el.select('label', true).first();
34279         var icon = this.el.select('i.fa-star', true).first();
34280
34281         if(label && icon){
34282             icon.remove();
34283         }
34284         
34285         this.fireEvent('valid', this);
34286     },
34287     
34288      /**
34289      * Mark this field as invalid
34290      * @param {String} msg The validation message
34291      */
34292     markInvalid : function(msg)
34293     {
34294         
34295         var label = this.el.select('label', true).first();
34296         var icon = this.el.select('i.fa-star', true).first();
34297
34298         if(label && !icon){
34299             this.el.select('.roo-date-split-field-label', true).createChild({
34300                 tag : 'i',
34301                 cls : 'text-danger fa fa-lg fa-star',
34302                 tooltip : 'This field is required',
34303                 style : 'margin-right:5px;'
34304             }, label, true);
34305         }
34306         
34307         this.fireEvent('invalid', this, msg);
34308     },
34309     
34310     clearInvalid : function()
34311     {
34312         var label = this.el.select('label', true).first();
34313         var icon = this.el.select('i.fa-star', true).first();
34314
34315         if(label && icon){
34316             icon.remove();
34317         }
34318         
34319         this.fireEvent('valid', this);
34320     },
34321     
34322     getName: function()
34323     {
34324         return this.name;
34325     }
34326     
34327 });
34328
34329  /**
34330  *
34331  * This is based on 
34332  * http://masonry.desandro.com
34333  *
34334  * The idea is to render all the bricks based on vertical width...
34335  *
34336  * The original code extends 'outlayer' - we might need to use that....
34337  * 
34338  */
34339
34340
34341 /**
34342  * @class Roo.bootstrap.LayoutMasonry
34343  * @extends Roo.bootstrap.Component
34344  * Bootstrap Layout Masonry class
34345  * 
34346  * @constructor
34347  * Create a new Element
34348  * @param {Object} config The config object
34349  */
34350
34351 Roo.bootstrap.LayoutMasonry = function(config){
34352     
34353     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34354     
34355     this.bricks = [];
34356     
34357     Roo.bootstrap.LayoutMasonry.register(this);
34358     
34359     this.addEvents({
34360         // raw events
34361         /**
34362          * @event layout
34363          * Fire after layout the items
34364          * @param {Roo.bootstrap.LayoutMasonry} this
34365          * @param {Roo.EventObject} e
34366          */
34367         "layout" : true
34368     });
34369     
34370 };
34371
34372 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34373     
34374     /**
34375      * @cfg {Boolean} isLayoutInstant = no animation?
34376      */   
34377     isLayoutInstant : false, // needed?
34378    
34379     /**
34380      * @cfg {Number} boxWidth  width of the columns
34381      */   
34382     boxWidth : 450,
34383     
34384       /**
34385      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34386      */   
34387     boxHeight : 0,
34388     
34389     /**
34390      * @cfg {Number} padWidth padding below box..
34391      */   
34392     padWidth : 10, 
34393     
34394     /**
34395      * @cfg {Number} gutter gutter width..
34396      */   
34397     gutter : 10,
34398     
34399      /**
34400      * @cfg {Number} maxCols maximum number of columns
34401      */   
34402     
34403     maxCols: 0,
34404     
34405     /**
34406      * @cfg {Boolean} isAutoInitial defalut true
34407      */   
34408     isAutoInitial : true, 
34409     
34410     containerWidth: 0,
34411     
34412     /**
34413      * @cfg {Boolean} isHorizontal defalut false
34414      */   
34415     isHorizontal : false, 
34416
34417     currentSize : null,
34418     
34419     tag: 'div',
34420     
34421     cls: '',
34422     
34423     bricks: null, //CompositeElement
34424     
34425     cols : 1,
34426     
34427     _isLayoutInited : false,
34428     
34429 //    isAlternative : false, // only use for vertical layout...
34430     
34431     /**
34432      * @cfg {Number} alternativePadWidth padding below box..
34433      */   
34434     alternativePadWidth : 50,
34435     
34436     selectedBrick : [],
34437     
34438     getAutoCreate : function(){
34439         
34440         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34441         
34442         var cfg = {
34443             tag: this.tag,
34444             cls: 'blog-masonary-wrapper ' + this.cls,
34445             cn : {
34446                 cls : 'mas-boxes masonary'
34447             }
34448         };
34449         
34450         return cfg;
34451     },
34452     
34453     getChildContainer: function( )
34454     {
34455         if (this.boxesEl) {
34456             return this.boxesEl;
34457         }
34458         
34459         this.boxesEl = this.el.select('.mas-boxes').first();
34460         
34461         return this.boxesEl;
34462     },
34463     
34464     
34465     initEvents : function()
34466     {
34467         var _this = this;
34468         
34469         if(this.isAutoInitial){
34470             Roo.log('hook children rendered');
34471             this.on('childrenrendered', function() {
34472                 Roo.log('children rendered');
34473                 _this.initial();
34474             } ,this);
34475         }
34476     },
34477     
34478     initial : function()
34479     {
34480         this.selectedBrick = [];
34481         
34482         this.currentSize = this.el.getBox(true);
34483         
34484         Roo.EventManager.onWindowResize(this.resize, this); 
34485
34486         if(!this.isAutoInitial){
34487             this.layout();
34488             return;
34489         }
34490         
34491         this.layout();
34492         
34493         return;
34494         //this.layout.defer(500,this);
34495         
34496     },
34497     
34498     resize : function()
34499     {
34500         var cs = this.el.getBox(true);
34501         
34502         if (
34503                 this.currentSize.width == cs.width && 
34504                 this.currentSize.x == cs.x && 
34505                 this.currentSize.height == cs.height && 
34506                 this.currentSize.y == cs.y 
34507         ) {
34508             Roo.log("no change in with or X or Y");
34509             return;
34510         }
34511         
34512         this.currentSize = cs;
34513         
34514         this.layout();
34515         
34516     },
34517     
34518     layout : function()
34519     {   
34520         this._resetLayout();
34521         
34522         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34523         
34524         this.layoutItems( isInstant );
34525       
34526         this._isLayoutInited = true;
34527         
34528         this.fireEvent('layout', this);
34529         
34530     },
34531     
34532     _resetLayout : function()
34533     {
34534         if(this.isHorizontal){
34535             this.horizontalMeasureColumns();
34536             return;
34537         }
34538         
34539         this.verticalMeasureColumns();
34540         
34541     },
34542     
34543     verticalMeasureColumns : function()
34544     {
34545         this.getContainerWidth();
34546         
34547 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34548 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34549 //            return;
34550 //        }
34551         
34552         var boxWidth = this.boxWidth + this.padWidth;
34553         
34554         if(this.containerWidth < this.boxWidth){
34555             boxWidth = this.containerWidth
34556         }
34557         
34558         var containerWidth = this.containerWidth;
34559         
34560         var cols = Math.floor(containerWidth / boxWidth);
34561         
34562         this.cols = Math.max( cols, 1 );
34563         
34564         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34565         
34566         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34567         
34568         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34569         
34570         this.colWidth = boxWidth + avail - this.padWidth;
34571         
34572         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34573         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34574     },
34575     
34576     horizontalMeasureColumns : function()
34577     {
34578         this.getContainerWidth();
34579         
34580         var boxWidth = this.boxWidth;
34581         
34582         if(this.containerWidth < boxWidth){
34583             boxWidth = this.containerWidth;
34584         }
34585         
34586         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34587         
34588         this.el.setHeight(boxWidth);
34589         
34590     },
34591     
34592     getContainerWidth : function()
34593     {
34594         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34595     },
34596     
34597     layoutItems : function( isInstant )
34598     {
34599         Roo.log(this.bricks);
34600         
34601         var items = Roo.apply([], this.bricks);
34602         
34603         if(this.isHorizontal){
34604             this._horizontalLayoutItems( items , isInstant );
34605             return;
34606         }
34607         
34608 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34609 //            this._verticalAlternativeLayoutItems( items , isInstant );
34610 //            return;
34611 //        }
34612         
34613         this._verticalLayoutItems( items , isInstant );
34614         
34615     },
34616     
34617     _verticalLayoutItems : function ( items , isInstant)
34618     {
34619         if ( !items || !items.length ) {
34620             return;
34621         }
34622         
34623         var standard = [
34624             ['xs', 'xs', 'xs', 'tall'],
34625             ['xs', 'xs', 'tall'],
34626             ['xs', 'xs', 'sm'],
34627             ['xs', 'xs', 'xs'],
34628             ['xs', 'tall'],
34629             ['xs', 'sm'],
34630             ['xs', 'xs'],
34631             ['xs'],
34632             
34633             ['sm', 'xs', 'xs'],
34634             ['sm', 'xs'],
34635             ['sm'],
34636             
34637             ['tall', 'xs', 'xs', 'xs'],
34638             ['tall', 'xs', 'xs'],
34639             ['tall', 'xs'],
34640             ['tall']
34641             
34642         ];
34643         
34644         var queue = [];
34645         
34646         var boxes = [];
34647         
34648         var box = [];
34649         
34650         Roo.each(items, function(item, k){
34651             
34652             switch (item.size) {
34653                 // these layouts take up a full box,
34654                 case 'md' :
34655                 case 'md-left' :
34656                 case 'md-right' :
34657                 case 'wide' :
34658                     
34659                     if(box.length){
34660                         boxes.push(box);
34661                         box = [];
34662                     }
34663                     
34664                     boxes.push([item]);
34665                     
34666                     break;
34667                     
34668                 case 'xs' :
34669                 case 'sm' :
34670                 case 'tall' :
34671                     
34672                     box.push(item);
34673                     
34674                     break;
34675                 default :
34676                     break;
34677                     
34678             }
34679             
34680         }, this);
34681         
34682         if(box.length){
34683             boxes.push(box);
34684             box = [];
34685         }
34686         
34687         var filterPattern = function(box, length)
34688         {
34689             if(!box.length){
34690                 return;
34691             }
34692             
34693             var match = false;
34694             
34695             var pattern = box.slice(0, length);
34696             
34697             var format = [];
34698             
34699             Roo.each(pattern, function(i){
34700                 format.push(i.size);
34701             }, this);
34702             
34703             Roo.each(standard, function(s){
34704                 
34705                 if(String(s) != String(format)){
34706                     return;
34707                 }
34708                 
34709                 match = true;
34710                 return false;
34711                 
34712             }, this);
34713             
34714             if(!match && length == 1){
34715                 return;
34716             }
34717             
34718             if(!match){
34719                 filterPattern(box, length - 1);
34720                 return;
34721             }
34722                 
34723             queue.push(pattern);
34724
34725             box = box.slice(length, box.length);
34726
34727             filterPattern(box, 4);
34728
34729             return;
34730             
34731         }
34732         
34733         Roo.each(boxes, function(box, k){
34734             
34735             if(!box.length){
34736                 return;
34737             }
34738             
34739             if(box.length == 1){
34740                 queue.push(box);
34741                 return;
34742             }
34743             
34744             filterPattern(box, 4);
34745             
34746         }, this);
34747         
34748         this._processVerticalLayoutQueue( queue, isInstant );
34749         
34750     },
34751     
34752 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34753 //    {
34754 //        if ( !items || !items.length ) {
34755 //            return;
34756 //        }
34757 //
34758 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34759 //        
34760 //    },
34761     
34762     _horizontalLayoutItems : function ( items , isInstant)
34763     {
34764         if ( !items || !items.length || items.length < 3) {
34765             return;
34766         }
34767         
34768         items.reverse();
34769         
34770         var eItems = items.slice(0, 3);
34771         
34772         items = items.slice(3, items.length);
34773         
34774         var standard = [
34775             ['xs', 'xs', 'xs', 'wide'],
34776             ['xs', 'xs', 'wide'],
34777             ['xs', 'xs', 'sm'],
34778             ['xs', 'xs', 'xs'],
34779             ['xs', 'wide'],
34780             ['xs', 'sm'],
34781             ['xs', 'xs'],
34782             ['xs'],
34783             
34784             ['sm', 'xs', 'xs'],
34785             ['sm', 'xs'],
34786             ['sm'],
34787             
34788             ['wide', 'xs', 'xs', 'xs'],
34789             ['wide', 'xs', 'xs'],
34790             ['wide', 'xs'],
34791             ['wide'],
34792             
34793             ['wide-thin']
34794         ];
34795         
34796         var queue = [];
34797         
34798         var boxes = [];
34799         
34800         var box = [];
34801         
34802         Roo.each(items, function(item, k){
34803             
34804             switch (item.size) {
34805                 case 'md' :
34806                 case 'md-left' :
34807                 case 'md-right' :
34808                 case 'tall' :
34809                     
34810                     if(box.length){
34811                         boxes.push(box);
34812                         box = [];
34813                     }
34814                     
34815                     boxes.push([item]);
34816                     
34817                     break;
34818                     
34819                 case 'xs' :
34820                 case 'sm' :
34821                 case 'wide' :
34822                 case 'wide-thin' :
34823                     
34824                     box.push(item);
34825                     
34826                     break;
34827                 default :
34828                     break;
34829                     
34830             }
34831             
34832         }, this);
34833         
34834         if(box.length){
34835             boxes.push(box);
34836             box = [];
34837         }
34838         
34839         var filterPattern = function(box, length)
34840         {
34841             if(!box.length){
34842                 return;
34843             }
34844             
34845             var match = false;
34846             
34847             var pattern = box.slice(0, length);
34848             
34849             var format = [];
34850             
34851             Roo.each(pattern, function(i){
34852                 format.push(i.size);
34853             }, this);
34854             
34855             Roo.each(standard, function(s){
34856                 
34857                 if(String(s) != String(format)){
34858                     return;
34859                 }
34860                 
34861                 match = true;
34862                 return false;
34863                 
34864             }, this);
34865             
34866             if(!match && length == 1){
34867                 return;
34868             }
34869             
34870             if(!match){
34871                 filterPattern(box, length - 1);
34872                 return;
34873             }
34874                 
34875             queue.push(pattern);
34876
34877             box = box.slice(length, box.length);
34878
34879             filterPattern(box, 4);
34880
34881             return;
34882             
34883         }
34884         
34885         Roo.each(boxes, function(box, k){
34886             
34887             if(!box.length){
34888                 return;
34889             }
34890             
34891             if(box.length == 1){
34892                 queue.push(box);
34893                 return;
34894             }
34895             
34896             filterPattern(box, 4);
34897             
34898         }, this);
34899         
34900         
34901         var prune = [];
34902         
34903         var pos = this.el.getBox(true);
34904         
34905         var minX = pos.x;
34906         
34907         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34908         
34909         var hit_end = false;
34910         
34911         Roo.each(queue, function(box){
34912             
34913             if(hit_end){
34914                 
34915                 Roo.each(box, function(b){
34916                 
34917                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34918                     b.el.hide();
34919
34920                 }, this);
34921
34922                 return;
34923             }
34924             
34925             var mx = 0;
34926             
34927             Roo.each(box, function(b){
34928                 
34929                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34930                 b.el.show();
34931
34932                 mx = Math.max(mx, b.x);
34933                 
34934             }, this);
34935             
34936             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34937             
34938             if(maxX < minX){
34939                 
34940                 Roo.each(box, function(b){
34941                 
34942                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34943                     b.el.hide();
34944                     
34945                 }, this);
34946                 
34947                 hit_end = true;
34948                 
34949                 return;
34950             }
34951             
34952             prune.push(box);
34953             
34954         }, this);
34955         
34956         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34957     },
34958     
34959     /** Sets position of item in DOM
34960     * @param {Element} item
34961     * @param {Number} x - horizontal position
34962     * @param {Number} y - vertical position
34963     * @param {Boolean} isInstant - disables transitions
34964     */
34965     _processVerticalLayoutQueue : function( queue, isInstant )
34966     {
34967         var pos = this.el.getBox(true);
34968         var x = pos.x;
34969         var y = pos.y;
34970         var maxY = [];
34971         
34972         for (var i = 0; i < this.cols; i++){
34973             maxY[i] = pos.y;
34974         }
34975         
34976         Roo.each(queue, function(box, k){
34977             
34978             var col = k % this.cols;
34979             
34980             Roo.each(box, function(b,kk){
34981                 
34982                 b.el.position('absolute');
34983                 
34984                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34985                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34986                 
34987                 if(b.size == 'md-left' || b.size == 'md-right'){
34988                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34989                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34990                 }
34991                 
34992                 b.el.setWidth(width);
34993                 b.el.setHeight(height);
34994                 // iframe?
34995                 b.el.select('iframe',true).setSize(width,height);
34996                 
34997             }, this);
34998             
34999             for (var i = 0; i < this.cols; i++){
35000                 
35001                 if(maxY[i] < maxY[col]){
35002                     col = i;
35003                     continue;
35004                 }
35005                 
35006                 col = Math.min(col, i);
35007                 
35008             }
35009             
35010             x = pos.x + col * (this.colWidth + this.padWidth);
35011             
35012             y = maxY[col];
35013             
35014             var positions = [];
35015             
35016             switch (box.length){
35017                 case 1 :
35018                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35019                     break;
35020                 case 2 :
35021                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35022                     break;
35023                 case 3 :
35024                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35025                     break;
35026                 case 4 :
35027                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35028                     break;
35029                 default :
35030                     break;
35031             }
35032             
35033             Roo.each(box, function(b,kk){
35034                 
35035                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35036                 
35037                 var sz = b.el.getSize();
35038                 
35039                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35040                 
35041             }, this);
35042             
35043         }, this);
35044         
35045         var mY = 0;
35046         
35047         for (var i = 0; i < this.cols; i++){
35048             mY = Math.max(mY, maxY[i]);
35049         }
35050         
35051         this.el.setHeight(mY - pos.y);
35052         
35053     },
35054     
35055 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35056 //    {
35057 //        var pos = this.el.getBox(true);
35058 //        var x = pos.x;
35059 //        var y = pos.y;
35060 //        var maxX = pos.right;
35061 //        
35062 //        var maxHeight = 0;
35063 //        
35064 //        Roo.each(items, function(item, k){
35065 //            
35066 //            var c = k % 2;
35067 //            
35068 //            item.el.position('absolute');
35069 //                
35070 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35071 //
35072 //            item.el.setWidth(width);
35073 //
35074 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35075 //
35076 //            item.el.setHeight(height);
35077 //            
35078 //            if(c == 0){
35079 //                item.el.setXY([x, y], isInstant ? false : true);
35080 //            } else {
35081 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35082 //            }
35083 //            
35084 //            y = y + height + this.alternativePadWidth;
35085 //            
35086 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35087 //            
35088 //        }, this);
35089 //        
35090 //        this.el.setHeight(maxHeight);
35091 //        
35092 //    },
35093     
35094     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35095     {
35096         var pos = this.el.getBox(true);
35097         
35098         var minX = pos.x;
35099         var minY = pos.y;
35100         
35101         var maxX = pos.right;
35102         
35103         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35104         
35105         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35106         
35107         Roo.each(queue, function(box, k){
35108             
35109             Roo.each(box, function(b, kk){
35110                 
35111                 b.el.position('absolute');
35112                 
35113                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35114                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35115                 
35116                 if(b.size == 'md-left' || b.size == 'md-right'){
35117                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35118                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35119                 }
35120                 
35121                 b.el.setWidth(width);
35122                 b.el.setHeight(height);
35123                 
35124             }, this);
35125             
35126             if(!box.length){
35127                 return;
35128             }
35129             
35130             var positions = [];
35131             
35132             switch (box.length){
35133                 case 1 :
35134                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35135                     break;
35136                 case 2 :
35137                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35138                     break;
35139                 case 3 :
35140                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35141                     break;
35142                 case 4 :
35143                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35144                     break;
35145                 default :
35146                     break;
35147             }
35148             
35149             Roo.each(box, function(b,kk){
35150                 
35151                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35152                 
35153                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35154                 
35155             }, this);
35156             
35157         }, this);
35158         
35159     },
35160     
35161     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35162     {
35163         Roo.each(eItems, function(b,k){
35164             
35165             b.size = (k == 0) ? 'sm' : 'xs';
35166             b.x = (k == 0) ? 2 : 1;
35167             b.y = (k == 0) ? 2 : 1;
35168             
35169             b.el.position('absolute');
35170             
35171             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35172                 
35173             b.el.setWidth(width);
35174             
35175             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35176             
35177             b.el.setHeight(height);
35178             
35179         }, this);
35180
35181         var positions = [];
35182         
35183         positions.push({
35184             x : maxX - this.unitWidth * 2 - this.gutter,
35185             y : minY
35186         });
35187         
35188         positions.push({
35189             x : maxX - this.unitWidth,
35190             y : minY + (this.unitWidth + this.gutter) * 2
35191         });
35192         
35193         positions.push({
35194             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35195             y : minY
35196         });
35197         
35198         Roo.each(eItems, function(b,k){
35199             
35200             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35201
35202         }, this);
35203         
35204     },
35205     
35206     getVerticalOneBoxColPositions : function(x, y, box)
35207     {
35208         var pos = [];
35209         
35210         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35211         
35212         if(box[0].size == 'md-left'){
35213             rand = 0;
35214         }
35215         
35216         if(box[0].size == 'md-right'){
35217             rand = 1;
35218         }
35219         
35220         pos.push({
35221             x : x + (this.unitWidth + this.gutter) * rand,
35222             y : y
35223         });
35224         
35225         return pos;
35226     },
35227     
35228     getVerticalTwoBoxColPositions : function(x, y, box)
35229     {
35230         var pos = [];
35231         
35232         if(box[0].size == 'xs'){
35233             
35234             pos.push({
35235                 x : x,
35236                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35237             });
35238
35239             pos.push({
35240                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35241                 y : y
35242             });
35243             
35244             return pos;
35245             
35246         }
35247         
35248         pos.push({
35249             x : x,
35250             y : y
35251         });
35252
35253         pos.push({
35254             x : x + (this.unitWidth + this.gutter) * 2,
35255             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35256         });
35257         
35258         return pos;
35259         
35260     },
35261     
35262     getVerticalThreeBoxColPositions : function(x, y, box)
35263     {
35264         var pos = [];
35265         
35266         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35267             
35268             pos.push({
35269                 x : x,
35270                 y : y
35271             });
35272
35273             pos.push({
35274                 x : x + (this.unitWidth + this.gutter) * 1,
35275                 y : y
35276             });
35277             
35278             pos.push({
35279                 x : x + (this.unitWidth + this.gutter) * 2,
35280                 y : y
35281             });
35282             
35283             return pos;
35284             
35285         }
35286         
35287         if(box[0].size == 'xs' && box[1].size == 'xs'){
35288             
35289             pos.push({
35290                 x : x,
35291                 y : y
35292             });
35293
35294             pos.push({
35295                 x : x,
35296                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35297             });
35298             
35299             pos.push({
35300                 x : x + (this.unitWidth + this.gutter) * 1,
35301                 y : y
35302             });
35303             
35304             return pos;
35305             
35306         }
35307         
35308         pos.push({
35309             x : x,
35310             y : y
35311         });
35312
35313         pos.push({
35314             x : x + (this.unitWidth + this.gutter) * 2,
35315             y : y
35316         });
35317
35318         pos.push({
35319             x : x + (this.unitWidth + this.gutter) * 2,
35320             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35321         });
35322             
35323         return pos;
35324         
35325     },
35326     
35327     getVerticalFourBoxColPositions : function(x, y, box)
35328     {
35329         var pos = [];
35330         
35331         if(box[0].size == 'xs'){
35332             
35333             pos.push({
35334                 x : x,
35335                 y : y
35336             });
35337
35338             pos.push({
35339                 x : x,
35340                 y : y + (this.unitHeight + this.gutter) * 1
35341             });
35342             
35343             pos.push({
35344                 x : x,
35345                 y : y + (this.unitHeight + this.gutter) * 2
35346             });
35347             
35348             pos.push({
35349                 x : x + (this.unitWidth + this.gutter) * 1,
35350                 y : y
35351             });
35352             
35353             return pos;
35354             
35355         }
35356         
35357         pos.push({
35358             x : x,
35359             y : y
35360         });
35361
35362         pos.push({
35363             x : x + (this.unitWidth + this.gutter) * 2,
35364             y : y
35365         });
35366
35367         pos.push({
35368             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35369             y : y + (this.unitHeight + this.gutter) * 1
35370         });
35371
35372         pos.push({
35373             x : x + (this.unitWidth + this.gutter) * 2,
35374             y : y + (this.unitWidth + this.gutter) * 2
35375         });
35376
35377         return pos;
35378         
35379     },
35380     
35381     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35382     {
35383         var pos = [];
35384         
35385         if(box[0].size == 'md-left'){
35386             pos.push({
35387                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35388                 y : minY
35389             });
35390             
35391             return pos;
35392         }
35393         
35394         if(box[0].size == 'md-right'){
35395             pos.push({
35396                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35397                 y : minY + (this.unitWidth + this.gutter) * 1
35398             });
35399             
35400             return pos;
35401         }
35402         
35403         var rand = Math.floor(Math.random() * (4 - box[0].y));
35404         
35405         pos.push({
35406             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35407             y : minY + (this.unitWidth + this.gutter) * rand
35408         });
35409         
35410         return pos;
35411         
35412     },
35413     
35414     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35415     {
35416         var pos = [];
35417         
35418         if(box[0].size == 'xs'){
35419             
35420             pos.push({
35421                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35422                 y : minY
35423             });
35424
35425             pos.push({
35426                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35427                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35428             });
35429             
35430             return pos;
35431             
35432         }
35433         
35434         pos.push({
35435             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35436             y : minY
35437         });
35438
35439         pos.push({
35440             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35441             y : minY + (this.unitWidth + this.gutter) * 2
35442         });
35443         
35444         return pos;
35445         
35446     },
35447     
35448     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35449     {
35450         var pos = [];
35451         
35452         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35453             
35454             pos.push({
35455                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35456                 y : minY
35457             });
35458
35459             pos.push({
35460                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35461                 y : minY + (this.unitWidth + this.gutter) * 1
35462             });
35463             
35464             pos.push({
35465                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35466                 y : minY + (this.unitWidth + this.gutter) * 2
35467             });
35468             
35469             return pos;
35470             
35471         }
35472         
35473         if(box[0].size == 'xs' && box[1].size == 'xs'){
35474             
35475             pos.push({
35476                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35477                 y : minY
35478             });
35479
35480             pos.push({
35481                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35482                 y : minY
35483             });
35484             
35485             pos.push({
35486                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35487                 y : minY + (this.unitWidth + this.gutter) * 1
35488             });
35489             
35490             return pos;
35491             
35492         }
35493         
35494         pos.push({
35495             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35496             y : minY
35497         });
35498
35499         pos.push({
35500             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35501             y : minY + (this.unitWidth + this.gutter) * 2
35502         });
35503
35504         pos.push({
35505             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35506             y : minY + (this.unitWidth + this.gutter) * 2
35507         });
35508             
35509         return pos;
35510         
35511     },
35512     
35513     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35514     {
35515         var pos = [];
35516         
35517         if(box[0].size == 'xs'){
35518             
35519             pos.push({
35520                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35521                 y : minY
35522             });
35523
35524             pos.push({
35525                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35526                 y : minY
35527             });
35528             
35529             pos.push({
35530                 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),
35531                 y : minY
35532             });
35533             
35534             pos.push({
35535                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35536                 y : minY + (this.unitWidth + this.gutter) * 1
35537             });
35538             
35539             return pos;
35540             
35541         }
35542         
35543         pos.push({
35544             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35545             y : minY
35546         });
35547         
35548         pos.push({
35549             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35550             y : minY + (this.unitWidth + this.gutter) * 2
35551         });
35552         
35553         pos.push({
35554             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35555             y : minY + (this.unitWidth + this.gutter) * 2
35556         });
35557         
35558         pos.push({
35559             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),
35560             y : minY + (this.unitWidth + this.gutter) * 2
35561         });
35562
35563         return pos;
35564         
35565     },
35566     
35567     /**
35568     * remove a Masonry Brick
35569     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35570     */
35571     removeBrick : function(brick_id)
35572     {
35573         if (!brick_id) {
35574             return;
35575         }
35576         
35577         for (var i = 0; i<this.bricks.length; i++) {
35578             if (this.bricks[i].id == brick_id) {
35579                 this.bricks.splice(i,1);
35580                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35581                 this.initial();
35582             }
35583         }
35584     },
35585     
35586     /**
35587     * adds a Masonry Brick
35588     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35589     */
35590     addBrick : function(cfg)
35591     {
35592         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35593         //this.register(cn);
35594         cn.parentId = this.id;
35595         cn.render(this.el);
35596         return cn;
35597     },
35598     
35599     /**
35600     * register a Masonry Brick
35601     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35602     */
35603     
35604     register : function(brick)
35605     {
35606         this.bricks.push(brick);
35607         brick.masonryId = this.id;
35608     },
35609     
35610     /**
35611     * clear all the Masonry Brick
35612     */
35613     clearAll : function()
35614     {
35615         this.bricks = [];
35616         //this.getChildContainer().dom.innerHTML = "";
35617         this.el.dom.innerHTML = '';
35618     },
35619     
35620     getSelected : function()
35621     {
35622         if (!this.selectedBrick) {
35623             return false;
35624         }
35625         
35626         return this.selectedBrick;
35627     }
35628 });
35629
35630 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35631     
35632     groups: {},
35633      /**
35634     * register a Masonry Layout
35635     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35636     */
35637     
35638     register : function(layout)
35639     {
35640         this.groups[layout.id] = layout;
35641     },
35642     /**
35643     * fetch a  Masonry Layout based on the masonry layout ID
35644     * @param {string} the masonry layout to add
35645     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35646     */
35647     
35648     get: function(layout_id) {
35649         if (typeof(this.groups[layout_id]) == 'undefined') {
35650             return false;
35651         }
35652         return this.groups[layout_id] ;
35653     }
35654     
35655     
35656     
35657 });
35658
35659  
35660
35661  /**
35662  *
35663  * This is based on 
35664  * http://masonry.desandro.com
35665  *
35666  * The idea is to render all the bricks based on vertical width...
35667  *
35668  * The original code extends 'outlayer' - we might need to use that....
35669  * 
35670  */
35671
35672
35673 /**
35674  * @class Roo.bootstrap.LayoutMasonryAuto
35675  * @extends Roo.bootstrap.Component
35676  * Bootstrap Layout Masonry class
35677  * 
35678  * @constructor
35679  * Create a new Element
35680  * @param {Object} config The config object
35681  */
35682
35683 Roo.bootstrap.LayoutMasonryAuto = function(config){
35684     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35685 };
35686
35687 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35688     
35689       /**
35690      * @cfg {Boolean} isFitWidth  - resize the width..
35691      */   
35692     isFitWidth : false,  // options..
35693     /**
35694      * @cfg {Boolean} isOriginLeft = left align?
35695      */   
35696     isOriginLeft : true,
35697     /**
35698      * @cfg {Boolean} isOriginTop = top align?
35699      */   
35700     isOriginTop : false,
35701     /**
35702      * @cfg {Boolean} isLayoutInstant = no animation?
35703      */   
35704     isLayoutInstant : false, // needed?
35705     /**
35706      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35707      */   
35708     isResizingContainer : true,
35709     /**
35710      * @cfg {Number} columnWidth  width of the columns 
35711      */   
35712     
35713     columnWidth : 0,
35714     
35715     /**
35716      * @cfg {Number} maxCols maximum number of columns
35717      */   
35718     
35719     maxCols: 0,
35720     /**
35721      * @cfg {Number} padHeight padding below box..
35722      */   
35723     
35724     padHeight : 10, 
35725     
35726     /**
35727      * @cfg {Boolean} isAutoInitial defalut true
35728      */   
35729     
35730     isAutoInitial : true, 
35731     
35732     // private?
35733     gutter : 0,
35734     
35735     containerWidth: 0,
35736     initialColumnWidth : 0,
35737     currentSize : null,
35738     
35739     colYs : null, // array.
35740     maxY : 0,
35741     padWidth: 10,
35742     
35743     
35744     tag: 'div',
35745     cls: '',
35746     bricks: null, //CompositeElement
35747     cols : 0, // array?
35748     // element : null, // wrapped now this.el
35749     _isLayoutInited : null, 
35750     
35751     
35752     getAutoCreate : function(){
35753         
35754         var cfg = {
35755             tag: this.tag,
35756             cls: 'blog-masonary-wrapper ' + this.cls,
35757             cn : {
35758                 cls : 'mas-boxes masonary'
35759             }
35760         };
35761         
35762         return cfg;
35763     },
35764     
35765     getChildContainer: function( )
35766     {
35767         if (this.boxesEl) {
35768             return this.boxesEl;
35769         }
35770         
35771         this.boxesEl = this.el.select('.mas-boxes').first();
35772         
35773         return this.boxesEl;
35774     },
35775     
35776     
35777     initEvents : function()
35778     {
35779         var _this = this;
35780         
35781         if(this.isAutoInitial){
35782             Roo.log('hook children rendered');
35783             this.on('childrenrendered', function() {
35784                 Roo.log('children rendered');
35785                 _this.initial();
35786             } ,this);
35787         }
35788         
35789     },
35790     
35791     initial : function()
35792     {
35793         this.reloadItems();
35794
35795         this.currentSize = this.el.getBox(true);
35796
35797         /// was window resize... - let's see if this works..
35798         Roo.EventManager.onWindowResize(this.resize, this); 
35799
35800         if(!this.isAutoInitial){
35801             this.layout();
35802             return;
35803         }
35804         
35805         this.layout.defer(500,this);
35806     },
35807     
35808     reloadItems: function()
35809     {
35810         this.bricks = this.el.select('.masonry-brick', true);
35811         
35812         this.bricks.each(function(b) {
35813             //Roo.log(b.getSize());
35814             if (!b.attr('originalwidth')) {
35815                 b.attr('originalwidth',  b.getSize().width);
35816             }
35817             
35818         });
35819         
35820         Roo.log(this.bricks.elements.length);
35821     },
35822     
35823     resize : function()
35824     {
35825         Roo.log('resize');
35826         var cs = this.el.getBox(true);
35827         
35828         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35829             Roo.log("no change in with or X");
35830             return;
35831         }
35832         this.currentSize = cs;
35833         this.layout();
35834     },
35835     
35836     layout : function()
35837     {
35838          Roo.log('layout');
35839         this._resetLayout();
35840         //this._manageStamps();
35841       
35842         // don't animate first layout
35843         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35844         this.layoutItems( isInstant );
35845       
35846         // flag for initalized
35847         this._isLayoutInited = true;
35848     },
35849     
35850     layoutItems : function( isInstant )
35851     {
35852         //var items = this._getItemsForLayout( this.items );
35853         // original code supports filtering layout items.. we just ignore it..
35854         
35855         this._layoutItems( this.bricks , isInstant );
35856       
35857         this._postLayout();
35858     },
35859     _layoutItems : function ( items , isInstant)
35860     {
35861        //this.fireEvent( 'layout', this, items );
35862     
35863
35864         if ( !items || !items.elements.length ) {
35865           // no items, emit event with empty array
35866             return;
35867         }
35868
35869         var queue = [];
35870         items.each(function(item) {
35871             Roo.log("layout item");
35872             Roo.log(item);
35873             // get x/y object from method
35874             var position = this._getItemLayoutPosition( item );
35875             // enqueue
35876             position.item = item;
35877             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35878             queue.push( position );
35879         }, this);
35880       
35881         this._processLayoutQueue( queue );
35882     },
35883     /** Sets position of item in DOM
35884     * @param {Element} item
35885     * @param {Number} x - horizontal position
35886     * @param {Number} y - vertical position
35887     * @param {Boolean} isInstant - disables transitions
35888     */
35889     _processLayoutQueue : function( queue )
35890     {
35891         for ( var i=0, len = queue.length; i < len; i++ ) {
35892             var obj = queue[i];
35893             obj.item.position('absolute');
35894             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35895         }
35896     },
35897       
35898     
35899     /**
35900     * Any logic you want to do after each layout,
35901     * i.e. size the container
35902     */
35903     _postLayout : function()
35904     {
35905         this.resizeContainer();
35906     },
35907     
35908     resizeContainer : function()
35909     {
35910         if ( !this.isResizingContainer ) {
35911             return;
35912         }
35913         var size = this._getContainerSize();
35914         if ( size ) {
35915             this.el.setSize(size.width,size.height);
35916             this.boxesEl.setSize(size.width,size.height);
35917         }
35918     },
35919     
35920     
35921     
35922     _resetLayout : function()
35923     {
35924         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35925         this.colWidth = this.el.getWidth();
35926         //this.gutter = this.el.getWidth(); 
35927         
35928         this.measureColumns();
35929
35930         // reset column Y
35931         var i = this.cols;
35932         this.colYs = [];
35933         while (i--) {
35934             this.colYs.push( 0 );
35935         }
35936     
35937         this.maxY = 0;
35938     },
35939
35940     measureColumns : function()
35941     {
35942         this.getContainerWidth();
35943       // if columnWidth is 0, default to outerWidth of first item
35944         if ( !this.columnWidth ) {
35945             var firstItem = this.bricks.first();
35946             Roo.log(firstItem);
35947             this.columnWidth  = this.containerWidth;
35948             if (firstItem && firstItem.attr('originalwidth') ) {
35949                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35950             }
35951             // columnWidth fall back to item of first element
35952             Roo.log("set column width?");
35953                         this.initialColumnWidth = this.columnWidth  ;
35954
35955             // if first elem has no width, default to size of container
35956             
35957         }
35958         
35959         
35960         if (this.initialColumnWidth) {
35961             this.columnWidth = this.initialColumnWidth;
35962         }
35963         
35964         
35965             
35966         // column width is fixed at the top - however if container width get's smaller we should
35967         // reduce it...
35968         
35969         // this bit calcs how man columns..
35970             
35971         var columnWidth = this.columnWidth += this.gutter;
35972       
35973         // calculate columns
35974         var containerWidth = this.containerWidth + this.gutter;
35975         
35976         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35977         // fix rounding errors, typically with gutters
35978         var excess = columnWidth - containerWidth % columnWidth;
35979         
35980         
35981         // if overshoot is less than a pixel, round up, otherwise floor it
35982         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35983         cols = Math[ mathMethod ]( cols );
35984         this.cols = Math.max( cols, 1 );
35985         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35986         
35987          // padding positioning..
35988         var totalColWidth = this.cols * this.columnWidth;
35989         var padavail = this.containerWidth - totalColWidth;
35990         // so for 2 columns - we need 3 'pads'
35991         
35992         var padNeeded = (1+this.cols) * this.padWidth;
35993         
35994         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35995         
35996         this.columnWidth += padExtra
35997         //this.padWidth = Math.floor(padavail /  ( this.cols));
35998         
35999         // adjust colum width so that padding is fixed??
36000         
36001         // we have 3 columns ... total = width * 3
36002         // we have X left over... that should be used by 
36003         
36004         //if (this.expandC) {
36005             
36006         //}
36007         
36008         
36009         
36010     },
36011     
36012     getContainerWidth : function()
36013     {
36014        /* // container is parent if fit width
36015         var container = this.isFitWidth ? this.element.parentNode : this.element;
36016         // check that this.size and size are there
36017         // IE8 triggers resize on body size change, so they might not be
36018         
36019         var size = getSize( container );  //FIXME
36020         this.containerWidth = size && size.innerWidth; //FIXME
36021         */
36022          
36023         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36024         
36025     },
36026     
36027     _getItemLayoutPosition : function( item )  // what is item?
36028     {
36029         // we resize the item to our columnWidth..
36030       
36031         item.setWidth(this.columnWidth);
36032         item.autoBoxAdjust  = false;
36033         
36034         var sz = item.getSize();
36035  
36036         // how many columns does this brick span
36037         var remainder = this.containerWidth % this.columnWidth;
36038         
36039         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36040         // round if off by 1 pixel, otherwise use ceil
36041         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36042         colSpan = Math.min( colSpan, this.cols );
36043         
36044         // normally this should be '1' as we dont' currently allow multi width columns..
36045         
36046         var colGroup = this._getColGroup( colSpan );
36047         // get the minimum Y value from the columns
36048         var minimumY = Math.min.apply( Math, colGroup );
36049         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36050         
36051         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36052          
36053         // position the brick
36054         var position = {
36055             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36056             y: this.currentSize.y + minimumY + this.padHeight
36057         };
36058         
36059         Roo.log(position);
36060         // apply setHeight to necessary columns
36061         var setHeight = minimumY + sz.height + this.padHeight;
36062         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36063         
36064         var setSpan = this.cols + 1 - colGroup.length;
36065         for ( var i = 0; i < setSpan; i++ ) {
36066           this.colYs[ shortColIndex + i ] = setHeight ;
36067         }
36068       
36069         return position;
36070     },
36071     
36072     /**
36073      * @param {Number} colSpan - number of columns the element spans
36074      * @returns {Array} colGroup
36075      */
36076     _getColGroup : function( colSpan )
36077     {
36078         if ( colSpan < 2 ) {
36079           // if brick spans only one column, use all the column Ys
36080           return this.colYs;
36081         }
36082       
36083         var colGroup = [];
36084         // how many different places could this brick fit horizontally
36085         var groupCount = this.cols + 1 - colSpan;
36086         // for each group potential horizontal position
36087         for ( var i = 0; i < groupCount; i++ ) {
36088           // make an array of colY values for that one group
36089           var groupColYs = this.colYs.slice( i, i + colSpan );
36090           // and get the max value of the array
36091           colGroup[i] = Math.max.apply( Math, groupColYs );
36092         }
36093         return colGroup;
36094     },
36095     /*
36096     _manageStamp : function( stamp )
36097     {
36098         var stampSize =  stamp.getSize();
36099         var offset = stamp.getBox();
36100         // get the columns that this stamp affects
36101         var firstX = this.isOriginLeft ? offset.x : offset.right;
36102         var lastX = firstX + stampSize.width;
36103         var firstCol = Math.floor( firstX / this.columnWidth );
36104         firstCol = Math.max( 0, firstCol );
36105         
36106         var lastCol = Math.floor( lastX / this.columnWidth );
36107         // lastCol should not go over if multiple of columnWidth #425
36108         lastCol -= lastX % this.columnWidth ? 0 : 1;
36109         lastCol = Math.min( this.cols - 1, lastCol );
36110         
36111         // set colYs to bottom of the stamp
36112         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36113             stampSize.height;
36114             
36115         for ( var i = firstCol; i <= lastCol; i++ ) {
36116           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36117         }
36118     },
36119     */
36120     
36121     _getContainerSize : function()
36122     {
36123         this.maxY = Math.max.apply( Math, this.colYs );
36124         var size = {
36125             height: this.maxY
36126         };
36127       
36128         if ( this.isFitWidth ) {
36129             size.width = this._getContainerFitWidth();
36130         }
36131       
36132         return size;
36133     },
36134     
36135     _getContainerFitWidth : function()
36136     {
36137         var unusedCols = 0;
36138         // count unused columns
36139         var i = this.cols;
36140         while ( --i ) {
36141           if ( this.colYs[i] !== 0 ) {
36142             break;
36143           }
36144           unusedCols++;
36145         }
36146         // fit container to columns that have been used
36147         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36148     },
36149     
36150     needsResizeLayout : function()
36151     {
36152         var previousWidth = this.containerWidth;
36153         this.getContainerWidth();
36154         return previousWidth !== this.containerWidth;
36155     }
36156  
36157 });
36158
36159  
36160
36161  /*
36162  * - LGPL
36163  *
36164  * element
36165  * 
36166  */
36167
36168 /**
36169  * @class Roo.bootstrap.MasonryBrick
36170  * @extends Roo.bootstrap.Component
36171  * Bootstrap MasonryBrick class
36172  * 
36173  * @constructor
36174  * Create a new MasonryBrick
36175  * @param {Object} config The config object
36176  */
36177
36178 Roo.bootstrap.MasonryBrick = function(config){
36179     
36180     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36181     
36182     Roo.bootstrap.MasonryBrick.register(this);
36183     
36184     this.addEvents({
36185         // raw events
36186         /**
36187          * @event click
36188          * When a MasonryBrick is clcik
36189          * @param {Roo.bootstrap.MasonryBrick} this
36190          * @param {Roo.EventObject} e
36191          */
36192         "click" : true
36193     });
36194 };
36195
36196 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36197     
36198     /**
36199      * @cfg {String} title
36200      */   
36201     title : '',
36202     /**
36203      * @cfg {String} html
36204      */   
36205     html : '',
36206     /**
36207      * @cfg {String} bgimage
36208      */   
36209     bgimage : '',
36210     /**
36211      * @cfg {String} videourl
36212      */   
36213     videourl : '',
36214     /**
36215      * @cfg {String} cls
36216      */   
36217     cls : '',
36218     /**
36219      * @cfg {String} href
36220      */   
36221     href : '',
36222     /**
36223      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36224      */   
36225     size : 'xs',
36226     
36227     /**
36228      * @cfg {String} placetitle (center|bottom)
36229      */   
36230     placetitle : '',
36231     
36232     /**
36233      * @cfg {Boolean} isFitContainer defalut true
36234      */   
36235     isFitContainer : true, 
36236     
36237     /**
36238      * @cfg {Boolean} preventDefault defalut false
36239      */   
36240     preventDefault : false, 
36241     
36242     /**
36243      * @cfg {Boolean} inverse defalut false
36244      */   
36245     maskInverse : false, 
36246     
36247     getAutoCreate : function()
36248     {
36249         if(!this.isFitContainer){
36250             return this.getSplitAutoCreate();
36251         }
36252         
36253         var cls = 'masonry-brick masonry-brick-full';
36254         
36255         if(this.href.length){
36256             cls += ' masonry-brick-link';
36257         }
36258         
36259         if(this.bgimage.length){
36260             cls += ' masonry-brick-image';
36261         }
36262         
36263         if(this.maskInverse){
36264             cls += ' mask-inverse';
36265         }
36266         
36267         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36268             cls += ' enable-mask';
36269         }
36270         
36271         if(this.size){
36272             cls += ' masonry-' + this.size + '-brick';
36273         }
36274         
36275         if(this.placetitle.length){
36276             
36277             switch (this.placetitle) {
36278                 case 'center' :
36279                     cls += ' masonry-center-title';
36280                     break;
36281                 case 'bottom' :
36282                     cls += ' masonry-bottom-title';
36283                     break;
36284                 default:
36285                     break;
36286             }
36287             
36288         } else {
36289             if(!this.html.length && !this.bgimage.length){
36290                 cls += ' masonry-center-title';
36291             }
36292
36293             if(!this.html.length && this.bgimage.length){
36294                 cls += ' masonry-bottom-title';
36295             }
36296         }
36297         
36298         if(this.cls){
36299             cls += ' ' + this.cls;
36300         }
36301         
36302         var cfg = {
36303             tag: (this.href.length) ? 'a' : 'div',
36304             cls: cls,
36305             cn: [
36306                 {
36307                     tag: 'div',
36308                     cls: 'masonry-brick-mask'
36309                 },
36310                 {
36311                     tag: 'div',
36312                     cls: 'masonry-brick-paragraph',
36313                     cn: []
36314                 }
36315             ]
36316         };
36317         
36318         if(this.href.length){
36319             cfg.href = this.href;
36320         }
36321         
36322         var cn = cfg.cn[1].cn;
36323         
36324         if(this.title.length){
36325             cn.push({
36326                 tag: 'h4',
36327                 cls: 'masonry-brick-title',
36328                 html: this.title
36329             });
36330         }
36331         
36332         if(this.html.length){
36333             cn.push({
36334                 tag: 'p',
36335                 cls: 'masonry-brick-text',
36336                 html: this.html
36337             });
36338         }
36339         
36340         if (!this.title.length && !this.html.length) {
36341             cfg.cn[1].cls += ' hide';
36342         }
36343         
36344         if(this.bgimage.length){
36345             cfg.cn.push({
36346                 tag: 'img',
36347                 cls: 'masonry-brick-image-view',
36348                 src: this.bgimage
36349             });
36350         }
36351         
36352         if(this.videourl.length){
36353             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36354             // youtube support only?
36355             cfg.cn.push({
36356                 tag: 'iframe',
36357                 cls: 'masonry-brick-image-view',
36358                 src: vurl,
36359                 frameborder : 0,
36360                 allowfullscreen : true
36361             });
36362         }
36363         
36364         return cfg;
36365         
36366     },
36367     
36368     getSplitAutoCreate : function()
36369     {
36370         var cls = 'masonry-brick masonry-brick-split';
36371         
36372         if(this.href.length){
36373             cls += ' masonry-brick-link';
36374         }
36375         
36376         if(this.bgimage.length){
36377             cls += ' masonry-brick-image';
36378         }
36379         
36380         if(this.size){
36381             cls += ' masonry-' + this.size + '-brick';
36382         }
36383         
36384         switch (this.placetitle) {
36385             case 'center' :
36386                 cls += ' masonry-center-title';
36387                 break;
36388             case 'bottom' :
36389                 cls += ' masonry-bottom-title';
36390                 break;
36391             default:
36392                 if(!this.bgimage.length){
36393                     cls += ' masonry-center-title';
36394                 }
36395
36396                 if(this.bgimage.length){
36397                     cls += ' masonry-bottom-title';
36398                 }
36399                 break;
36400         }
36401         
36402         if(this.cls){
36403             cls += ' ' + this.cls;
36404         }
36405         
36406         var cfg = {
36407             tag: (this.href.length) ? 'a' : 'div',
36408             cls: cls,
36409             cn: [
36410                 {
36411                     tag: 'div',
36412                     cls: 'masonry-brick-split-head',
36413                     cn: [
36414                         {
36415                             tag: 'div',
36416                             cls: 'masonry-brick-paragraph',
36417                             cn: []
36418                         }
36419                     ]
36420                 },
36421                 {
36422                     tag: 'div',
36423                     cls: 'masonry-brick-split-body',
36424                     cn: []
36425                 }
36426             ]
36427         };
36428         
36429         if(this.href.length){
36430             cfg.href = this.href;
36431         }
36432         
36433         if(this.title.length){
36434             cfg.cn[0].cn[0].cn.push({
36435                 tag: 'h4',
36436                 cls: 'masonry-brick-title',
36437                 html: this.title
36438             });
36439         }
36440         
36441         if(this.html.length){
36442             cfg.cn[1].cn.push({
36443                 tag: 'p',
36444                 cls: 'masonry-brick-text',
36445                 html: this.html
36446             });
36447         }
36448
36449         if(this.bgimage.length){
36450             cfg.cn[0].cn.push({
36451                 tag: 'img',
36452                 cls: 'masonry-brick-image-view',
36453                 src: this.bgimage
36454             });
36455         }
36456         
36457         if(this.videourl.length){
36458             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36459             // youtube support only?
36460             cfg.cn[0].cn.cn.push({
36461                 tag: 'iframe',
36462                 cls: 'masonry-brick-image-view',
36463                 src: vurl,
36464                 frameborder : 0,
36465                 allowfullscreen : true
36466             });
36467         }
36468         
36469         return cfg;
36470     },
36471     
36472     initEvents: function() 
36473     {
36474         switch (this.size) {
36475             case 'xs' :
36476                 this.x = 1;
36477                 this.y = 1;
36478                 break;
36479             case 'sm' :
36480                 this.x = 2;
36481                 this.y = 2;
36482                 break;
36483             case 'md' :
36484             case 'md-left' :
36485             case 'md-right' :
36486                 this.x = 3;
36487                 this.y = 3;
36488                 break;
36489             case 'tall' :
36490                 this.x = 2;
36491                 this.y = 3;
36492                 break;
36493             case 'wide' :
36494                 this.x = 3;
36495                 this.y = 2;
36496                 break;
36497             case 'wide-thin' :
36498                 this.x = 3;
36499                 this.y = 1;
36500                 break;
36501                         
36502             default :
36503                 break;
36504         }
36505         
36506         if(Roo.isTouch){
36507             this.el.on('touchstart', this.onTouchStart, this);
36508             this.el.on('touchmove', this.onTouchMove, this);
36509             this.el.on('touchend', this.onTouchEnd, this);
36510             this.el.on('contextmenu', this.onContextMenu, this);
36511         } else {
36512             this.el.on('mouseenter'  ,this.enter, this);
36513             this.el.on('mouseleave', this.leave, this);
36514             this.el.on('click', this.onClick, this);
36515         }
36516         
36517         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36518             this.parent().bricks.push(this);   
36519         }
36520         
36521     },
36522     
36523     onClick: function(e, el)
36524     {
36525         var time = this.endTimer - this.startTimer;
36526         // Roo.log(e.preventDefault());
36527         if(Roo.isTouch){
36528             if(time > 1000){
36529                 e.preventDefault();
36530                 return;
36531             }
36532         }
36533         
36534         if(!this.preventDefault){
36535             return;
36536         }
36537         
36538         e.preventDefault();
36539         
36540         if (this.activeClass != '') {
36541             this.selectBrick();
36542         }
36543         
36544         this.fireEvent('click', this, e);
36545     },
36546     
36547     enter: function(e, el)
36548     {
36549         e.preventDefault();
36550         
36551         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36552             return;
36553         }
36554         
36555         if(this.bgimage.length && this.html.length){
36556             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36557         }
36558     },
36559     
36560     leave: function(e, el)
36561     {
36562         e.preventDefault();
36563         
36564         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36565             return;
36566         }
36567         
36568         if(this.bgimage.length && this.html.length){
36569             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36570         }
36571     },
36572     
36573     onTouchStart: function(e, el)
36574     {
36575 //        e.preventDefault();
36576         
36577         this.touchmoved = false;
36578         
36579         if(!this.isFitContainer){
36580             return;
36581         }
36582         
36583         if(!this.bgimage.length || !this.html.length){
36584             return;
36585         }
36586         
36587         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36588         
36589         this.timer = new Date().getTime();
36590         
36591     },
36592     
36593     onTouchMove: function(e, el)
36594     {
36595         this.touchmoved = true;
36596     },
36597     
36598     onContextMenu : function(e,el)
36599     {
36600         e.preventDefault();
36601         e.stopPropagation();
36602         return false;
36603     },
36604     
36605     onTouchEnd: function(e, el)
36606     {
36607 //        e.preventDefault();
36608         
36609         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36610         
36611             this.leave(e,el);
36612             
36613             return;
36614         }
36615         
36616         if(!this.bgimage.length || !this.html.length){
36617             
36618             if(this.href.length){
36619                 window.location.href = this.href;
36620             }
36621             
36622             return;
36623         }
36624         
36625         if(!this.isFitContainer){
36626             return;
36627         }
36628         
36629         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36630         
36631         window.location.href = this.href;
36632     },
36633     
36634     //selection on single brick only
36635     selectBrick : function() {
36636         
36637         if (!this.parentId) {
36638             return;
36639         }
36640         
36641         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36642         var index = m.selectedBrick.indexOf(this.id);
36643         
36644         if ( index > -1) {
36645             m.selectedBrick.splice(index,1);
36646             this.el.removeClass(this.activeClass);
36647             return;
36648         }
36649         
36650         for(var i = 0; i < m.selectedBrick.length; i++) {
36651             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36652             b.el.removeClass(b.activeClass);
36653         }
36654         
36655         m.selectedBrick = [];
36656         
36657         m.selectedBrick.push(this.id);
36658         this.el.addClass(this.activeClass);
36659         return;
36660     },
36661     
36662     isSelected : function(){
36663         return this.el.hasClass(this.activeClass);
36664         
36665     }
36666 });
36667
36668 Roo.apply(Roo.bootstrap.MasonryBrick, {
36669     
36670     //groups: {},
36671     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36672      /**
36673     * register a Masonry Brick
36674     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36675     */
36676     
36677     register : function(brick)
36678     {
36679         //this.groups[brick.id] = brick;
36680         this.groups.add(brick.id, brick);
36681     },
36682     /**
36683     * fetch a  masonry brick based on the masonry brick ID
36684     * @param {string} the masonry brick to add
36685     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36686     */
36687     
36688     get: function(brick_id) 
36689     {
36690         // if (typeof(this.groups[brick_id]) == 'undefined') {
36691         //     return false;
36692         // }
36693         // return this.groups[brick_id] ;
36694         
36695         if(this.groups.key(brick_id)) {
36696             return this.groups.key(brick_id);
36697         }
36698         
36699         return false;
36700     }
36701     
36702     
36703     
36704 });
36705
36706  /*
36707  * - LGPL
36708  *
36709  * element
36710  * 
36711  */
36712
36713 /**
36714  * @class Roo.bootstrap.Brick
36715  * @extends Roo.bootstrap.Component
36716  * Bootstrap Brick class
36717  * 
36718  * @constructor
36719  * Create a new Brick
36720  * @param {Object} config The config object
36721  */
36722
36723 Roo.bootstrap.Brick = function(config){
36724     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36725     
36726     this.addEvents({
36727         // raw events
36728         /**
36729          * @event click
36730          * When a Brick is click
36731          * @param {Roo.bootstrap.Brick} this
36732          * @param {Roo.EventObject} e
36733          */
36734         "click" : true
36735     });
36736 };
36737
36738 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36739     
36740     /**
36741      * @cfg {String} title
36742      */   
36743     title : '',
36744     /**
36745      * @cfg {String} html
36746      */   
36747     html : '',
36748     /**
36749      * @cfg {String} bgimage
36750      */   
36751     bgimage : '',
36752     /**
36753      * @cfg {String} cls
36754      */   
36755     cls : '',
36756     /**
36757      * @cfg {String} href
36758      */   
36759     href : '',
36760     /**
36761      * @cfg {String} video
36762      */   
36763     video : '',
36764     /**
36765      * @cfg {Boolean} square
36766      */   
36767     square : true,
36768     
36769     getAutoCreate : function()
36770     {
36771         var cls = 'roo-brick';
36772         
36773         if(this.href.length){
36774             cls += ' roo-brick-link';
36775         }
36776         
36777         if(this.bgimage.length){
36778             cls += ' roo-brick-image';
36779         }
36780         
36781         if(!this.html.length && !this.bgimage.length){
36782             cls += ' roo-brick-center-title';
36783         }
36784         
36785         if(!this.html.length && this.bgimage.length){
36786             cls += ' roo-brick-bottom-title';
36787         }
36788         
36789         if(this.cls){
36790             cls += ' ' + this.cls;
36791         }
36792         
36793         var cfg = {
36794             tag: (this.href.length) ? 'a' : 'div',
36795             cls: cls,
36796             cn: [
36797                 {
36798                     tag: 'div',
36799                     cls: 'roo-brick-paragraph',
36800                     cn: []
36801                 }
36802             ]
36803         };
36804         
36805         if(this.href.length){
36806             cfg.href = this.href;
36807         }
36808         
36809         var cn = cfg.cn[0].cn;
36810         
36811         if(this.title.length){
36812             cn.push({
36813                 tag: 'h4',
36814                 cls: 'roo-brick-title',
36815                 html: this.title
36816             });
36817         }
36818         
36819         if(this.html.length){
36820             cn.push({
36821                 tag: 'p',
36822                 cls: 'roo-brick-text',
36823                 html: this.html
36824             });
36825         } else {
36826             cn.cls += ' hide';
36827         }
36828         
36829         if(this.bgimage.length){
36830             cfg.cn.push({
36831                 tag: 'img',
36832                 cls: 'roo-brick-image-view',
36833                 src: this.bgimage
36834             });
36835         }
36836         
36837         return cfg;
36838     },
36839     
36840     initEvents: function() 
36841     {
36842         if(this.title.length || this.html.length){
36843             this.el.on('mouseenter'  ,this.enter, this);
36844             this.el.on('mouseleave', this.leave, this);
36845         }
36846         
36847         Roo.EventManager.onWindowResize(this.resize, this); 
36848         
36849         if(this.bgimage.length){
36850             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36851             this.imageEl.on('load', this.onImageLoad, this);
36852             return;
36853         }
36854         
36855         this.resize();
36856     },
36857     
36858     onImageLoad : function()
36859     {
36860         this.resize();
36861     },
36862     
36863     resize : function()
36864     {
36865         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36866         
36867         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36868         
36869         if(this.bgimage.length){
36870             var image = this.el.select('.roo-brick-image-view', true).first();
36871             
36872             image.setWidth(paragraph.getWidth());
36873             
36874             if(this.square){
36875                 image.setHeight(paragraph.getWidth());
36876             }
36877             
36878             this.el.setHeight(image.getHeight());
36879             paragraph.setHeight(image.getHeight());
36880             
36881         }
36882         
36883     },
36884     
36885     enter: function(e, el)
36886     {
36887         e.preventDefault();
36888         
36889         if(this.bgimage.length){
36890             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36891             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36892         }
36893     },
36894     
36895     leave: function(e, el)
36896     {
36897         e.preventDefault();
36898         
36899         if(this.bgimage.length){
36900             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36901             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36902         }
36903     }
36904     
36905 });
36906
36907  
36908
36909  /*
36910  * - LGPL
36911  *
36912  * Number field 
36913  */
36914
36915 /**
36916  * @class Roo.bootstrap.NumberField
36917  * @extends Roo.bootstrap.Input
36918  * Bootstrap NumberField class
36919  * 
36920  * 
36921  * 
36922  * 
36923  * @constructor
36924  * Create a new NumberField
36925  * @param {Object} config The config object
36926  */
36927
36928 Roo.bootstrap.NumberField = function(config){
36929     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36930 };
36931
36932 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36933     
36934     /**
36935      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36936      */
36937     allowDecimals : true,
36938     /**
36939      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36940      */
36941     decimalSeparator : ".",
36942     /**
36943      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36944      */
36945     decimalPrecision : 2,
36946     /**
36947      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36948      */
36949     allowNegative : true,
36950     
36951     /**
36952      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36953      */
36954     allowZero: true,
36955     /**
36956      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36957      */
36958     minValue : Number.NEGATIVE_INFINITY,
36959     /**
36960      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36961      */
36962     maxValue : Number.MAX_VALUE,
36963     /**
36964      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36965      */
36966     minText : "The minimum value for this field is {0}",
36967     /**
36968      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36969      */
36970     maxText : "The maximum value for this field is {0}",
36971     /**
36972      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36973      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36974      */
36975     nanText : "{0} is not a valid number",
36976     /**
36977      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36978      */
36979     thousandsDelimiter : false,
36980     /**
36981      * @cfg {String} valueAlign alignment of value
36982      */
36983     valueAlign : "left",
36984
36985     getAutoCreate : function()
36986     {
36987         var hiddenInput = {
36988             tag: 'input',
36989             type: 'hidden',
36990             id: Roo.id(),
36991             cls: 'hidden-number-input'
36992         };
36993         
36994         if (this.name) {
36995             hiddenInput.name = this.name;
36996         }
36997         
36998         this.name = '';
36999         
37000         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37001         
37002         this.name = hiddenInput.name;
37003         
37004         if(cfg.cn.length > 0) {
37005             cfg.cn.push(hiddenInput);
37006         }
37007         
37008         return cfg;
37009     },
37010
37011     // private
37012     initEvents : function()
37013     {   
37014         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37015         
37016         var allowed = "0123456789";
37017         
37018         if(this.allowDecimals){
37019             allowed += this.decimalSeparator;
37020         }
37021         
37022         if(this.allowNegative){
37023             allowed += "-";
37024         }
37025         
37026         if(this.thousandsDelimiter) {
37027             allowed += ",";
37028         }
37029         
37030         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37031         
37032         var keyPress = function(e){
37033             
37034             var k = e.getKey();
37035             
37036             var c = e.getCharCode();
37037             
37038             if(
37039                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37040                     allowed.indexOf(String.fromCharCode(c)) === -1
37041             ){
37042                 e.stopEvent();
37043                 return;
37044             }
37045             
37046             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37047                 return;
37048             }
37049             
37050             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37051                 e.stopEvent();
37052             }
37053         };
37054         
37055         this.el.on("keypress", keyPress, this);
37056     },
37057     
37058     validateValue : function(value)
37059     {
37060         
37061         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37062             return false;
37063         }
37064         
37065         var num = this.parseValue(value);
37066         
37067         if(isNaN(num)){
37068             this.markInvalid(String.format(this.nanText, value));
37069             return false;
37070         }
37071         
37072         if(num < this.minValue){
37073             this.markInvalid(String.format(this.minText, this.minValue));
37074             return false;
37075         }
37076         
37077         if(num > this.maxValue){
37078             this.markInvalid(String.format(this.maxText, this.maxValue));
37079             return false;
37080         }
37081         
37082         return true;
37083     },
37084
37085     getValue : function()
37086     {
37087         var v = this.hiddenEl().getValue();
37088         
37089         return this.fixPrecision(this.parseValue(v));
37090     },
37091
37092     parseValue : function(value)
37093     {
37094         if(this.thousandsDelimiter) {
37095             value += "";
37096             r = new RegExp(",", "g");
37097             value = value.replace(r, "");
37098         }
37099         
37100         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37101         return isNaN(value) ? '' : value;
37102     },
37103
37104     fixPrecision : function(value)
37105     {
37106         if(this.thousandsDelimiter) {
37107             value += "";
37108             r = new RegExp(",", "g");
37109             value = value.replace(r, "");
37110         }
37111         
37112         var nan = isNaN(value);
37113         
37114         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37115             return nan ? '' : value;
37116         }
37117         return parseFloat(value).toFixed(this.decimalPrecision);
37118     },
37119
37120     setValue : function(v)
37121     {
37122         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37123         
37124         this.value = v;
37125         
37126         if(this.rendered){
37127             
37128             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37129             
37130             this.inputEl().dom.value = (v == '') ? '' :
37131                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37132             
37133             if(!this.allowZero && v === '0') {
37134                 this.hiddenEl().dom.value = '';
37135                 this.inputEl().dom.value = '';
37136             }
37137             
37138             this.validate();
37139         }
37140     },
37141
37142     decimalPrecisionFcn : function(v)
37143     {
37144         return Math.floor(v);
37145     },
37146
37147     beforeBlur : function()
37148     {
37149         var v = this.parseValue(this.getRawValue());
37150         
37151         if(v || v === 0 || v === ''){
37152             this.setValue(v);
37153         }
37154     },
37155     
37156     hiddenEl : function()
37157     {
37158         return this.el.select('input.hidden-number-input',true).first();
37159     }
37160     
37161 });
37162
37163  
37164
37165 /*
37166 * Licence: LGPL
37167 */
37168
37169 /**
37170  * @class Roo.bootstrap.DocumentSlider
37171  * @extends Roo.bootstrap.Component
37172  * Bootstrap DocumentSlider class
37173  * 
37174  * @constructor
37175  * Create a new DocumentViewer
37176  * @param {Object} config The config object
37177  */
37178
37179 Roo.bootstrap.DocumentSlider = function(config){
37180     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37181     
37182     this.files = [];
37183     
37184     this.addEvents({
37185         /**
37186          * @event initial
37187          * Fire after initEvent
37188          * @param {Roo.bootstrap.DocumentSlider} this
37189          */
37190         "initial" : true,
37191         /**
37192          * @event update
37193          * Fire after update
37194          * @param {Roo.bootstrap.DocumentSlider} this
37195          */
37196         "update" : true,
37197         /**
37198          * @event click
37199          * Fire after click
37200          * @param {Roo.bootstrap.DocumentSlider} this
37201          */
37202         "click" : true
37203     });
37204 };
37205
37206 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37207     
37208     files : false,
37209     
37210     indicator : 0,
37211     
37212     getAutoCreate : function()
37213     {
37214         var cfg = {
37215             tag : 'div',
37216             cls : 'roo-document-slider',
37217             cn : [
37218                 {
37219                     tag : 'div',
37220                     cls : 'roo-document-slider-header',
37221                     cn : [
37222                         {
37223                             tag : 'div',
37224                             cls : 'roo-document-slider-header-title'
37225                         }
37226                     ]
37227                 },
37228                 {
37229                     tag : 'div',
37230                     cls : 'roo-document-slider-body',
37231                     cn : [
37232                         {
37233                             tag : 'div',
37234                             cls : 'roo-document-slider-prev',
37235                             cn : [
37236                                 {
37237                                     tag : 'i',
37238                                     cls : 'fa fa-chevron-left'
37239                                 }
37240                             ]
37241                         },
37242                         {
37243                             tag : 'div',
37244                             cls : 'roo-document-slider-thumb',
37245                             cn : [
37246                                 {
37247                                     tag : 'img',
37248                                     cls : 'roo-document-slider-image'
37249                                 }
37250                             ]
37251                         },
37252                         {
37253                             tag : 'div',
37254                             cls : 'roo-document-slider-next',
37255                             cn : [
37256                                 {
37257                                     tag : 'i',
37258                                     cls : 'fa fa-chevron-right'
37259                                 }
37260                             ]
37261                         }
37262                     ]
37263                 }
37264             ]
37265         };
37266         
37267         return cfg;
37268     },
37269     
37270     initEvents : function()
37271     {
37272         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37273         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37274         
37275         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37276         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37277         
37278         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37279         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37280         
37281         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37282         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37283         
37284         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37285         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37286         
37287         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37288         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37289         
37290         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37291         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37292         
37293         this.thumbEl.on('click', this.onClick, this);
37294         
37295         this.prevIndicator.on('click', this.prev, this);
37296         
37297         this.nextIndicator.on('click', this.next, this);
37298         
37299     },
37300     
37301     initial : function()
37302     {
37303         if(this.files.length){
37304             this.indicator = 1;
37305             this.update()
37306         }
37307         
37308         this.fireEvent('initial', this);
37309     },
37310     
37311     update : function()
37312     {
37313         this.imageEl.attr('src', this.files[this.indicator - 1]);
37314         
37315         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37316         
37317         this.prevIndicator.show();
37318         
37319         if(this.indicator == 1){
37320             this.prevIndicator.hide();
37321         }
37322         
37323         this.nextIndicator.show();
37324         
37325         if(this.indicator == this.files.length){
37326             this.nextIndicator.hide();
37327         }
37328         
37329         this.thumbEl.scrollTo('top');
37330         
37331         this.fireEvent('update', this);
37332     },
37333     
37334     onClick : function(e)
37335     {
37336         e.preventDefault();
37337         
37338         this.fireEvent('click', this);
37339     },
37340     
37341     prev : function(e)
37342     {
37343         e.preventDefault();
37344         
37345         this.indicator = Math.max(1, this.indicator - 1);
37346         
37347         this.update();
37348     },
37349     
37350     next : function(e)
37351     {
37352         e.preventDefault();
37353         
37354         this.indicator = Math.min(this.files.length, this.indicator + 1);
37355         
37356         this.update();
37357     }
37358 });
37359 /*
37360  * - LGPL
37361  *
37362  * RadioSet
37363  *
37364  *
37365  */
37366
37367 /**
37368  * @class Roo.bootstrap.RadioSet
37369  * @extends Roo.bootstrap.Input
37370  * Bootstrap RadioSet class
37371  * @cfg {String} indicatorpos (left|right) default left
37372  * @cfg {Boolean} inline (true|false) inline the element (default true)
37373  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37374  * @constructor
37375  * Create a new RadioSet
37376  * @param {Object} config The config object
37377  */
37378
37379 Roo.bootstrap.RadioSet = function(config){
37380     
37381     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37382     
37383     this.radioes = [];
37384     
37385     Roo.bootstrap.RadioSet.register(this);
37386     
37387     this.addEvents({
37388         /**
37389         * @event check
37390         * Fires when the element is checked or unchecked.
37391         * @param {Roo.bootstrap.RadioSet} this This radio
37392         * @param {Roo.bootstrap.Radio} item The checked item
37393         */
37394        check : true,
37395        /**
37396         * @event click
37397         * Fires when the element is click.
37398         * @param {Roo.bootstrap.RadioSet} this This radio set
37399         * @param {Roo.bootstrap.Radio} item The checked item
37400         * @param {Roo.EventObject} e The event object
37401         */
37402        click : true
37403     });
37404     
37405 };
37406
37407 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37408
37409     radioes : false,
37410     
37411     inline : true,
37412     
37413     weight : '',
37414     
37415     indicatorpos : 'left',
37416     
37417     getAutoCreate : function()
37418     {
37419         var label = {
37420             tag : 'label',
37421             cls : 'roo-radio-set-label',
37422             cn : [
37423                 {
37424                     tag : 'span',
37425                     html : this.fieldLabel
37426                 }
37427             ]
37428         };
37429         if (Roo.bootstrap.version == 3) {
37430             
37431             
37432             if(this.indicatorpos == 'left'){
37433                 label.cn.unshift({
37434                     tag : 'i',
37435                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37436                     tooltip : 'This field is required'
37437                 });
37438             } else {
37439                 label.cn.push({
37440                     tag : 'i',
37441                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37442                     tooltip : 'This field is required'
37443                 });
37444             }
37445         }
37446         var items = {
37447             tag : 'div',
37448             cls : 'roo-radio-set-items'
37449         };
37450         
37451         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37452         
37453         if (align === 'left' && this.fieldLabel.length) {
37454             
37455             items = {
37456                 cls : "roo-radio-set-right", 
37457                 cn: [
37458                     items
37459                 ]
37460             };
37461             
37462             if(this.labelWidth > 12){
37463                 label.style = "width: " + this.labelWidth + 'px';
37464             }
37465             
37466             if(this.labelWidth < 13 && this.labelmd == 0){
37467                 this.labelmd = this.labelWidth;
37468             }
37469             
37470             if(this.labellg > 0){
37471                 label.cls += ' col-lg-' + this.labellg;
37472                 items.cls += ' col-lg-' + (12 - this.labellg);
37473             }
37474             
37475             if(this.labelmd > 0){
37476                 label.cls += ' col-md-' + this.labelmd;
37477                 items.cls += ' col-md-' + (12 - this.labelmd);
37478             }
37479             
37480             if(this.labelsm > 0){
37481                 label.cls += ' col-sm-' + this.labelsm;
37482                 items.cls += ' col-sm-' + (12 - this.labelsm);
37483             }
37484             
37485             if(this.labelxs > 0){
37486                 label.cls += ' col-xs-' + this.labelxs;
37487                 items.cls += ' col-xs-' + (12 - this.labelxs);
37488             }
37489         }
37490         
37491         var cfg = {
37492             tag : 'div',
37493             cls : 'roo-radio-set',
37494             cn : [
37495                 {
37496                     tag : 'input',
37497                     cls : 'roo-radio-set-input',
37498                     type : 'hidden',
37499                     name : this.name,
37500                     value : this.value ? this.value :  ''
37501                 },
37502                 label,
37503                 items
37504             ]
37505         };
37506         
37507         if(this.weight.length){
37508             cfg.cls += ' roo-radio-' + this.weight;
37509         }
37510         
37511         if(this.inline) {
37512             cfg.cls += ' roo-radio-set-inline';
37513         }
37514         
37515         var settings=this;
37516         ['xs','sm','md','lg'].map(function(size){
37517             if (settings[size]) {
37518                 cfg.cls += ' col-' + size + '-' + settings[size];
37519             }
37520         });
37521         
37522         return cfg;
37523         
37524     },
37525
37526     initEvents : function()
37527     {
37528         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37529         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37530         
37531         if(!this.fieldLabel.length){
37532             this.labelEl.hide();
37533         }
37534         
37535         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37536         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37537         
37538         this.indicator = this.indicatorEl();
37539         
37540         if(this.indicator){
37541             this.indicator.addClass('invisible');
37542         }
37543         
37544         this.originalValue = this.getValue();
37545         
37546     },
37547     
37548     inputEl: function ()
37549     {
37550         return this.el.select('.roo-radio-set-input', true).first();
37551     },
37552     
37553     getChildContainer : function()
37554     {
37555         return this.itemsEl;
37556     },
37557     
37558     register : function(item)
37559     {
37560         this.radioes.push(item);
37561         
37562     },
37563     
37564     validate : function()
37565     {   
37566         if(this.getVisibilityEl().hasClass('hidden')){
37567             return true;
37568         }
37569         
37570         var valid = false;
37571         
37572         Roo.each(this.radioes, function(i){
37573             if(!i.checked){
37574                 return;
37575             }
37576             
37577             valid = true;
37578             return false;
37579         });
37580         
37581         if(this.allowBlank) {
37582             return true;
37583         }
37584         
37585         if(this.disabled || valid){
37586             this.markValid();
37587             return true;
37588         }
37589         
37590         this.markInvalid();
37591         return false;
37592         
37593     },
37594     
37595     markValid : function()
37596     {
37597         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37598             this.indicatorEl().removeClass('visible');
37599             this.indicatorEl().addClass('invisible');
37600         }
37601         
37602         
37603         if (Roo.bootstrap.version == 3) {
37604             this.el.removeClass([this.invalidClass, this.validClass]);
37605             this.el.addClass(this.validClass);
37606         } else {
37607             this.el.removeClass(['is-invalid','is-valid']);
37608             this.el.addClass(['is-valid']);
37609         }
37610         this.fireEvent('valid', this);
37611     },
37612     
37613     markInvalid : function(msg)
37614     {
37615         if(this.allowBlank || this.disabled){
37616             return;
37617         }
37618         
37619         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37620             this.indicatorEl().removeClass('invisible');
37621             this.indicatorEl().addClass('visible');
37622         }
37623         if (Roo.bootstrap.version == 3) {
37624             this.el.removeClass([this.invalidClass, this.validClass]);
37625             this.el.addClass(this.invalidClass);
37626         } else {
37627             this.el.removeClass(['is-invalid','is-valid']);
37628             this.el.addClass(['is-invalid']);
37629         }
37630         
37631         this.fireEvent('invalid', this, msg);
37632         
37633     },
37634     
37635     setValue : function(v, suppressEvent)
37636     {   
37637         if(this.value === v){
37638             return;
37639         }
37640         
37641         this.value = v;
37642         
37643         if(this.rendered){
37644             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37645         }
37646         
37647         Roo.each(this.radioes, function(i){
37648             i.checked = false;
37649             i.el.removeClass('checked');
37650         });
37651         
37652         Roo.each(this.radioes, function(i){
37653             
37654             if(i.value === v || i.value.toString() === v.toString()){
37655                 i.checked = true;
37656                 i.el.addClass('checked');
37657                 
37658                 if(suppressEvent !== true){
37659                     this.fireEvent('check', this, i);
37660                 }
37661                 
37662                 return false;
37663             }
37664             
37665         }, this);
37666         
37667         this.validate();
37668     },
37669     
37670     clearInvalid : function(){
37671         
37672         if(!this.el || this.preventMark){
37673             return;
37674         }
37675         
37676         this.el.removeClass([this.invalidClass]);
37677         
37678         this.fireEvent('valid', this);
37679     }
37680     
37681 });
37682
37683 Roo.apply(Roo.bootstrap.RadioSet, {
37684     
37685     groups: {},
37686     
37687     register : function(set)
37688     {
37689         this.groups[set.name] = set;
37690     },
37691     
37692     get: function(name) 
37693     {
37694         if (typeof(this.groups[name]) == 'undefined') {
37695             return false;
37696         }
37697         
37698         return this.groups[name] ;
37699     }
37700     
37701 });
37702 /*
37703  * Based on:
37704  * Ext JS Library 1.1.1
37705  * Copyright(c) 2006-2007, Ext JS, LLC.
37706  *
37707  * Originally Released Under LGPL - original licence link has changed is not relivant.
37708  *
37709  * Fork - LGPL
37710  * <script type="text/javascript">
37711  */
37712
37713
37714 /**
37715  * @class Roo.bootstrap.SplitBar
37716  * @extends Roo.util.Observable
37717  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37718  * <br><br>
37719  * Usage:
37720  * <pre><code>
37721 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37722                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37723 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37724 split.minSize = 100;
37725 split.maxSize = 600;
37726 split.animate = true;
37727 split.on('moved', splitterMoved);
37728 </code></pre>
37729  * @constructor
37730  * Create a new SplitBar
37731  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37732  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37733  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37734  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37735                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37736                         position of the SplitBar).
37737  */
37738 Roo.bootstrap.SplitBar = function(cfg){
37739     
37740     /** @private */
37741     
37742     //{
37743     //  dragElement : elm
37744     //  resizingElement: el,
37745         // optional..
37746     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37747     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37748         // existingProxy ???
37749     //}
37750     
37751     this.el = Roo.get(cfg.dragElement, true);
37752     this.el.dom.unselectable = "on";
37753     /** @private */
37754     this.resizingEl = Roo.get(cfg.resizingElement, true);
37755
37756     /**
37757      * @private
37758      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37759      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37760      * @type Number
37761      */
37762     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37763     
37764     /**
37765      * The minimum size of the resizing element. (Defaults to 0)
37766      * @type Number
37767      */
37768     this.minSize = 0;
37769     
37770     /**
37771      * The maximum size of the resizing element. (Defaults to 2000)
37772      * @type Number
37773      */
37774     this.maxSize = 2000;
37775     
37776     /**
37777      * Whether to animate the transition to the new size
37778      * @type Boolean
37779      */
37780     this.animate = false;
37781     
37782     /**
37783      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37784      * @type Boolean
37785      */
37786     this.useShim = false;
37787     
37788     /** @private */
37789     this.shim = null;
37790     
37791     if(!cfg.existingProxy){
37792         /** @private */
37793         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37794     }else{
37795         this.proxy = Roo.get(cfg.existingProxy).dom;
37796     }
37797     /** @private */
37798     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37799     
37800     /** @private */
37801     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37802     
37803     /** @private */
37804     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37805     
37806     /** @private */
37807     this.dragSpecs = {};
37808     
37809     /**
37810      * @private The adapter to use to positon and resize elements
37811      */
37812     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37813     this.adapter.init(this);
37814     
37815     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37816         /** @private */
37817         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37818         this.el.addClass("roo-splitbar-h");
37819     }else{
37820         /** @private */
37821         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37822         this.el.addClass("roo-splitbar-v");
37823     }
37824     
37825     this.addEvents({
37826         /**
37827          * @event resize
37828          * Fires when the splitter is moved (alias for {@link #event-moved})
37829          * @param {Roo.bootstrap.SplitBar} this
37830          * @param {Number} newSize the new width or height
37831          */
37832         "resize" : true,
37833         /**
37834          * @event moved
37835          * Fires when the splitter is moved
37836          * @param {Roo.bootstrap.SplitBar} this
37837          * @param {Number} newSize the new width or height
37838          */
37839         "moved" : true,
37840         /**
37841          * @event beforeresize
37842          * Fires before the splitter is dragged
37843          * @param {Roo.bootstrap.SplitBar} this
37844          */
37845         "beforeresize" : true,
37846
37847         "beforeapply" : true
37848     });
37849
37850     Roo.util.Observable.call(this);
37851 };
37852
37853 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37854     onStartProxyDrag : function(x, y){
37855         this.fireEvent("beforeresize", this);
37856         if(!this.overlay){
37857             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37858             o.unselectable();
37859             o.enableDisplayMode("block");
37860             // all splitbars share the same overlay
37861             Roo.bootstrap.SplitBar.prototype.overlay = o;
37862         }
37863         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37864         this.overlay.show();
37865         Roo.get(this.proxy).setDisplayed("block");
37866         var size = this.adapter.getElementSize(this);
37867         this.activeMinSize = this.getMinimumSize();;
37868         this.activeMaxSize = this.getMaximumSize();;
37869         var c1 = size - this.activeMinSize;
37870         var c2 = Math.max(this.activeMaxSize - size, 0);
37871         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37872             this.dd.resetConstraints();
37873             this.dd.setXConstraint(
37874                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37875                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37876             );
37877             this.dd.setYConstraint(0, 0);
37878         }else{
37879             this.dd.resetConstraints();
37880             this.dd.setXConstraint(0, 0);
37881             this.dd.setYConstraint(
37882                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37883                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37884             );
37885          }
37886         this.dragSpecs.startSize = size;
37887         this.dragSpecs.startPoint = [x, y];
37888         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37889     },
37890     
37891     /** 
37892      * @private Called after the drag operation by the DDProxy
37893      */
37894     onEndProxyDrag : function(e){
37895         Roo.get(this.proxy).setDisplayed(false);
37896         var endPoint = Roo.lib.Event.getXY(e);
37897         if(this.overlay){
37898             this.overlay.hide();
37899         }
37900         var newSize;
37901         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37902             newSize = this.dragSpecs.startSize + 
37903                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37904                     endPoint[0] - this.dragSpecs.startPoint[0] :
37905                     this.dragSpecs.startPoint[0] - endPoint[0]
37906                 );
37907         }else{
37908             newSize = this.dragSpecs.startSize + 
37909                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37910                     endPoint[1] - this.dragSpecs.startPoint[1] :
37911                     this.dragSpecs.startPoint[1] - endPoint[1]
37912                 );
37913         }
37914         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37915         if(newSize != this.dragSpecs.startSize){
37916             if(this.fireEvent('beforeapply', this, newSize) !== false){
37917                 this.adapter.setElementSize(this, newSize);
37918                 this.fireEvent("moved", this, newSize);
37919                 this.fireEvent("resize", this, newSize);
37920             }
37921         }
37922     },
37923     
37924     /**
37925      * Get the adapter this SplitBar uses
37926      * @return The adapter object
37927      */
37928     getAdapter : function(){
37929         return this.adapter;
37930     },
37931     
37932     /**
37933      * Set the adapter this SplitBar uses
37934      * @param {Object} adapter A SplitBar adapter object
37935      */
37936     setAdapter : function(adapter){
37937         this.adapter = adapter;
37938         this.adapter.init(this);
37939     },
37940     
37941     /**
37942      * Gets the minimum size for the resizing element
37943      * @return {Number} The minimum size
37944      */
37945     getMinimumSize : function(){
37946         return this.minSize;
37947     },
37948     
37949     /**
37950      * Sets the minimum size for the resizing element
37951      * @param {Number} minSize The minimum size
37952      */
37953     setMinimumSize : function(minSize){
37954         this.minSize = minSize;
37955     },
37956     
37957     /**
37958      * Gets the maximum size for the resizing element
37959      * @return {Number} The maximum size
37960      */
37961     getMaximumSize : function(){
37962         return this.maxSize;
37963     },
37964     
37965     /**
37966      * Sets the maximum size for the resizing element
37967      * @param {Number} maxSize The maximum size
37968      */
37969     setMaximumSize : function(maxSize){
37970         this.maxSize = maxSize;
37971     },
37972     
37973     /**
37974      * Sets the initialize size for the resizing element
37975      * @param {Number} size The initial size
37976      */
37977     setCurrentSize : function(size){
37978         var oldAnimate = this.animate;
37979         this.animate = false;
37980         this.adapter.setElementSize(this, size);
37981         this.animate = oldAnimate;
37982     },
37983     
37984     /**
37985      * Destroy this splitbar. 
37986      * @param {Boolean} removeEl True to remove the element
37987      */
37988     destroy : function(removeEl){
37989         if(this.shim){
37990             this.shim.remove();
37991         }
37992         this.dd.unreg();
37993         this.proxy.parentNode.removeChild(this.proxy);
37994         if(removeEl){
37995             this.el.remove();
37996         }
37997     }
37998 });
37999
38000 /**
38001  * @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.
38002  */
38003 Roo.bootstrap.SplitBar.createProxy = function(dir){
38004     var proxy = new Roo.Element(document.createElement("div"));
38005     proxy.unselectable();
38006     var cls = 'roo-splitbar-proxy';
38007     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38008     document.body.appendChild(proxy.dom);
38009     return proxy.dom;
38010 };
38011
38012 /** 
38013  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38014  * Default Adapter. It assumes the splitter and resizing element are not positioned
38015  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38016  */
38017 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38018 };
38019
38020 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38021     // do nothing for now
38022     init : function(s){
38023     
38024     },
38025     /**
38026      * Called before drag operations to get the current size of the resizing element. 
38027      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38028      */
38029      getElementSize : function(s){
38030         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38031             return s.resizingEl.getWidth();
38032         }else{
38033             return s.resizingEl.getHeight();
38034         }
38035     },
38036     
38037     /**
38038      * Called after drag operations to set the size of the resizing element.
38039      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38040      * @param {Number} newSize The new size to set
38041      * @param {Function} onComplete A function to be invoked when resizing is complete
38042      */
38043     setElementSize : function(s, newSize, onComplete){
38044         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38045             if(!s.animate){
38046                 s.resizingEl.setWidth(newSize);
38047                 if(onComplete){
38048                     onComplete(s, newSize);
38049                 }
38050             }else{
38051                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38052             }
38053         }else{
38054             
38055             if(!s.animate){
38056                 s.resizingEl.setHeight(newSize);
38057                 if(onComplete){
38058                     onComplete(s, newSize);
38059                 }
38060             }else{
38061                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38062             }
38063         }
38064     }
38065 };
38066
38067 /** 
38068  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38069  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38070  * Adapter that  moves the splitter element to align with the resized sizing element. 
38071  * Used with an absolute positioned SplitBar.
38072  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38073  * document.body, make sure you assign an id to the body element.
38074  */
38075 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38076     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38077     this.container = Roo.get(container);
38078 };
38079
38080 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38081     init : function(s){
38082         this.basic.init(s);
38083     },
38084     
38085     getElementSize : function(s){
38086         return this.basic.getElementSize(s);
38087     },
38088     
38089     setElementSize : function(s, newSize, onComplete){
38090         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38091     },
38092     
38093     moveSplitter : function(s){
38094         var yes = Roo.bootstrap.SplitBar;
38095         switch(s.placement){
38096             case yes.LEFT:
38097                 s.el.setX(s.resizingEl.getRight());
38098                 break;
38099             case yes.RIGHT:
38100                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38101                 break;
38102             case yes.TOP:
38103                 s.el.setY(s.resizingEl.getBottom());
38104                 break;
38105             case yes.BOTTOM:
38106                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38107                 break;
38108         }
38109     }
38110 };
38111
38112 /**
38113  * Orientation constant - Create a vertical SplitBar
38114  * @static
38115  * @type Number
38116  */
38117 Roo.bootstrap.SplitBar.VERTICAL = 1;
38118
38119 /**
38120  * Orientation constant - Create a horizontal SplitBar
38121  * @static
38122  * @type Number
38123  */
38124 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38125
38126 /**
38127  * Placement constant - The resizing element is to the left of the splitter element
38128  * @static
38129  * @type Number
38130  */
38131 Roo.bootstrap.SplitBar.LEFT = 1;
38132
38133 /**
38134  * Placement constant - The resizing element is to the right of the splitter element
38135  * @static
38136  * @type Number
38137  */
38138 Roo.bootstrap.SplitBar.RIGHT = 2;
38139
38140 /**
38141  * Placement constant - The resizing element is positioned above the splitter element
38142  * @static
38143  * @type Number
38144  */
38145 Roo.bootstrap.SplitBar.TOP = 3;
38146
38147 /**
38148  * Placement constant - The resizing element is positioned under splitter element
38149  * @static
38150  * @type Number
38151  */
38152 Roo.bootstrap.SplitBar.BOTTOM = 4;
38153 Roo.namespace("Roo.bootstrap.layout");/*
38154  * Based on:
38155  * Ext JS Library 1.1.1
38156  * Copyright(c) 2006-2007, Ext JS, LLC.
38157  *
38158  * Originally Released Under LGPL - original licence link has changed is not relivant.
38159  *
38160  * Fork - LGPL
38161  * <script type="text/javascript">
38162  */
38163
38164 /**
38165  * @class Roo.bootstrap.layout.Manager
38166  * @extends Roo.bootstrap.Component
38167  * Base class for layout managers.
38168  */
38169 Roo.bootstrap.layout.Manager = function(config)
38170 {
38171     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38172
38173
38174
38175
38176
38177     /** false to disable window resize monitoring @type Boolean */
38178     this.monitorWindowResize = true;
38179     this.regions = {};
38180     this.addEvents({
38181         /**
38182          * @event layout
38183          * Fires when a layout is performed.
38184          * @param {Roo.LayoutManager} this
38185          */
38186         "layout" : true,
38187         /**
38188          * @event regionresized
38189          * Fires when the user resizes a region.
38190          * @param {Roo.LayoutRegion} region The resized region
38191          * @param {Number} newSize The new size (width for east/west, height for north/south)
38192          */
38193         "regionresized" : true,
38194         /**
38195          * @event regioncollapsed
38196          * Fires when a region is collapsed.
38197          * @param {Roo.LayoutRegion} region The collapsed region
38198          */
38199         "regioncollapsed" : true,
38200         /**
38201          * @event regionexpanded
38202          * Fires when a region is expanded.
38203          * @param {Roo.LayoutRegion} region The expanded region
38204          */
38205         "regionexpanded" : true
38206     });
38207     this.updating = false;
38208
38209     if (config.el) {
38210         this.el = Roo.get(config.el);
38211         this.initEvents();
38212     }
38213
38214 };
38215
38216 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38217
38218
38219     regions : null,
38220
38221     monitorWindowResize : true,
38222
38223
38224     updating : false,
38225
38226
38227     onRender : function(ct, position)
38228     {
38229         if(!this.el){
38230             this.el = Roo.get(ct);
38231             this.initEvents();
38232         }
38233         //this.fireEvent('render',this);
38234     },
38235
38236
38237     initEvents: function()
38238     {
38239
38240
38241         // ie scrollbar fix
38242         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38243             document.body.scroll = "no";
38244         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38245             this.el.position('relative');
38246         }
38247         this.id = this.el.id;
38248         this.el.addClass("roo-layout-container");
38249         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38250         if(this.el.dom != document.body ) {
38251             this.el.on('resize', this.layout,this);
38252             this.el.on('show', this.layout,this);
38253         }
38254
38255     },
38256
38257     /**
38258      * Returns true if this layout is currently being updated
38259      * @return {Boolean}
38260      */
38261     isUpdating : function(){
38262         return this.updating;
38263     },
38264
38265     /**
38266      * Suspend the LayoutManager from doing auto-layouts while
38267      * making multiple add or remove calls
38268      */
38269     beginUpdate : function(){
38270         this.updating = true;
38271     },
38272
38273     /**
38274      * Restore auto-layouts and optionally disable the manager from performing a layout
38275      * @param {Boolean} noLayout true to disable a layout update
38276      */
38277     endUpdate : function(noLayout){
38278         this.updating = false;
38279         if(!noLayout){
38280             this.layout();
38281         }
38282     },
38283
38284     layout: function(){
38285         // abstract...
38286     },
38287
38288     onRegionResized : function(region, newSize){
38289         this.fireEvent("regionresized", region, newSize);
38290         this.layout();
38291     },
38292
38293     onRegionCollapsed : function(region){
38294         this.fireEvent("regioncollapsed", region);
38295     },
38296
38297     onRegionExpanded : function(region){
38298         this.fireEvent("regionexpanded", region);
38299     },
38300
38301     /**
38302      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38303      * performs box-model adjustments.
38304      * @return {Object} The size as an object {width: (the width), height: (the height)}
38305      */
38306     getViewSize : function()
38307     {
38308         var size;
38309         if(this.el.dom != document.body){
38310             size = this.el.getSize();
38311         }else{
38312             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38313         }
38314         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38315         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38316         return size;
38317     },
38318
38319     /**
38320      * Returns the Element this layout is bound to.
38321      * @return {Roo.Element}
38322      */
38323     getEl : function(){
38324         return this.el;
38325     },
38326
38327     /**
38328      * Returns the specified region.
38329      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38330      * @return {Roo.LayoutRegion}
38331      */
38332     getRegion : function(target){
38333         return this.regions[target.toLowerCase()];
38334     },
38335
38336     onWindowResize : function(){
38337         if(this.monitorWindowResize){
38338             this.layout();
38339         }
38340     }
38341 });
38342 /*
38343  * Based on:
38344  * Ext JS Library 1.1.1
38345  * Copyright(c) 2006-2007, Ext JS, LLC.
38346  *
38347  * Originally Released Under LGPL - original licence link has changed is not relivant.
38348  *
38349  * Fork - LGPL
38350  * <script type="text/javascript">
38351  */
38352 /**
38353  * @class Roo.bootstrap.layout.Border
38354  * @extends Roo.bootstrap.layout.Manager
38355  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38356  * please see: examples/bootstrap/nested.html<br><br>
38357  
38358 <b>The container the layout is rendered into can be either the body element or any other element.
38359 If it is not the body element, the container needs to either be an absolute positioned element,
38360 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38361 the container size if it is not the body element.</b>
38362
38363 * @constructor
38364 * Create a new Border
38365 * @param {Object} config Configuration options
38366  */
38367 Roo.bootstrap.layout.Border = function(config){
38368     config = config || {};
38369     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38370     
38371     
38372     
38373     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38374         if(config[region]){
38375             config[region].region = region;
38376             this.addRegion(config[region]);
38377         }
38378     },this);
38379     
38380 };
38381
38382 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38383
38384 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38385     
38386     parent : false, // this might point to a 'nest' or a ???
38387     
38388     /**
38389      * Creates and adds a new region if it doesn't already exist.
38390      * @param {String} target The target region key (north, south, east, west or center).
38391      * @param {Object} config The regions config object
38392      * @return {BorderLayoutRegion} The new region
38393      */
38394     addRegion : function(config)
38395     {
38396         if(!this.regions[config.region]){
38397             var r = this.factory(config);
38398             this.bindRegion(r);
38399         }
38400         return this.regions[config.region];
38401     },
38402
38403     // private (kinda)
38404     bindRegion : function(r){
38405         this.regions[r.config.region] = r;
38406         
38407         r.on("visibilitychange",    this.layout, this);
38408         r.on("paneladded",          this.layout, this);
38409         r.on("panelremoved",        this.layout, this);
38410         r.on("invalidated",         this.layout, this);
38411         r.on("resized",             this.onRegionResized, this);
38412         r.on("collapsed",           this.onRegionCollapsed, this);
38413         r.on("expanded",            this.onRegionExpanded, this);
38414     },
38415
38416     /**
38417      * Performs a layout update.
38418      */
38419     layout : function()
38420     {
38421         if(this.updating) {
38422             return;
38423         }
38424         
38425         // render all the rebions if they have not been done alreayd?
38426         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38427             if(this.regions[region] && !this.regions[region].bodyEl){
38428                 this.regions[region].onRender(this.el)
38429             }
38430         },this);
38431         
38432         var size = this.getViewSize();
38433         var w = size.width;
38434         var h = size.height;
38435         var centerW = w;
38436         var centerH = h;
38437         var centerY = 0;
38438         var centerX = 0;
38439         //var x = 0, y = 0;
38440
38441         var rs = this.regions;
38442         var north = rs["north"];
38443         var south = rs["south"]; 
38444         var west = rs["west"];
38445         var east = rs["east"];
38446         var center = rs["center"];
38447         //if(this.hideOnLayout){ // not supported anymore
38448             //c.el.setStyle("display", "none");
38449         //}
38450         if(north && north.isVisible()){
38451             var b = north.getBox();
38452             var m = north.getMargins();
38453             b.width = w - (m.left+m.right);
38454             b.x = m.left;
38455             b.y = m.top;
38456             centerY = b.height + b.y + m.bottom;
38457             centerH -= centerY;
38458             north.updateBox(this.safeBox(b));
38459         }
38460         if(south && south.isVisible()){
38461             var b = south.getBox();
38462             var m = south.getMargins();
38463             b.width = w - (m.left+m.right);
38464             b.x = m.left;
38465             var totalHeight = (b.height + m.top + m.bottom);
38466             b.y = h - totalHeight + m.top;
38467             centerH -= totalHeight;
38468             south.updateBox(this.safeBox(b));
38469         }
38470         if(west && west.isVisible()){
38471             var b = west.getBox();
38472             var m = west.getMargins();
38473             b.height = centerH - (m.top+m.bottom);
38474             b.x = m.left;
38475             b.y = centerY + m.top;
38476             var totalWidth = (b.width + m.left + m.right);
38477             centerX += totalWidth;
38478             centerW -= totalWidth;
38479             west.updateBox(this.safeBox(b));
38480         }
38481         if(east && east.isVisible()){
38482             var b = east.getBox();
38483             var m = east.getMargins();
38484             b.height = centerH - (m.top+m.bottom);
38485             var totalWidth = (b.width + m.left + m.right);
38486             b.x = w - totalWidth + m.left;
38487             b.y = centerY + m.top;
38488             centerW -= totalWidth;
38489             east.updateBox(this.safeBox(b));
38490         }
38491         if(center){
38492             var m = center.getMargins();
38493             var centerBox = {
38494                 x: centerX + m.left,
38495                 y: centerY + m.top,
38496                 width: centerW - (m.left+m.right),
38497                 height: centerH - (m.top+m.bottom)
38498             };
38499             //if(this.hideOnLayout){
38500                 //center.el.setStyle("display", "block");
38501             //}
38502             center.updateBox(this.safeBox(centerBox));
38503         }
38504         this.el.repaint();
38505         this.fireEvent("layout", this);
38506     },
38507
38508     // private
38509     safeBox : function(box){
38510         box.width = Math.max(0, box.width);
38511         box.height = Math.max(0, box.height);
38512         return box;
38513     },
38514
38515     /**
38516      * Adds a ContentPanel (or subclass) to this layout.
38517      * @param {String} target The target region key (north, south, east, west or center).
38518      * @param {Roo.ContentPanel} panel The panel to add
38519      * @return {Roo.ContentPanel} The added panel
38520      */
38521     add : function(target, panel){
38522          
38523         target = target.toLowerCase();
38524         return this.regions[target].add(panel);
38525     },
38526
38527     /**
38528      * Remove a ContentPanel (or subclass) to this layout.
38529      * @param {String} target The target region key (north, south, east, west or center).
38530      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38531      * @return {Roo.ContentPanel} The removed panel
38532      */
38533     remove : function(target, panel){
38534         target = target.toLowerCase();
38535         return this.regions[target].remove(panel);
38536     },
38537
38538     /**
38539      * Searches all regions for a panel with the specified id
38540      * @param {String} panelId
38541      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38542      */
38543     findPanel : function(panelId){
38544         var rs = this.regions;
38545         for(var target in rs){
38546             if(typeof rs[target] != "function"){
38547                 var p = rs[target].getPanel(panelId);
38548                 if(p){
38549                     return p;
38550                 }
38551             }
38552         }
38553         return null;
38554     },
38555
38556     /**
38557      * Searches all regions for a panel with the specified id and activates (shows) it.
38558      * @param {String/ContentPanel} panelId The panels id or the panel itself
38559      * @return {Roo.ContentPanel} The shown panel or null
38560      */
38561     showPanel : function(panelId) {
38562       var rs = this.regions;
38563       for(var target in rs){
38564          var r = rs[target];
38565          if(typeof r != "function"){
38566             if(r.hasPanel(panelId)){
38567                return r.showPanel(panelId);
38568             }
38569          }
38570       }
38571       return null;
38572    },
38573
38574    /**
38575      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38576      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38577      */
38578    /*
38579     restoreState : function(provider){
38580         if(!provider){
38581             provider = Roo.state.Manager;
38582         }
38583         var sm = new Roo.LayoutStateManager();
38584         sm.init(this, provider);
38585     },
38586 */
38587  
38588  
38589     /**
38590      * Adds a xtype elements to the layout.
38591      * <pre><code>
38592
38593 layout.addxtype({
38594        xtype : 'ContentPanel',
38595        region: 'west',
38596        items: [ .... ]
38597    }
38598 );
38599
38600 layout.addxtype({
38601         xtype : 'NestedLayoutPanel',
38602         region: 'west',
38603         layout: {
38604            center: { },
38605            west: { }   
38606         },
38607         items : [ ... list of content panels or nested layout panels.. ]
38608    }
38609 );
38610 </code></pre>
38611      * @param {Object} cfg Xtype definition of item to add.
38612      */
38613     addxtype : function(cfg)
38614     {
38615         // basically accepts a pannel...
38616         // can accept a layout region..!?!?
38617         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38618         
38619         
38620         // theory?  children can only be panels??
38621         
38622         //if (!cfg.xtype.match(/Panel$/)) {
38623         //    return false;
38624         //}
38625         var ret = false;
38626         
38627         if (typeof(cfg.region) == 'undefined') {
38628             Roo.log("Failed to add Panel, region was not set");
38629             Roo.log(cfg);
38630             return false;
38631         }
38632         var region = cfg.region;
38633         delete cfg.region;
38634         
38635           
38636         var xitems = [];
38637         if (cfg.items) {
38638             xitems = cfg.items;
38639             delete cfg.items;
38640         }
38641         var nb = false;
38642         
38643         if ( region == 'center') {
38644             Roo.log("Center: " + cfg.title);
38645         }
38646         
38647         
38648         switch(cfg.xtype) 
38649         {
38650             case 'Content':  // ContentPanel (el, cfg)
38651             case 'Scroll':  // ContentPanel (el, cfg)
38652             case 'View': 
38653                 cfg.autoCreate = cfg.autoCreate || true;
38654                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38655                 //} else {
38656                 //    var el = this.el.createChild();
38657                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38658                 //}
38659                 
38660                 this.add(region, ret);
38661                 break;
38662             
38663             /*
38664             case 'TreePanel': // our new panel!
38665                 cfg.el = this.el.createChild();
38666                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38667                 this.add(region, ret);
38668                 break;
38669             */
38670             
38671             case 'Nest': 
38672                 // create a new Layout (which is  a Border Layout...
38673                 
38674                 var clayout = cfg.layout;
38675                 clayout.el  = this.el.createChild();
38676                 clayout.items   = clayout.items  || [];
38677                 
38678                 delete cfg.layout;
38679                 
38680                 // replace this exitems with the clayout ones..
38681                 xitems = clayout.items;
38682                  
38683                 // force background off if it's in center...
38684                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38685                     cfg.background = false;
38686                 }
38687                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38688                 
38689                 
38690                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38691                 //console.log('adding nested layout panel '  + cfg.toSource());
38692                 this.add(region, ret);
38693                 nb = {}; /// find first...
38694                 break;
38695             
38696             case 'Grid':
38697                 
38698                 // needs grid and region
38699                 
38700                 //var el = this.getRegion(region).el.createChild();
38701                 /*
38702                  *var el = this.el.createChild();
38703                 // create the grid first...
38704                 cfg.grid.container = el;
38705                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38706                 */
38707                 
38708                 if (region == 'center' && this.active ) {
38709                     cfg.background = false;
38710                 }
38711                 
38712                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38713                 
38714                 this.add(region, ret);
38715                 /*
38716                 if (cfg.background) {
38717                     // render grid on panel activation (if panel background)
38718                     ret.on('activate', function(gp) {
38719                         if (!gp.grid.rendered) {
38720                     //        gp.grid.render(el);
38721                         }
38722                     });
38723                 } else {
38724                   //  cfg.grid.render(el);
38725                 }
38726                 */
38727                 break;
38728            
38729            
38730             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38731                 // it was the old xcomponent building that caused this before.
38732                 // espeically if border is the top element in the tree.
38733                 ret = this;
38734                 break; 
38735                 
38736                     
38737                 
38738                 
38739                 
38740             default:
38741                 /*
38742                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38743                     
38744                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38745                     this.add(region, ret);
38746                 } else {
38747                 */
38748                     Roo.log(cfg);
38749                     throw "Can not add '" + cfg.xtype + "' to Border";
38750                     return null;
38751              
38752                                 
38753              
38754         }
38755         this.beginUpdate();
38756         // add children..
38757         var region = '';
38758         var abn = {};
38759         Roo.each(xitems, function(i)  {
38760             region = nb && i.region ? i.region : false;
38761             
38762             var add = ret.addxtype(i);
38763            
38764             if (region) {
38765                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38766                 if (!i.background) {
38767                     abn[region] = nb[region] ;
38768                 }
38769             }
38770             
38771         });
38772         this.endUpdate();
38773
38774         // make the last non-background panel active..
38775         //if (nb) { Roo.log(abn); }
38776         if (nb) {
38777             
38778             for(var r in abn) {
38779                 region = this.getRegion(r);
38780                 if (region) {
38781                     // tried using nb[r], but it does not work..
38782                      
38783                     region.showPanel(abn[r]);
38784                    
38785                 }
38786             }
38787         }
38788         return ret;
38789         
38790     },
38791     
38792     
38793 // private
38794     factory : function(cfg)
38795     {
38796         
38797         var validRegions = Roo.bootstrap.layout.Border.regions;
38798
38799         var target = cfg.region;
38800         cfg.mgr = this;
38801         
38802         var r = Roo.bootstrap.layout;
38803         Roo.log(target);
38804         switch(target){
38805             case "north":
38806                 return new r.North(cfg);
38807             case "south":
38808                 return new r.South(cfg);
38809             case "east":
38810                 return new r.East(cfg);
38811             case "west":
38812                 return new r.West(cfg);
38813             case "center":
38814                 return new r.Center(cfg);
38815         }
38816         throw 'Layout region "'+target+'" not supported.';
38817     }
38818     
38819     
38820 });
38821  /*
38822  * Based on:
38823  * Ext JS Library 1.1.1
38824  * Copyright(c) 2006-2007, Ext JS, LLC.
38825  *
38826  * Originally Released Under LGPL - original licence link has changed is not relivant.
38827  *
38828  * Fork - LGPL
38829  * <script type="text/javascript">
38830  */
38831  
38832 /**
38833  * @class Roo.bootstrap.layout.Basic
38834  * @extends Roo.util.Observable
38835  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38836  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38837  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38838  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38839  * @cfg {string}   region  the region that it inhabits..
38840  * @cfg {bool}   skipConfig skip config?
38841  * 
38842
38843  */
38844 Roo.bootstrap.layout.Basic = function(config){
38845     
38846     this.mgr = config.mgr;
38847     
38848     this.position = config.region;
38849     
38850     var skipConfig = config.skipConfig;
38851     
38852     this.events = {
38853         /**
38854          * @scope Roo.BasicLayoutRegion
38855          */
38856         
38857         /**
38858          * @event beforeremove
38859          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38860          * @param {Roo.LayoutRegion} this
38861          * @param {Roo.ContentPanel} panel The panel
38862          * @param {Object} e The cancel event object
38863          */
38864         "beforeremove" : true,
38865         /**
38866          * @event invalidated
38867          * Fires when the layout for this region is changed.
38868          * @param {Roo.LayoutRegion} this
38869          */
38870         "invalidated" : true,
38871         /**
38872          * @event visibilitychange
38873          * Fires when this region is shown or hidden 
38874          * @param {Roo.LayoutRegion} this
38875          * @param {Boolean} visibility true or false
38876          */
38877         "visibilitychange" : true,
38878         /**
38879          * @event paneladded
38880          * Fires when a panel is added. 
38881          * @param {Roo.LayoutRegion} this
38882          * @param {Roo.ContentPanel} panel The panel
38883          */
38884         "paneladded" : true,
38885         /**
38886          * @event panelremoved
38887          * Fires when a panel is removed. 
38888          * @param {Roo.LayoutRegion} this
38889          * @param {Roo.ContentPanel} panel The panel
38890          */
38891         "panelremoved" : true,
38892         /**
38893          * @event beforecollapse
38894          * Fires when this region before collapse.
38895          * @param {Roo.LayoutRegion} this
38896          */
38897         "beforecollapse" : true,
38898         /**
38899          * @event collapsed
38900          * Fires when this region is collapsed.
38901          * @param {Roo.LayoutRegion} this
38902          */
38903         "collapsed" : true,
38904         /**
38905          * @event expanded
38906          * Fires when this region is expanded.
38907          * @param {Roo.LayoutRegion} this
38908          */
38909         "expanded" : true,
38910         /**
38911          * @event slideshow
38912          * Fires when this region is slid into view.
38913          * @param {Roo.LayoutRegion} this
38914          */
38915         "slideshow" : true,
38916         /**
38917          * @event slidehide
38918          * Fires when this region slides out of view. 
38919          * @param {Roo.LayoutRegion} this
38920          */
38921         "slidehide" : true,
38922         /**
38923          * @event panelactivated
38924          * Fires when a panel is activated. 
38925          * @param {Roo.LayoutRegion} this
38926          * @param {Roo.ContentPanel} panel The activated panel
38927          */
38928         "panelactivated" : true,
38929         /**
38930          * @event resized
38931          * Fires when the user resizes this region. 
38932          * @param {Roo.LayoutRegion} this
38933          * @param {Number} newSize The new size (width for east/west, height for north/south)
38934          */
38935         "resized" : true
38936     };
38937     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38938     this.panels = new Roo.util.MixedCollection();
38939     this.panels.getKey = this.getPanelId.createDelegate(this);
38940     this.box = null;
38941     this.activePanel = null;
38942     // ensure listeners are added...
38943     
38944     if (config.listeners || config.events) {
38945         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38946             listeners : config.listeners || {},
38947             events : config.events || {}
38948         });
38949     }
38950     
38951     if(skipConfig !== true){
38952         this.applyConfig(config);
38953     }
38954 };
38955
38956 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38957 {
38958     getPanelId : function(p){
38959         return p.getId();
38960     },
38961     
38962     applyConfig : function(config){
38963         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38964         this.config = config;
38965         
38966     },
38967     
38968     /**
38969      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38970      * the width, for horizontal (north, south) the height.
38971      * @param {Number} newSize The new width or height
38972      */
38973     resizeTo : function(newSize){
38974         var el = this.el ? this.el :
38975                  (this.activePanel ? this.activePanel.getEl() : null);
38976         if(el){
38977             switch(this.position){
38978                 case "east":
38979                 case "west":
38980                     el.setWidth(newSize);
38981                     this.fireEvent("resized", this, newSize);
38982                 break;
38983                 case "north":
38984                 case "south":
38985                     el.setHeight(newSize);
38986                     this.fireEvent("resized", this, newSize);
38987                 break;                
38988             }
38989         }
38990     },
38991     
38992     getBox : function(){
38993         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38994     },
38995     
38996     getMargins : function(){
38997         return this.margins;
38998     },
38999     
39000     updateBox : function(box){
39001         this.box = box;
39002         var el = this.activePanel.getEl();
39003         el.dom.style.left = box.x + "px";
39004         el.dom.style.top = box.y + "px";
39005         this.activePanel.setSize(box.width, box.height);
39006     },
39007     
39008     /**
39009      * Returns the container element for this region.
39010      * @return {Roo.Element}
39011      */
39012     getEl : function(){
39013         return this.activePanel;
39014     },
39015     
39016     /**
39017      * Returns true if this region is currently visible.
39018      * @return {Boolean}
39019      */
39020     isVisible : function(){
39021         return this.activePanel ? true : false;
39022     },
39023     
39024     setActivePanel : function(panel){
39025         panel = this.getPanel(panel);
39026         if(this.activePanel && this.activePanel != panel){
39027             this.activePanel.setActiveState(false);
39028             this.activePanel.getEl().setLeftTop(-10000,-10000);
39029         }
39030         this.activePanel = panel;
39031         panel.setActiveState(true);
39032         if(this.box){
39033             panel.setSize(this.box.width, this.box.height);
39034         }
39035         this.fireEvent("panelactivated", this, panel);
39036         this.fireEvent("invalidated");
39037     },
39038     
39039     /**
39040      * Show the specified panel.
39041      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39042      * @return {Roo.ContentPanel} The shown panel or null
39043      */
39044     showPanel : function(panel){
39045         panel = this.getPanel(panel);
39046         if(panel){
39047             this.setActivePanel(panel);
39048         }
39049         return panel;
39050     },
39051     
39052     /**
39053      * Get the active panel for this region.
39054      * @return {Roo.ContentPanel} The active panel or null
39055      */
39056     getActivePanel : function(){
39057         return this.activePanel;
39058     },
39059     
39060     /**
39061      * Add the passed ContentPanel(s)
39062      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39063      * @return {Roo.ContentPanel} The panel added (if only one was added)
39064      */
39065     add : function(panel){
39066         if(arguments.length > 1){
39067             for(var i = 0, len = arguments.length; i < len; i++) {
39068                 this.add(arguments[i]);
39069             }
39070             return null;
39071         }
39072         if(this.hasPanel(panel)){
39073             this.showPanel(panel);
39074             return panel;
39075         }
39076         var el = panel.getEl();
39077         if(el.dom.parentNode != this.mgr.el.dom){
39078             this.mgr.el.dom.appendChild(el.dom);
39079         }
39080         if(panel.setRegion){
39081             panel.setRegion(this);
39082         }
39083         this.panels.add(panel);
39084         el.setStyle("position", "absolute");
39085         if(!panel.background){
39086             this.setActivePanel(panel);
39087             if(this.config.initialSize && this.panels.getCount()==1){
39088                 this.resizeTo(this.config.initialSize);
39089             }
39090         }
39091         this.fireEvent("paneladded", this, panel);
39092         return panel;
39093     },
39094     
39095     /**
39096      * Returns true if the panel is in this region.
39097      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39098      * @return {Boolean}
39099      */
39100     hasPanel : function(panel){
39101         if(typeof panel == "object"){ // must be panel obj
39102             panel = panel.getId();
39103         }
39104         return this.getPanel(panel) ? true : false;
39105     },
39106     
39107     /**
39108      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39109      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39110      * @param {Boolean} preservePanel Overrides the config preservePanel option
39111      * @return {Roo.ContentPanel} The panel that was removed
39112      */
39113     remove : function(panel, preservePanel){
39114         panel = this.getPanel(panel);
39115         if(!panel){
39116             return null;
39117         }
39118         var e = {};
39119         this.fireEvent("beforeremove", this, panel, e);
39120         if(e.cancel === true){
39121             return null;
39122         }
39123         var panelId = panel.getId();
39124         this.panels.removeKey(panelId);
39125         return panel;
39126     },
39127     
39128     /**
39129      * Returns the panel specified or null if it's not in this region.
39130      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39131      * @return {Roo.ContentPanel}
39132      */
39133     getPanel : function(id){
39134         if(typeof id == "object"){ // must be panel obj
39135             return id;
39136         }
39137         return this.panels.get(id);
39138     },
39139     
39140     /**
39141      * Returns this regions position (north/south/east/west/center).
39142      * @return {String} 
39143      */
39144     getPosition: function(){
39145         return this.position;    
39146     }
39147 });/*
39148  * Based on:
39149  * Ext JS Library 1.1.1
39150  * Copyright(c) 2006-2007, Ext JS, LLC.
39151  *
39152  * Originally Released Under LGPL - original licence link has changed is not relivant.
39153  *
39154  * Fork - LGPL
39155  * <script type="text/javascript">
39156  */
39157  
39158 /**
39159  * @class Roo.bootstrap.layout.Region
39160  * @extends Roo.bootstrap.layout.Basic
39161  * This class represents a region in a layout manager.
39162  
39163  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39164  * @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})
39165  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39166  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39167  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39168  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39169  * @cfg {String}    title           The title for the region (overrides panel titles)
39170  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39171  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39172  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39173  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39174  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39175  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39176  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39177  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39178  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39179  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39180
39181  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39182  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39183  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39184  * @cfg {Number}    width           For East/West panels
39185  * @cfg {Number}    height          For North/South panels
39186  * @cfg {Boolean}   split           To show the splitter
39187  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39188  * 
39189  * @cfg {string}   cls             Extra CSS classes to add to region
39190  * 
39191  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39192  * @cfg {string}   region  the region that it inhabits..
39193  *
39194
39195  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39196  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39197
39198  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39199  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39200  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39201  */
39202 Roo.bootstrap.layout.Region = function(config)
39203 {
39204     this.applyConfig(config);
39205
39206     var mgr = config.mgr;
39207     var pos = config.region;
39208     config.skipConfig = true;
39209     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39210     
39211     if (mgr.el) {
39212         this.onRender(mgr.el);   
39213     }
39214      
39215     this.visible = true;
39216     this.collapsed = false;
39217     this.unrendered_panels = [];
39218 };
39219
39220 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39221
39222     position: '', // set by wrapper (eg. north/south etc..)
39223     unrendered_panels : null,  // unrendered panels.
39224     
39225     tabPosition : false,
39226     
39227     mgr: false, // points to 'Border'
39228     
39229     
39230     createBody : function(){
39231         /** This region's body element 
39232         * @type Roo.Element */
39233         this.bodyEl = this.el.createChild({
39234                 tag: "div",
39235                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39236         });
39237     },
39238
39239     onRender: function(ctr, pos)
39240     {
39241         var dh = Roo.DomHelper;
39242         /** This region's container element 
39243         * @type Roo.Element */
39244         this.el = dh.append(ctr.dom, {
39245                 tag: "div",
39246                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39247             }, true);
39248         /** This region's title element 
39249         * @type Roo.Element */
39250     
39251         this.titleEl = dh.append(this.el.dom,  {
39252                 tag: "div",
39253                 unselectable: "on",
39254                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39255                 children:[
39256                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39257                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39258                 ]
39259             }, true);
39260         
39261         this.titleEl.enableDisplayMode();
39262         /** This region's title text element 
39263         * @type HTMLElement */
39264         this.titleTextEl = this.titleEl.dom.firstChild;
39265         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39266         /*
39267         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39268         this.closeBtn.enableDisplayMode();
39269         this.closeBtn.on("click", this.closeClicked, this);
39270         this.closeBtn.hide();
39271     */
39272         this.createBody(this.config);
39273         if(this.config.hideWhenEmpty){
39274             this.hide();
39275             this.on("paneladded", this.validateVisibility, this);
39276             this.on("panelremoved", this.validateVisibility, this);
39277         }
39278         if(this.autoScroll){
39279             this.bodyEl.setStyle("overflow", "auto");
39280         }else{
39281             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39282         }
39283         //if(c.titlebar !== false){
39284             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39285                 this.titleEl.hide();
39286             }else{
39287                 this.titleEl.show();
39288                 if(this.config.title){
39289                     this.titleTextEl.innerHTML = this.config.title;
39290                 }
39291             }
39292         //}
39293         if(this.config.collapsed){
39294             this.collapse(true);
39295         }
39296         if(this.config.hidden){
39297             this.hide();
39298         }
39299         
39300         if (this.unrendered_panels && this.unrendered_panels.length) {
39301             for (var i =0;i< this.unrendered_panels.length; i++) {
39302                 this.add(this.unrendered_panels[i]);
39303             }
39304             this.unrendered_panels = null;
39305             
39306         }
39307         
39308     },
39309     
39310     applyConfig : function(c)
39311     {
39312         /*
39313          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39314             var dh = Roo.DomHelper;
39315             if(c.titlebar !== false){
39316                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39317                 this.collapseBtn.on("click", this.collapse, this);
39318                 this.collapseBtn.enableDisplayMode();
39319                 /*
39320                 if(c.showPin === true || this.showPin){
39321                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39322                     this.stickBtn.enableDisplayMode();
39323                     this.stickBtn.on("click", this.expand, this);
39324                     this.stickBtn.hide();
39325                 }
39326                 
39327             }
39328             */
39329             /** This region's collapsed element
39330             * @type Roo.Element */
39331             /*
39332              *
39333             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39334                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39335             ]}, true);
39336             
39337             if(c.floatable !== false){
39338                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39339                this.collapsedEl.on("click", this.collapseClick, this);
39340             }
39341
39342             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39343                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39344                    id: "message", unselectable: "on", style:{"float":"left"}});
39345                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39346              }
39347             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39348             this.expandBtn.on("click", this.expand, this);
39349             
39350         }
39351         
39352         if(this.collapseBtn){
39353             this.collapseBtn.setVisible(c.collapsible == true);
39354         }
39355         
39356         this.cmargins = c.cmargins || this.cmargins ||
39357                          (this.position == "west" || this.position == "east" ?
39358                              {top: 0, left: 2, right:2, bottom: 0} :
39359                              {top: 2, left: 0, right:0, bottom: 2});
39360         */
39361         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39362         
39363         
39364         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39365         
39366         this.autoScroll = c.autoScroll || false;
39367         
39368         
39369        
39370         
39371         this.duration = c.duration || .30;
39372         this.slideDuration = c.slideDuration || .45;
39373         this.config = c;
39374        
39375     },
39376     /**
39377      * Returns true if this region is currently visible.
39378      * @return {Boolean}
39379      */
39380     isVisible : function(){
39381         return this.visible;
39382     },
39383
39384     /**
39385      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39386      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39387      */
39388     //setCollapsedTitle : function(title){
39389     //    title = title || "&#160;";
39390      //   if(this.collapsedTitleTextEl){
39391       //      this.collapsedTitleTextEl.innerHTML = title;
39392        // }
39393     //},
39394
39395     getBox : function(){
39396         var b;
39397       //  if(!this.collapsed){
39398             b = this.el.getBox(false, true);
39399        // }else{
39400           //  b = this.collapsedEl.getBox(false, true);
39401         //}
39402         return b;
39403     },
39404
39405     getMargins : function(){
39406         return this.margins;
39407         //return this.collapsed ? this.cmargins : this.margins;
39408     },
39409 /*
39410     highlight : function(){
39411         this.el.addClass("x-layout-panel-dragover");
39412     },
39413
39414     unhighlight : function(){
39415         this.el.removeClass("x-layout-panel-dragover");
39416     },
39417 */
39418     updateBox : function(box)
39419     {
39420         if (!this.bodyEl) {
39421             return; // not rendered yet..
39422         }
39423         
39424         this.box = box;
39425         if(!this.collapsed){
39426             this.el.dom.style.left = box.x + "px";
39427             this.el.dom.style.top = box.y + "px";
39428             this.updateBody(box.width, box.height);
39429         }else{
39430             this.collapsedEl.dom.style.left = box.x + "px";
39431             this.collapsedEl.dom.style.top = box.y + "px";
39432             this.collapsedEl.setSize(box.width, box.height);
39433         }
39434         if(this.tabs){
39435             this.tabs.autoSizeTabs();
39436         }
39437     },
39438
39439     updateBody : function(w, h)
39440     {
39441         if(w !== null){
39442             this.el.setWidth(w);
39443             w -= this.el.getBorderWidth("rl");
39444             if(this.config.adjustments){
39445                 w += this.config.adjustments[0];
39446             }
39447         }
39448         if(h !== null && h > 0){
39449             this.el.setHeight(h);
39450             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39451             h -= this.el.getBorderWidth("tb");
39452             if(this.config.adjustments){
39453                 h += this.config.adjustments[1];
39454             }
39455             this.bodyEl.setHeight(h);
39456             if(this.tabs){
39457                 h = this.tabs.syncHeight(h);
39458             }
39459         }
39460         if(this.panelSize){
39461             w = w !== null ? w : this.panelSize.width;
39462             h = h !== null ? h : this.panelSize.height;
39463         }
39464         if(this.activePanel){
39465             var el = this.activePanel.getEl();
39466             w = w !== null ? w : el.getWidth();
39467             h = h !== null ? h : el.getHeight();
39468             this.panelSize = {width: w, height: h};
39469             this.activePanel.setSize(w, h);
39470         }
39471         if(Roo.isIE && this.tabs){
39472             this.tabs.el.repaint();
39473         }
39474     },
39475
39476     /**
39477      * Returns the container element for this region.
39478      * @return {Roo.Element}
39479      */
39480     getEl : function(){
39481         return this.el;
39482     },
39483
39484     /**
39485      * Hides this region.
39486      */
39487     hide : function(){
39488         //if(!this.collapsed){
39489             this.el.dom.style.left = "-2000px";
39490             this.el.hide();
39491         //}else{
39492          //   this.collapsedEl.dom.style.left = "-2000px";
39493          //   this.collapsedEl.hide();
39494        // }
39495         this.visible = false;
39496         this.fireEvent("visibilitychange", this, false);
39497     },
39498
39499     /**
39500      * Shows this region if it was previously hidden.
39501      */
39502     show : function(){
39503         //if(!this.collapsed){
39504             this.el.show();
39505         //}else{
39506         //    this.collapsedEl.show();
39507        // }
39508         this.visible = true;
39509         this.fireEvent("visibilitychange", this, true);
39510     },
39511 /*
39512     closeClicked : function(){
39513         if(this.activePanel){
39514             this.remove(this.activePanel);
39515         }
39516     },
39517
39518     collapseClick : function(e){
39519         if(this.isSlid){
39520            e.stopPropagation();
39521            this.slideIn();
39522         }else{
39523            e.stopPropagation();
39524            this.slideOut();
39525         }
39526     },
39527 */
39528     /**
39529      * Collapses this region.
39530      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39531      */
39532     /*
39533     collapse : function(skipAnim, skipCheck = false){
39534         if(this.collapsed) {
39535             return;
39536         }
39537         
39538         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39539             
39540             this.collapsed = true;
39541             if(this.split){
39542                 this.split.el.hide();
39543             }
39544             if(this.config.animate && skipAnim !== true){
39545                 this.fireEvent("invalidated", this);
39546                 this.animateCollapse();
39547             }else{
39548                 this.el.setLocation(-20000,-20000);
39549                 this.el.hide();
39550                 this.collapsedEl.show();
39551                 this.fireEvent("collapsed", this);
39552                 this.fireEvent("invalidated", this);
39553             }
39554         }
39555         
39556     },
39557 */
39558     animateCollapse : function(){
39559         // overridden
39560     },
39561
39562     /**
39563      * Expands this region if it was previously collapsed.
39564      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39565      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39566      */
39567     /*
39568     expand : function(e, skipAnim){
39569         if(e) {
39570             e.stopPropagation();
39571         }
39572         if(!this.collapsed || this.el.hasActiveFx()) {
39573             return;
39574         }
39575         if(this.isSlid){
39576             this.afterSlideIn();
39577             skipAnim = true;
39578         }
39579         this.collapsed = false;
39580         if(this.config.animate && skipAnim !== true){
39581             this.animateExpand();
39582         }else{
39583             this.el.show();
39584             if(this.split){
39585                 this.split.el.show();
39586             }
39587             this.collapsedEl.setLocation(-2000,-2000);
39588             this.collapsedEl.hide();
39589             this.fireEvent("invalidated", this);
39590             this.fireEvent("expanded", this);
39591         }
39592     },
39593 */
39594     animateExpand : function(){
39595         // overridden
39596     },
39597
39598     initTabs : function()
39599     {
39600         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39601         
39602         var ts = new Roo.bootstrap.panel.Tabs({
39603             el: this.bodyEl.dom,
39604             region : this,
39605             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39606             disableTooltips: this.config.disableTabTips,
39607             toolbar : this.config.toolbar
39608         });
39609         
39610         if(this.config.hideTabs){
39611             ts.stripWrap.setDisplayed(false);
39612         }
39613         this.tabs = ts;
39614         ts.resizeTabs = this.config.resizeTabs === true;
39615         ts.minTabWidth = this.config.minTabWidth || 40;
39616         ts.maxTabWidth = this.config.maxTabWidth || 250;
39617         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39618         ts.monitorResize = false;
39619         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39620         ts.bodyEl.addClass('roo-layout-tabs-body');
39621         this.panels.each(this.initPanelAsTab, this);
39622     },
39623
39624     initPanelAsTab : function(panel){
39625         var ti = this.tabs.addTab(
39626             panel.getEl().id,
39627             panel.getTitle(),
39628             null,
39629             this.config.closeOnTab && panel.isClosable(),
39630             panel.tpl
39631         );
39632         if(panel.tabTip !== undefined){
39633             ti.setTooltip(panel.tabTip);
39634         }
39635         ti.on("activate", function(){
39636               this.setActivePanel(panel);
39637         }, this);
39638         
39639         if(this.config.closeOnTab){
39640             ti.on("beforeclose", function(t, e){
39641                 e.cancel = true;
39642                 this.remove(panel);
39643             }, this);
39644         }
39645         
39646         panel.tabItem = ti;
39647         
39648         return ti;
39649     },
39650
39651     updatePanelTitle : function(panel, title)
39652     {
39653         if(this.activePanel == panel){
39654             this.updateTitle(title);
39655         }
39656         if(this.tabs){
39657             var ti = this.tabs.getTab(panel.getEl().id);
39658             ti.setText(title);
39659             if(panel.tabTip !== undefined){
39660                 ti.setTooltip(panel.tabTip);
39661             }
39662         }
39663     },
39664
39665     updateTitle : function(title){
39666         if(this.titleTextEl && !this.config.title){
39667             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39668         }
39669     },
39670
39671     setActivePanel : function(panel)
39672     {
39673         panel = this.getPanel(panel);
39674         if(this.activePanel && this.activePanel != panel){
39675             if(this.activePanel.setActiveState(false) === false){
39676                 return;
39677             }
39678         }
39679         this.activePanel = panel;
39680         panel.setActiveState(true);
39681         if(this.panelSize){
39682             panel.setSize(this.panelSize.width, this.panelSize.height);
39683         }
39684         if(this.closeBtn){
39685             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39686         }
39687         this.updateTitle(panel.getTitle());
39688         if(this.tabs){
39689             this.fireEvent("invalidated", this);
39690         }
39691         this.fireEvent("panelactivated", this, panel);
39692     },
39693
39694     /**
39695      * Shows the specified panel.
39696      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39697      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39698      */
39699     showPanel : function(panel)
39700     {
39701         panel = this.getPanel(panel);
39702         if(panel){
39703             if(this.tabs){
39704                 var tab = this.tabs.getTab(panel.getEl().id);
39705                 if(tab.isHidden()){
39706                     this.tabs.unhideTab(tab.id);
39707                 }
39708                 tab.activate();
39709             }else{
39710                 this.setActivePanel(panel);
39711             }
39712         }
39713         return panel;
39714     },
39715
39716     /**
39717      * Get the active panel for this region.
39718      * @return {Roo.ContentPanel} The active panel or null
39719      */
39720     getActivePanel : function(){
39721         return this.activePanel;
39722     },
39723
39724     validateVisibility : function(){
39725         if(this.panels.getCount() < 1){
39726             this.updateTitle("&#160;");
39727             this.closeBtn.hide();
39728             this.hide();
39729         }else{
39730             if(!this.isVisible()){
39731                 this.show();
39732             }
39733         }
39734     },
39735
39736     /**
39737      * Adds the passed ContentPanel(s) to this region.
39738      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39739      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39740      */
39741     add : function(panel)
39742     {
39743         if(arguments.length > 1){
39744             for(var i = 0, len = arguments.length; i < len; i++) {
39745                 this.add(arguments[i]);
39746             }
39747             return null;
39748         }
39749         
39750         // if we have not been rendered yet, then we can not really do much of this..
39751         if (!this.bodyEl) {
39752             this.unrendered_panels.push(panel);
39753             return panel;
39754         }
39755         
39756         
39757         
39758         
39759         if(this.hasPanel(panel)){
39760             this.showPanel(panel);
39761             return panel;
39762         }
39763         panel.setRegion(this);
39764         this.panels.add(panel);
39765        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39766             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39767             // and hide them... ???
39768             this.bodyEl.dom.appendChild(panel.getEl().dom);
39769             if(panel.background !== true){
39770                 this.setActivePanel(panel);
39771             }
39772             this.fireEvent("paneladded", this, panel);
39773             return panel;
39774         }
39775         */
39776         if(!this.tabs){
39777             this.initTabs();
39778         }else{
39779             this.initPanelAsTab(panel);
39780         }
39781         
39782         
39783         if(panel.background !== true){
39784             this.tabs.activate(panel.getEl().id);
39785         }
39786         this.fireEvent("paneladded", this, panel);
39787         return panel;
39788     },
39789
39790     /**
39791      * Hides the tab for the specified panel.
39792      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39793      */
39794     hidePanel : function(panel){
39795         if(this.tabs && (panel = this.getPanel(panel))){
39796             this.tabs.hideTab(panel.getEl().id);
39797         }
39798     },
39799
39800     /**
39801      * Unhides the tab for a previously hidden panel.
39802      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39803      */
39804     unhidePanel : function(panel){
39805         if(this.tabs && (panel = this.getPanel(panel))){
39806             this.tabs.unhideTab(panel.getEl().id);
39807         }
39808     },
39809
39810     clearPanels : function(){
39811         while(this.panels.getCount() > 0){
39812              this.remove(this.panels.first());
39813         }
39814     },
39815
39816     /**
39817      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39818      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39819      * @param {Boolean} preservePanel Overrides the config preservePanel option
39820      * @return {Roo.ContentPanel} The panel that was removed
39821      */
39822     remove : function(panel, preservePanel)
39823     {
39824         panel = this.getPanel(panel);
39825         if(!panel){
39826             return null;
39827         }
39828         var e = {};
39829         this.fireEvent("beforeremove", this, panel, e);
39830         if(e.cancel === true){
39831             return null;
39832         }
39833         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39834         var panelId = panel.getId();
39835         this.panels.removeKey(panelId);
39836         if(preservePanel){
39837             document.body.appendChild(panel.getEl().dom);
39838         }
39839         if(this.tabs){
39840             this.tabs.removeTab(panel.getEl().id);
39841         }else if (!preservePanel){
39842             this.bodyEl.dom.removeChild(panel.getEl().dom);
39843         }
39844         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39845             var p = this.panels.first();
39846             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39847             tempEl.appendChild(p.getEl().dom);
39848             this.bodyEl.update("");
39849             this.bodyEl.dom.appendChild(p.getEl().dom);
39850             tempEl = null;
39851             this.updateTitle(p.getTitle());
39852             this.tabs = null;
39853             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39854             this.setActivePanel(p);
39855         }
39856         panel.setRegion(null);
39857         if(this.activePanel == panel){
39858             this.activePanel = null;
39859         }
39860         if(this.config.autoDestroy !== false && preservePanel !== true){
39861             try{panel.destroy();}catch(e){}
39862         }
39863         this.fireEvent("panelremoved", this, panel);
39864         return panel;
39865     },
39866
39867     /**
39868      * Returns the TabPanel component used by this region
39869      * @return {Roo.TabPanel}
39870      */
39871     getTabs : function(){
39872         return this.tabs;
39873     },
39874
39875     createTool : function(parentEl, className){
39876         var btn = Roo.DomHelper.append(parentEl, {
39877             tag: "div",
39878             cls: "x-layout-tools-button",
39879             children: [ {
39880                 tag: "div",
39881                 cls: "roo-layout-tools-button-inner " + className,
39882                 html: "&#160;"
39883             }]
39884         }, true);
39885         btn.addClassOnOver("roo-layout-tools-button-over");
39886         return btn;
39887     }
39888 });/*
39889  * Based on:
39890  * Ext JS Library 1.1.1
39891  * Copyright(c) 2006-2007, Ext JS, LLC.
39892  *
39893  * Originally Released Under LGPL - original licence link has changed is not relivant.
39894  *
39895  * Fork - LGPL
39896  * <script type="text/javascript">
39897  */
39898  
39899
39900
39901 /**
39902  * @class Roo.SplitLayoutRegion
39903  * @extends Roo.LayoutRegion
39904  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39905  */
39906 Roo.bootstrap.layout.Split = function(config){
39907     this.cursor = config.cursor;
39908     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39909 };
39910
39911 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39912 {
39913     splitTip : "Drag to resize.",
39914     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39915     useSplitTips : false,
39916
39917     applyConfig : function(config){
39918         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39919     },
39920     
39921     onRender : function(ctr,pos) {
39922         
39923         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39924         if(!this.config.split){
39925             return;
39926         }
39927         if(!this.split){
39928             
39929             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39930                             tag: "div",
39931                             id: this.el.id + "-split",
39932                             cls: "roo-layout-split roo-layout-split-"+this.position,
39933                             html: "&#160;"
39934             });
39935             /** The SplitBar for this region 
39936             * @type Roo.SplitBar */
39937             // does not exist yet...
39938             Roo.log([this.position, this.orientation]);
39939             
39940             this.split = new Roo.bootstrap.SplitBar({
39941                 dragElement : splitEl,
39942                 resizingElement: this.el,
39943                 orientation : this.orientation
39944             });
39945             
39946             this.split.on("moved", this.onSplitMove, this);
39947             this.split.useShim = this.config.useShim === true;
39948             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39949             if(this.useSplitTips){
39950                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39951             }
39952             //if(config.collapsible){
39953             //    this.split.el.on("dblclick", this.collapse,  this);
39954             //}
39955         }
39956         if(typeof this.config.minSize != "undefined"){
39957             this.split.minSize = this.config.minSize;
39958         }
39959         if(typeof this.config.maxSize != "undefined"){
39960             this.split.maxSize = this.config.maxSize;
39961         }
39962         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39963             this.hideSplitter();
39964         }
39965         
39966     },
39967
39968     getHMaxSize : function(){
39969          var cmax = this.config.maxSize || 10000;
39970          var center = this.mgr.getRegion("center");
39971          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39972     },
39973
39974     getVMaxSize : function(){
39975          var cmax = this.config.maxSize || 10000;
39976          var center = this.mgr.getRegion("center");
39977          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39978     },
39979
39980     onSplitMove : function(split, newSize){
39981         this.fireEvent("resized", this, newSize);
39982     },
39983     
39984     /** 
39985      * Returns the {@link Roo.SplitBar} for this region.
39986      * @return {Roo.SplitBar}
39987      */
39988     getSplitBar : function(){
39989         return this.split;
39990     },
39991     
39992     hide : function(){
39993         this.hideSplitter();
39994         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39995     },
39996
39997     hideSplitter : function(){
39998         if(this.split){
39999             this.split.el.setLocation(-2000,-2000);
40000             this.split.el.hide();
40001         }
40002     },
40003
40004     show : function(){
40005         if(this.split){
40006             this.split.el.show();
40007         }
40008         Roo.bootstrap.layout.Split.superclass.show.call(this);
40009     },
40010     
40011     beforeSlide: function(){
40012         if(Roo.isGecko){// firefox overflow auto bug workaround
40013             this.bodyEl.clip();
40014             if(this.tabs) {
40015                 this.tabs.bodyEl.clip();
40016             }
40017             if(this.activePanel){
40018                 this.activePanel.getEl().clip();
40019                 
40020                 if(this.activePanel.beforeSlide){
40021                     this.activePanel.beforeSlide();
40022                 }
40023             }
40024         }
40025     },
40026     
40027     afterSlide : function(){
40028         if(Roo.isGecko){// firefox overflow auto bug workaround
40029             this.bodyEl.unclip();
40030             if(this.tabs) {
40031                 this.tabs.bodyEl.unclip();
40032             }
40033             if(this.activePanel){
40034                 this.activePanel.getEl().unclip();
40035                 if(this.activePanel.afterSlide){
40036                     this.activePanel.afterSlide();
40037                 }
40038             }
40039         }
40040     },
40041
40042     initAutoHide : function(){
40043         if(this.autoHide !== false){
40044             if(!this.autoHideHd){
40045                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40046                 this.autoHideHd = {
40047                     "mouseout": function(e){
40048                         if(!e.within(this.el, true)){
40049                             st.delay(500);
40050                         }
40051                     },
40052                     "mouseover" : function(e){
40053                         st.cancel();
40054                     },
40055                     scope : this
40056                 };
40057             }
40058             this.el.on(this.autoHideHd);
40059         }
40060     },
40061
40062     clearAutoHide : function(){
40063         if(this.autoHide !== false){
40064             this.el.un("mouseout", this.autoHideHd.mouseout);
40065             this.el.un("mouseover", this.autoHideHd.mouseover);
40066         }
40067     },
40068
40069     clearMonitor : function(){
40070         Roo.get(document).un("click", this.slideInIf, this);
40071     },
40072
40073     // these names are backwards but not changed for compat
40074     slideOut : function(){
40075         if(this.isSlid || this.el.hasActiveFx()){
40076             return;
40077         }
40078         this.isSlid = true;
40079         if(this.collapseBtn){
40080             this.collapseBtn.hide();
40081         }
40082         this.closeBtnState = this.closeBtn.getStyle('display');
40083         this.closeBtn.hide();
40084         if(this.stickBtn){
40085             this.stickBtn.show();
40086         }
40087         this.el.show();
40088         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40089         this.beforeSlide();
40090         this.el.setStyle("z-index", 10001);
40091         this.el.slideIn(this.getSlideAnchor(), {
40092             callback: function(){
40093                 this.afterSlide();
40094                 this.initAutoHide();
40095                 Roo.get(document).on("click", this.slideInIf, this);
40096                 this.fireEvent("slideshow", this);
40097             },
40098             scope: this,
40099             block: true
40100         });
40101     },
40102
40103     afterSlideIn : function(){
40104         this.clearAutoHide();
40105         this.isSlid = false;
40106         this.clearMonitor();
40107         this.el.setStyle("z-index", "");
40108         if(this.collapseBtn){
40109             this.collapseBtn.show();
40110         }
40111         this.closeBtn.setStyle('display', this.closeBtnState);
40112         if(this.stickBtn){
40113             this.stickBtn.hide();
40114         }
40115         this.fireEvent("slidehide", this);
40116     },
40117
40118     slideIn : function(cb){
40119         if(!this.isSlid || this.el.hasActiveFx()){
40120             Roo.callback(cb);
40121             return;
40122         }
40123         this.isSlid = false;
40124         this.beforeSlide();
40125         this.el.slideOut(this.getSlideAnchor(), {
40126             callback: function(){
40127                 this.el.setLeftTop(-10000, -10000);
40128                 this.afterSlide();
40129                 this.afterSlideIn();
40130                 Roo.callback(cb);
40131             },
40132             scope: this,
40133             block: true
40134         });
40135     },
40136     
40137     slideInIf : function(e){
40138         if(!e.within(this.el)){
40139             this.slideIn();
40140         }
40141     },
40142
40143     animateCollapse : function(){
40144         this.beforeSlide();
40145         this.el.setStyle("z-index", 20000);
40146         var anchor = this.getSlideAnchor();
40147         this.el.slideOut(anchor, {
40148             callback : function(){
40149                 this.el.setStyle("z-index", "");
40150                 this.collapsedEl.slideIn(anchor, {duration:.3});
40151                 this.afterSlide();
40152                 this.el.setLocation(-10000,-10000);
40153                 this.el.hide();
40154                 this.fireEvent("collapsed", this);
40155             },
40156             scope: this,
40157             block: true
40158         });
40159     },
40160
40161     animateExpand : function(){
40162         this.beforeSlide();
40163         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40164         this.el.setStyle("z-index", 20000);
40165         this.collapsedEl.hide({
40166             duration:.1
40167         });
40168         this.el.slideIn(this.getSlideAnchor(), {
40169             callback : function(){
40170                 this.el.setStyle("z-index", "");
40171                 this.afterSlide();
40172                 if(this.split){
40173                     this.split.el.show();
40174                 }
40175                 this.fireEvent("invalidated", this);
40176                 this.fireEvent("expanded", this);
40177             },
40178             scope: this,
40179             block: true
40180         });
40181     },
40182
40183     anchors : {
40184         "west" : "left",
40185         "east" : "right",
40186         "north" : "top",
40187         "south" : "bottom"
40188     },
40189
40190     sanchors : {
40191         "west" : "l",
40192         "east" : "r",
40193         "north" : "t",
40194         "south" : "b"
40195     },
40196
40197     canchors : {
40198         "west" : "tl-tr",
40199         "east" : "tr-tl",
40200         "north" : "tl-bl",
40201         "south" : "bl-tl"
40202     },
40203
40204     getAnchor : function(){
40205         return this.anchors[this.position];
40206     },
40207
40208     getCollapseAnchor : function(){
40209         return this.canchors[this.position];
40210     },
40211
40212     getSlideAnchor : function(){
40213         return this.sanchors[this.position];
40214     },
40215
40216     getAlignAdj : function(){
40217         var cm = this.cmargins;
40218         switch(this.position){
40219             case "west":
40220                 return [0, 0];
40221             break;
40222             case "east":
40223                 return [0, 0];
40224             break;
40225             case "north":
40226                 return [0, 0];
40227             break;
40228             case "south":
40229                 return [0, 0];
40230             break;
40231         }
40232     },
40233
40234     getExpandAdj : function(){
40235         var c = this.collapsedEl, cm = this.cmargins;
40236         switch(this.position){
40237             case "west":
40238                 return [-(cm.right+c.getWidth()+cm.left), 0];
40239             break;
40240             case "east":
40241                 return [cm.right+c.getWidth()+cm.left, 0];
40242             break;
40243             case "north":
40244                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40245             break;
40246             case "south":
40247                 return [0, cm.top+cm.bottom+c.getHeight()];
40248             break;
40249         }
40250     }
40251 });/*
40252  * Based on:
40253  * Ext JS Library 1.1.1
40254  * Copyright(c) 2006-2007, Ext JS, LLC.
40255  *
40256  * Originally Released Under LGPL - original licence link has changed is not relivant.
40257  *
40258  * Fork - LGPL
40259  * <script type="text/javascript">
40260  */
40261 /*
40262  * These classes are private internal classes
40263  */
40264 Roo.bootstrap.layout.Center = function(config){
40265     config.region = "center";
40266     Roo.bootstrap.layout.Region.call(this, config);
40267     this.visible = true;
40268     this.minWidth = config.minWidth || 20;
40269     this.minHeight = config.minHeight || 20;
40270 };
40271
40272 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40273     hide : function(){
40274         // center panel can't be hidden
40275     },
40276     
40277     show : function(){
40278         // center panel can't be hidden
40279     },
40280     
40281     getMinWidth: function(){
40282         return this.minWidth;
40283     },
40284     
40285     getMinHeight: function(){
40286         return this.minHeight;
40287     }
40288 });
40289
40290
40291
40292
40293  
40294
40295
40296
40297
40298
40299
40300 Roo.bootstrap.layout.North = function(config)
40301 {
40302     config.region = 'north';
40303     config.cursor = 'n-resize';
40304     
40305     Roo.bootstrap.layout.Split.call(this, config);
40306     
40307     
40308     if(this.split){
40309         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40310         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40311         this.split.el.addClass("roo-layout-split-v");
40312     }
40313     //var size = config.initialSize || config.height;
40314     //if(this.el && typeof size != "undefined"){
40315     //    this.el.setHeight(size);
40316     //}
40317 };
40318 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40319 {
40320     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40321      
40322      
40323     onRender : function(ctr, pos)
40324     {
40325         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40326         var size = this.config.initialSize || this.config.height;
40327         if(this.el && typeof size != "undefined"){
40328             this.el.setHeight(size);
40329         }
40330     
40331     },
40332     
40333     getBox : function(){
40334         if(this.collapsed){
40335             return this.collapsedEl.getBox();
40336         }
40337         var box = this.el.getBox();
40338         if(this.split){
40339             box.height += this.split.el.getHeight();
40340         }
40341         return box;
40342     },
40343     
40344     updateBox : function(box){
40345         if(this.split && !this.collapsed){
40346             box.height -= this.split.el.getHeight();
40347             this.split.el.setLeft(box.x);
40348             this.split.el.setTop(box.y+box.height);
40349             this.split.el.setWidth(box.width);
40350         }
40351         if(this.collapsed){
40352             this.updateBody(box.width, null);
40353         }
40354         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40355     }
40356 });
40357
40358
40359
40360
40361
40362 Roo.bootstrap.layout.South = function(config){
40363     config.region = 'south';
40364     config.cursor = 's-resize';
40365     Roo.bootstrap.layout.Split.call(this, config);
40366     if(this.split){
40367         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40368         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40369         this.split.el.addClass("roo-layout-split-v");
40370     }
40371     
40372 };
40373
40374 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40375     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40376     
40377     onRender : function(ctr, pos)
40378     {
40379         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40380         var size = this.config.initialSize || this.config.height;
40381         if(this.el && typeof size != "undefined"){
40382             this.el.setHeight(size);
40383         }
40384     
40385     },
40386     
40387     getBox : function(){
40388         if(this.collapsed){
40389             return this.collapsedEl.getBox();
40390         }
40391         var box = this.el.getBox();
40392         if(this.split){
40393             var sh = this.split.el.getHeight();
40394             box.height += sh;
40395             box.y -= sh;
40396         }
40397         return box;
40398     },
40399     
40400     updateBox : function(box){
40401         if(this.split && !this.collapsed){
40402             var sh = this.split.el.getHeight();
40403             box.height -= sh;
40404             box.y += sh;
40405             this.split.el.setLeft(box.x);
40406             this.split.el.setTop(box.y-sh);
40407             this.split.el.setWidth(box.width);
40408         }
40409         if(this.collapsed){
40410             this.updateBody(box.width, null);
40411         }
40412         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40413     }
40414 });
40415
40416 Roo.bootstrap.layout.East = function(config){
40417     config.region = "east";
40418     config.cursor = "e-resize";
40419     Roo.bootstrap.layout.Split.call(this, config);
40420     if(this.split){
40421         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40422         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40423         this.split.el.addClass("roo-layout-split-h");
40424     }
40425     
40426 };
40427 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40428     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40429     
40430     onRender : function(ctr, pos)
40431     {
40432         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40433         var size = this.config.initialSize || this.config.width;
40434         if(this.el && typeof size != "undefined"){
40435             this.el.setWidth(size);
40436         }
40437     
40438     },
40439     
40440     getBox : function(){
40441         if(this.collapsed){
40442             return this.collapsedEl.getBox();
40443         }
40444         var box = this.el.getBox();
40445         if(this.split){
40446             var sw = this.split.el.getWidth();
40447             box.width += sw;
40448             box.x -= sw;
40449         }
40450         return box;
40451     },
40452
40453     updateBox : function(box){
40454         if(this.split && !this.collapsed){
40455             var sw = this.split.el.getWidth();
40456             box.width -= sw;
40457             this.split.el.setLeft(box.x);
40458             this.split.el.setTop(box.y);
40459             this.split.el.setHeight(box.height);
40460             box.x += sw;
40461         }
40462         if(this.collapsed){
40463             this.updateBody(null, box.height);
40464         }
40465         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40466     }
40467 });
40468
40469 Roo.bootstrap.layout.West = function(config){
40470     config.region = "west";
40471     config.cursor = "w-resize";
40472     
40473     Roo.bootstrap.layout.Split.call(this, config);
40474     if(this.split){
40475         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40476         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40477         this.split.el.addClass("roo-layout-split-h");
40478     }
40479     
40480 };
40481 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40482     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40483     
40484     onRender: function(ctr, pos)
40485     {
40486         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40487         var size = this.config.initialSize || this.config.width;
40488         if(typeof size != "undefined"){
40489             this.el.setWidth(size);
40490         }
40491     },
40492     
40493     getBox : function(){
40494         if(this.collapsed){
40495             return this.collapsedEl.getBox();
40496         }
40497         var box = this.el.getBox();
40498         if (box.width == 0) {
40499             box.width = this.config.width; // kludge?
40500         }
40501         if(this.split){
40502             box.width += this.split.el.getWidth();
40503         }
40504         return box;
40505     },
40506     
40507     updateBox : function(box){
40508         if(this.split && !this.collapsed){
40509             var sw = this.split.el.getWidth();
40510             box.width -= sw;
40511             this.split.el.setLeft(box.x+box.width);
40512             this.split.el.setTop(box.y);
40513             this.split.el.setHeight(box.height);
40514         }
40515         if(this.collapsed){
40516             this.updateBody(null, box.height);
40517         }
40518         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40519     }
40520 });Roo.namespace("Roo.bootstrap.panel");/*
40521  * Based on:
40522  * Ext JS Library 1.1.1
40523  * Copyright(c) 2006-2007, Ext JS, LLC.
40524  *
40525  * Originally Released Under LGPL - original licence link has changed is not relivant.
40526  *
40527  * Fork - LGPL
40528  * <script type="text/javascript">
40529  */
40530 /**
40531  * @class Roo.ContentPanel
40532  * @extends Roo.util.Observable
40533  * A basic ContentPanel element.
40534  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40535  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40536  * @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
40537  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40538  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40539  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40540  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40541  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40542  * @cfg {String} title          The title for this panel
40543  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40544  * @cfg {String} url            Calls {@link #setUrl} with this value
40545  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40546  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40547  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40548  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40549  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40550  * @cfg {Boolean} badges render the badges
40551  * @cfg {String} cls  extra classes to use  
40552  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40553
40554  * @constructor
40555  * Create a new ContentPanel.
40556  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40557  * @param {String/Object} config A string to set only the title or a config object
40558  * @param {String} content (optional) Set the HTML content for this panel
40559  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40560  */
40561 Roo.bootstrap.panel.Content = function( config){
40562     
40563     this.tpl = config.tpl || false;
40564     
40565     var el = config.el;
40566     var content = config.content;
40567
40568     if(config.autoCreate){ // xtype is available if this is called from factory
40569         el = Roo.id();
40570     }
40571     this.el = Roo.get(el);
40572     if(!this.el && config && config.autoCreate){
40573         if(typeof config.autoCreate == "object"){
40574             if(!config.autoCreate.id){
40575                 config.autoCreate.id = config.id||el;
40576             }
40577             this.el = Roo.DomHelper.append(document.body,
40578                         config.autoCreate, true);
40579         }else{
40580             var elcfg =  {
40581                 tag: "div",
40582                 cls: (config.cls || '') +
40583                     (config.background ? ' bg-' + config.background : '') +
40584                     " roo-layout-inactive-content",
40585                 id: config.id||el
40586             };
40587             if (config.iframe) {
40588                 elcfg.cn = [
40589                     {
40590                         tag : 'iframe',
40591                         style : 'border: 0px',
40592                         src : 'about:blank'
40593                     }
40594                 ];
40595             }
40596               
40597             if (config.html) {
40598                 elcfg.html = config.html;
40599                 
40600             }
40601                         
40602             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40603             if (config.iframe) {
40604                 this.iframeEl = this.el.select('iframe',true).first();
40605             }
40606             
40607         }
40608     } 
40609     this.closable = false;
40610     this.loaded = false;
40611     this.active = false;
40612    
40613       
40614     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40615         
40616         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40617         
40618         this.wrapEl = this.el; //this.el.wrap();
40619         var ti = [];
40620         if (config.toolbar.items) {
40621             ti = config.toolbar.items ;
40622             delete config.toolbar.items ;
40623         }
40624         
40625         var nitems = [];
40626         this.toolbar.render(this.wrapEl, 'before');
40627         for(var i =0;i < ti.length;i++) {
40628           //  Roo.log(['add child', items[i]]);
40629             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40630         }
40631         this.toolbar.items = nitems;
40632         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40633         delete config.toolbar;
40634         
40635     }
40636     /*
40637     // xtype created footer. - not sure if will work as we normally have to render first..
40638     if (this.footer && !this.footer.el && this.footer.xtype) {
40639         if (!this.wrapEl) {
40640             this.wrapEl = this.el.wrap();
40641         }
40642     
40643         this.footer.container = this.wrapEl.createChild();
40644          
40645         this.footer = Roo.factory(this.footer, Roo);
40646         
40647     }
40648     */
40649     
40650      if(typeof config == "string"){
40651         this.title = config;
40652     }else{
40653         Roo.apply(this, config);
40654     }
40655     
40656     if(this.resizeEl){
40657         this.resizeEl = Roo.get(this.resizeEl, true);
40658     }else{
40659         this.resizeEl = this.el;
40660     }
40661     // handle view.xtype
40662     
40663  
40664     
40665     
40666     this.addEvents({
40667         /**
40668          * @event activate
40669          * Fires when this panel is activated. 
40670          * @param {Roo.ContentPanel} this
40671          */
40672         "activate" : true,
40673         /**
40674          * @event deactivate
40675          * Fires when this panel is activated. 
40676          * @param {Roo.ContentPanel} this
40677          */
40678         "deactivate" : true,
40679
40680         /**
40681          * @event resize
40682          * Fires when this panel is resized if fitToFrame is true.
40683          * @param {Roo.ContentPanel} this
40684          * @param {Number} width The width after any component adjustments
40685          * @param {Number} height The height after any component adjustments
40686          */
40687         "resize" : true,
40688         
40689          /**
40690          * @event render
40691          * Fires when this tab is created
40692          * @param {Roo.ContentPanel} this
40693          */
40694         "render" : true,
40695         
40696           /**
40697          * @event scroll
40698          * Fires when this content is scrolled
40699          * @param {Roo.ContentPanel} this
40700          * @param {Event} scrollEvent
40701          */
40702         "scroll" : true
40703         
40704         
40705         
40706     });
40707     
40708
40709     
40710     
40711     if(this.autoScroll && !this.iframe){
40712         this.resizeEl.setStyle("overflow", "auto");
40713         this.resizeEl.on('scroll', this.onScroll, this);
40714     } else {
40715         // fix randome scrolling
40716         //this.el.on('scroll', function() {
40717         //    Roo.log('fix random scolling');
40718         //    this.scrollTo('top',0); 
40719         //});
40720     }
40721     content = content || this.content;
40722     if(content){
40723         this.setContent(content);
40724     }
40725     if(config && config.url){
40726         this.setUrl(this.url, this.params, this.loadOnce);
40727     }
40728     
40729     
40730     
40731     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40732     
40733     if (this.view && typeof(this.view.xtype) != 'undefined') {
40734         this.view.el = this.el.appendChild(document.createElement("div"));
40735         this.view = Roo.factory(this.view); 
40736         this.view.render  &&  this.view.render(false, '');  
40737     }
40738     
40739     
40740     this.fireEvent('render', this);
40741 };
40742
40743 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40744     
40745     cls : '',
40746     background : '',
40747     
40748     tabTip : '',
40749     
40750     iframe : false,
40751     iframeEl : false,
40752     
40753     /* Resize Element - use this to work out scroll etc. */
40754     resizeEl : false,
40755     
40756     setRegion : function(region){
40757         this.region = region;
40758         this.setActiveClass(region && !this.background);
40759     },
40760     
40761     
40762     setActiveClass: function(state)
40763     {
40764         if(state){
40765            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40766            this.el.setStyle('position','relative');
40767         }else{
40768            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40769            this.el.setStyle('position', 'absolute');
40770         } 
40771     },
40772     
40773     /**
40774      * Returns the toolbar for this Panel if one was configured. 
40775      * @return {Roo.Toolbar} 
40776      */
40777     getToolbar : function(){
40778         return this.toolbar;
40779     },
40780     
40781     setActiveState : function(active)
40782     {
40783         this.active = active;
40784         this.setActiveClass(active);
40785         if(!active){
40786             if(this.fireEvent("deactivate", this) === false){
40787                 return false;
40788             }
40789             return true;
40790         }
40791         this.fireEvent("activate", this);
40792         return true;
40793     },
40794     /**
40795      * Updates this panel's element (not for iframe)
40796      * @param {String} content The new content
40797      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40798     */
40799     setContent : function(content, loadScripts){
40800         if (this.iframe) {
40801             return;
40802         }
40803         
40804         this.el.update(content, loadScripts);
40805     },
40806
40807     ignoreResize : function(w, h){
40808         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40809             return true;
40810         }else{
40811             this.lastSize = {width: w, height: h};
40812             return false;
40813         }
40814     },
40815     /**
40816      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40817      * @return {Roo.UpdateManager} The UpdateManager
40818      */
40819     getUpdateManager : function(){
40820         if (this.iframe) {
40821             return false;
40822         }
40823         return this.el.getUpdateManager();
40824     },
40825      /**
40826      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40827      * Does not work with IFRAME contents
40828      * @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:
40829 <pre><code>
40830 panel.load({
40831     url: "your-url.php",
40832     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40833     callback: yourFunction,
40834     scope: yourObject, //(optional scope)
40835     discardUrl: false,
40836     nocache: false,
40837     text: "Loading...",
40838     timeout: 30,
40839     scripts: false
40840 });
40841 </code></pre>
40842      
40843      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40844      * 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.
40845      * @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}
40846      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40847      * @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.
40848      * @return {Roo.ContentPanel} this
40849      */
40850     load : function(){
40851         
40852         if (this.iframe) {
40853             return this;
40854         }
40855         
40856         var um = this.el.getUpdateManager();
40857         um.update.apply(um, arguments);
40858         return this;
40859     },
40860
40861
40862     /**
40863      * 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.
40864      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40865      * @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)
40866      * @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)
40867      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40868      */
40869     setUrl : function(url, params, loadOnce){
40870         if (this.iframe) {
40871             this.iframeEl.dom.src = url;
40872             return false;
40873         }
40874         
40875         if(this.refreshDelegate){
40876             this.removeListener("activate", this.refreshDelegate);
40877         }
40878         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40879         this.on("activate", this.refreshDelegate);
40880         return this.el.getUpdateManager();
40881     },
40882     
40883     _handleRefresh : function(url, params, loadOnce){
40884         if(!loadOnce || !this.loaded){
40885             var updater = this.el.getUpdateManager();
40886             updater.update(url, params, this._setLoaded.createDelegate(this));
40887         }
40888     },
40889     
40890     _setLoaded : function(){
40891         this.loaded = true;
40892     }, 
40893     
40894     /**
40895      * Returns this panel's id
40896      * @return {String} 
40897      */
40898     getId : function(){
40899         return this.el.id;
40900     },
40901     
40902     /** 
40903      * Returns this panel's element - used by regiosn to add.
40904      * @return {Roo.Element} 
40905      */
40906     getEl : function(){
40907         return this.wrapEl || this.el;
40908     },
40909     
40910    
40911     
40912     adjustForComponents : function(width, height)
40913     {
40914         //Roo.log('adjustForComponents ');
40915         if(this.resizeEl != this.el){
40916             width -= this.el.getFrameWidth('lr');
40917             height -= this.el.getFrameWidth('tb');
40918         }
40919         if(this.toolbar){
40920             var te = this.toolbar.getEl();
40921             te.setWidth(width);
40922             height -= te.getHeight();
40923         }
40924         if(this.footer){
40925             var te = this.footer.getEl();
40926             te.setWidth(width);
40927             height -= te.getHeight();
40928         }
40929         
40930         
40931         if(this.adjustments){
40932             width += this.adjustments[0];
40933             height += this.adjustments[1];
40934         }
40935         return {"width": width, "height": height};
40936     },
40937     
40938     setSize : function(width, height){
40939         if(this.fitToFrame && !this.ignoreResize(width, height)){
40940             if(this.fitContainer && this.resizeEl != this.el){
40941                 this.el.setSize(width, height);
40942             }
40943             var size = this.adjustForComponents(width, height);
40944             if (this.iframe) {
40945                 this.iframeEl.setSize(width,height);
40946             }
40947             
40948             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40949             this.fireEvent('resize', this, size.width, size.height);
40950             
40951             
40952         }
40953     },
40954     
40955     /**
40956      * Returns this panel's title
40957      * @return {String} 
40958      */
40959     getTitle : function(){
40960         
40961         if (typeof(this.title) != 'object') {
40962             return this.title;
40963         }
40964         
40965         var t = '';
40966         for (var k in this.title) {
40967             if (!this.title.hasOwnProperty(k)) {
40968                 continue;
40969             }
40970             
40971             if (k.indexOf('-') >= 0) {
40972                 var s = k.split('-');
40973                 for (var i = 0; i<s.length; i++) {
40974                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40975                 }
40976             } else {
40977                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40978             }
40979         }
40980         return t;
40981     },
40982     
40983     /**
40984      * Set this panel's title
40985      * @param {String} title
40986      */
40987     setTitle : function(title){
40988         this.title = title;
40989         if(this.region){
40990             this.region.updatePanelTitle(this, title);
40991         }
40992     },
40993     
40994     /**
40995      * Returns true is this panel was configured to be closable
40996      * @return {Boolean} 
40997      */
40998     isClosable : function(){
40999         return this.closable;
41000     },
41001     
41002     beforeSlide : function(){
41003         this.el.clip();
41004         this.resizeEl.clip();
41005     },
41006     
41007     afterSlide : function(){
41008         this.el.unclip();
41009         this.resizeEl.unclip();
41010     },
41011     
41012     /**
41013      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41014      *   Will fail silently if the {@link #setUrl} method has not been called.
41015      *   This does not activate the panel, just updates its content.
41016      */
41017     refresh : function(){
41018         if(this.refreshDelegate){
41019            this.loaded = false;
41020            this.refreshDelegate();
41021         }
41022     },
41023     
41024     /**
41025      * Destroys this panel
41026      */
41027     destroy : function(){
41028         this.el.removeAllListeners();
41029         var tempEl = document.createElement("span");
41030         tempEl.appendChild(this.el.dom);
41031         tempEl.innerHTML = "";
41032         this.el.remove();
41033         this.el = null;
41034     },
41035     
41036     /**
41037      * form - if the content panel contains a form - this is a reference to it.
41038      * @type {Roo.form.Form}
41039      */
41040     form : false,
41041     /**
41042      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41043      *    This contains a reference to it.
41044      * @type {Roo.View}
41045      */
41046     view : false,
41047     
41048       /**
41049      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41050      * <pre><code>
41051
41052 layout.addxtype({
41053        xtype : 'Form',
41054        items: [ .... ]
41055    }
41056 );
41057
41058 </code></pre>
41059      * @param {Object} cfg Xtype definition of item to add.
41060      */
41061     
41062     
41063     getChildContainer: function () {
41064         return this.getEl();
41065     },
41066     
41067     
41068     onScroll : function(e)
41069     {
41070         this.fireEvent('scroll', this, e);
41071     }
41072     
41073     
41074     /*
41075         var  ret = new Roo.factory(cfg);
41076         return ret;
41077         
41078         
41079         // add form..
41080         if (cfg.xtype.match(/^Form$/)) {
41081             
41082             var el;
41083             //if (this.footer) {
41084             //    el = this.footer.container.insertSibling(false, 'before');
41085             //} else {
41086                 el = this.el.createChild();
41087             //}
41088
41089             this.form = new  Roo.form.Form(cfg);
41090             
41091             
41092             if ( this.form.allItems.length) {
41093                 this.form.render(el.dom);
41094             }
41095             return this.form;
41096         }
41097         // should only have one of theses..
41098         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41099             // views.. should not be just added - used named prop 'view''
41100             
41101             cfg.el = this.el.appendChild(document.createElement("div"));
41102             // factory?
41103             
41104             var ret = new Roo.factory(cfg);
41105              
41106              ret.render && ret.render(false, ''); // render blank..
41107             this.view = ret;
41108             return ret;
41109         }
41110         return false;
41111     }
41112     \*/
41113 });
41114  
41115 /**
41116  * @class Roo.bootstrap.panel.Grid
41117  * @extends Roo.bootstrap.panel.Content
41118  * @constructor
41119  * Create a new GridPanel.
41120  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41121  * @param {Object} config A the config object
41122   
41123  */
41124
41125
41126
41127 Roo.bootstrap.panel.Grid = function(config)
41128 {
41129     
41130       
41131     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41132         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41133
41134     config.el = this.wrapper;
41135     //this.el = this.wrapper;
41136     
41137       if (config.container) {
41138         // ctor'ed from a Border/panel.grid
41139         
41140         
41141         this.wrapper.setStyle("overflow", "hidden");
41142         this.wrapper.addClass('roo-grid-container');
41143
41144     }
41145     
41146     
41147     if(config.toolbar){
41148         var tool_el = this.wrapper.createChild();    
41149         this.toolbar = Roo.factory(config.toolbar);
41150         var ti = [];
41151         if (config.toolbar.items) {
41152             ti = config.toolbar.items ;
41153             delete config.toolbar.items ;
41154         }
41155         
41156         var nitems = [];
41157         this.toolbar.render(tool_el);
41158         for(var i =0;i < ti.length;i++) {
41159           //  Roo.log(['add child', items[i]]);
41160             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41161         }
41162         this.toolbar.items = nitems;
41163         
41164         delete config.toolbar;
41165     }
41166     
41167     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41168     config.grid.scrollBody = true;;
41169     config.grid.monitorWindowResize = false; // turn off autosizing
41170     config.grid.autoHeight = false;
41171     config.grid.autoWidth = false;
41172     
41173     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41174     
41175     if (config.background) {
41176         // render grid on panel activation (if panel background)
41177         this.on('activate', function(gp) {
41178             if (!gp.grid.rendered) {
41179                 gp.grid.render(this.wrapper);
41180                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41181             }
41182         });
41183             
41184     } else {
41185         this.grid.render(this.wrapper);
41186         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41187
41188     }
41189     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41190     // ??? needed ??? config.el = this.wrapper;
41191     
41192     
41193     
41194   
41195     // xtype created footer. - not sure if will work as we normally have to render first..
41196     if (this.footer && !this.footer.el && this.footer.xtype) {
41197         
41198         var ctr = this.grid.getView().getFooterPanel(true);
41199         this.footer.dataSource = this.grid.dataSource;
41200         this.footer = Roo.factory(this.footer, Roo);
41201         this.footer.render(ctr);
41202         
41203     }
41204     
41205     
41206     
41207     
41208      
41209 };
41210
41211 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41212     getId : function(){
41213         return this.grid.id;
41214     },
41215     
41216     /**
41217      * Returns the grid for this panel
41218      * @return {Roo.bootstrap.Table} 
41219      */
41220     getGrid : function(){
41221         return this.grid;    
41222     },
41223     
41224     setSize : function(width, height){
41225         if(!this.ignoreResize(width, height)){
41226             var grid = this.grid;
41227             var size = this.adjustForComponents(width, height);
41228             // tfoot is not a footer?
41229           
41230             
41231             var gridel = grid.getGridEl();
41232             gridel.setSize(size.width, size.height);
41233             
41234             var tbd = grid.getGridEl().select('tbody', true).first();
41235             var thd = grid.getGridEl().select('thead',true).first();
41236             var tbf= grid.getGridEl().select('tfoot', true).first();
41237
41238             if (tbf) {
41239                 size.height -= tbf.getHeight();
41240             }
41241             if (thd) {
41242                 size.height -= thd.getHeight();
41243             }
41244             
41245             tbd.setSize(size.width, size.height );
41246             // this is for the account management tab -seems to work there.
41247             var thd = grid.getGridEl().select('thead',true).first();
41248             //if (tbd) {
41249             //    tbd.setSize(size.width, size.height - thd.getHeight());
41250             //}
41251              
41252             grid.autoSize();
41253         }
41254     },
41255      
41256     
41257     
41258     beforeSlide : function(){
41259         this.grid.getView().scroller.clip();
41260     },
41261     
41262     afterSlide : function(){
41263         this.grid.getView().scroller.unclip();
41264     },
41265     
41266     destroy : function(){
41267         this.grid.destroy();
41268         delete this.grid;
41269         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41270     }
41271 });
41272
41273 /**
41274  * @class Roo.bootstrap.panel.Nest
41275  * @extends Roo.bootstrap.panel.Content
41276  * @constructor
41277  * Create a new Panel, that can contain a layout.Border.
41278  * 
41279  * 
41280  * @param {Roo.BorderLayout} layout The layout for this panel
41281  * @param {String/Object} config A string to set only the title or a config object
41282  */
41283 Roo.bootstrap.panel.Nest = function(config)
41284 {
41285     // construct with only one argument..
41286     /* FIXME - implement nicer consturctors
41287     if (layout.layout) {
41288         config = layout;
41289         layout = config.layout;
41290         delete config.layout;
41291     }
41292     if (layout.xtype && !layout.getEl) {
41293         // then layout needs constructing..
41294         layout = Roo.factory(layout, Roo);
41295     }
41296     */
41297     
41298     config.el =  config.layout.getEl();
41299     
41300     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41301     
41302     config.layout.monitorWindowResize = false; // turn off autosizing
41303     this.layout = config.layout;
41304     this.layout.getEl().addClass("roo-layout-nested-layout");
41305     this.layout.parent = this;
41306     
41307     
41308     
41309     
41310 };
41311
41312 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41313
41314     setSize : function(width, height){
41315         if(!this.ignoreResize(width, height)){
41316             var size = this.adjustForComponents(width, height);
41317             var el = this.layout.getEl();
41318             if (size.height < 1) {
41319                 el.setWidth(size.width);   
41320             } else {
41321                 el.setSize(size.width, size.height);
41322             }
41323             var touch = el.dom.offsetWidth;
41324             this.layout.layout();
41325             // ie requires a double layout on the first pass
41326             if(Roo.isIE && !this.initialized){
41327                 this.initialized = true;
41328                 this.layout.layout();
41329             }
41330         }
41331     },
41332     
41333     // activate all subpanels if not currently active..
41334     
41335     setActiveState : function(active){
41336         this.active = active;
41337         this.setActiveClass(active);
41338         
41339         if(!active){
41340             this.fireEvent("deactivate", this);
41341             return;
41342         }
41343         
41344         this.fireEvent("activate", this);
41345         // not sure if this should happen before or after..
41346         if (!this.layout) {
41347             return; // should not happen..
41348         }
41349         var reg = false;
41350         for (var r in this.layout.regions) {
41351             reg = this.layout.getRegion(r);
41352             if (reg.getActivePanel()) {
41353                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41354                 reg.setActivePanel(reg.getActivePanel());
41355                 continue;
41356             }
41357             if (!reg.panels.length) {
41358                 continue;
41359             }
41360             reg.showPanel(reg.getPanel(0));
41361         }
41362         
41363         
41364         
41365         
41366     },
41367     
41368     /**
41369      * Returns the nested BorderLayout for this panel
41370      * @return {Roo.BorderLayout} 
41371      */
41372     getLayout : function(){
41373         return this.layout;
41374     },
41375     
41376      /**
41377      * Adds a xtype elements to the layout of the nested panel
41378      * <pre><code>
41379
41380 panel.addxtype({
41381        xtype : 'ContentPanel',
41382        region: 'west',
41383        items: [ .... ]
41384    }
41385 );
41386
41387 panel.addxtype({
41388         xtype : 'NestedLayoutPanel',
41389         region: 'west',
41390         layout: {
41391            center: { },
41392            west: { }   
41393         },
41394         items : [ ... list of content panels or nested layout panels.. ]
41395    }
41396 );
41397 </code></pre>
41398      * @param {Object} cfg Xtype definition of item to add.
41399      */
41400     addxtype : function(cfg) {
41401         return this.layout.addxtype(cfg);
41402     
41403     }
41404 });/*
41405  * Based on:
41406  * Ext JS Library 1.1.1
41407  * Copyright(c) 2006-2007, Ext JS, LLC.
41408  *
41409  * Originally Released Under LGPL - original licence link has changed is not relivant.
41410  *
41411  * Fork - LGPL
41412  * <script type="text/javascript">
41413  */
41414 /**
41415  * @class Roo.TabPanel
41416  * @extends Roo.util.Observable
41417  * A lightweight tab container.
41418  * <br><br>
41419  * Usage:
41420  * <pre><code>
41421 // basic tabs 1, built from existing content
41422 var tabs = new Roo.TabPanel("tabs1");
41423 tabs.addTab("script", "View Script");
41424 tabs.addTab("markup", "View Markup");
41425 tabs.activate("script");
41426
41427 // more advanced tabs, built from javascript
41428 var jtabs = new Roo.TabPanel("jtabs");
41429 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41430
41431 // set up the UpdateManager
41432 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41433 var updater = tab2.getUpdateManager();
41434 updater.setDefaultUrl("ajax1.htm");
41435 tab2.on('activate', updater.refresh, updater, true);
41436
41437 // Use setUrl for Ajax loading
41438 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41439 tab3.setUrl("ajax2.htm", null, true);
41440
41441 // Disabled tab
41442 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41443 tab4.disable();
41444
41445 jtabs.activate("jtabs-1");
41446  * </code></pre>
41447  * @constructor
41448  * Create a new TabPanel.
41449  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41450  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41451  */
41452 Roo.bootstrap.panel.Tabs = function(config){
41453     /**
41454     * The container element for this TabPanel.
41455     * @type Roo.Element
41456     */
41457     this.el = Roo.get(config.el);
41458     delete config.el;
41459     if(config){
41460         if(typeof config == "boolean"){
41461             this.tabPosition = config ? "bottom" : "top";
41462         }else{
41463             Roo.apply(this, config);
41464         }
41465     }
41466     
41467     if(this.tabPosition == "bottom"){
41468         // if tabs are at the bottom = create the body first.
41469         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41470         this.el.addClass("roo-tabs-bottom");
41471     }
41472     // next create the tabs holders
41473     
41474     if (this.tabPosition == "west"){
41475         
41476         var reg = this.region; // fake it..
41477         while (reg) {
41478             if (!reg.mgr.parent) {
41479                 break;
41480             }
41481             reg = reg.mgr.parent.region;
41482         }
41483         Roo.log("got nest?");
41484         Roo.log(reg);
41485         if (reg.mgr.getRegion('west')) {
41486             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41487             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41488             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41489             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41490             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41491         
41492             
41493         }
41494         
41495         
41496     } else {
41497      
41498         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41499         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41500         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41501         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41502     }
41503     
41504     
41505     if(Roo.isIE){
41506         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41507     }
41508     
41509     // finally - if tabs are at the top, then create the body last..
41510     if(this.tabPosition != "bottom"){
41511         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41512          * @type Roo.Element
41513          */
41514         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41515         this.el.addClass("roo-tabs-top");
41516     }
41517     this.items = [];
41518
41519     this.bodyEl.setStyle("position", "relative");
41520
41521     this.active = null;
41522     this.activateDelegate = this.activate.createDelegate(this);
41523
41524     this.addEvents({
41525         /**
41526          * @event tabchange
41527          * Fires when the active tab changes
41528          * @param {Roo.TabPanel} this
41529          * @param {Roo.TabPanelItem} activePanel The new active tab
41530          */
41531         "tabchange": true,
41532         /**
41533          * @event beforetabchange
41534          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41535          * @param {Roo.TabPanel} this
41536          * @param {Object} e Set cancel to true on this object to cancel the tab change
41537          * @param {Roo.TabPanelItem} tab The tab being changed to
41538          */
41539         "beforetabchange" : true
41540     });
41541
41542     Roo.EventManager.onWindowResize(this.onResize, this);
41543     this.cpad = this.el.getPadding("lr");
41544     this.hiddenCount = 0;
41545
41546
41547     // toolbar on the tabbar support...
41548     if (this.toolbar) {
41549         alert("no toolbar support yet");
41550         this.toolbar  = false;
41551         /*
41552         var tcfg = this.toolbar;
41553         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41554         this.toolbar = new Roo.Toolbar(tcfg);
41555         if (Roo.isSafari) {
41556             var tbl = tcfg.container.child('table', true);
41557             tbl.setAttribute('width', '100%');
41558         }
41559         */
41560         
41561     }
41562    
41563
41564
41565     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41566 };
41567
41568 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41569     /*
41570      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41571      */
41572     tabPosition : "top",
41573     /*
41574      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41575      */
41576     currentTabWidth : 0,
41577     /*
41578      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41579      */
41580     minTabWidth : 40,
41581     /*
41582      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41583      */
41584     maxTabWidth : 250,
41585     /*
41586      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41587      */
41588     preferredTabWidth : 175,
41589     /*
41590      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41591      */
41592     resizeTabs : false,
41593     /*
41594      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41595      */
41596     monitorResize : true,
41597     /*
41598      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41599      */
41600     toolbar : false,  // set by caller..
41601     
41602     region : false, /// set by caller
41603     
41604     disableTooltips : true, // not used yet...
41605
41606     /**
41607      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41608      * @param {String} id The id of the div to use <b>or create</b>
41609      * @param {String} text The text for the tab
41610      * @param {String} content (optional) Content to put in the TabPanelItem body
41611      * @param {Boolean} closable (optional) True to create a close icon on the tab
41612      * @return {Roo.TabPanelItem} The created TabPanelItem
41613      */
41614     addTab : function(id, text, content, closable, tpl)
41615     {
41616         var item = new Roo.bootstrap.panel.TabItem({
41617             panel: this,
41618             id : id,
41619             text : text,
41620             closable : closable,
41621             tpl : tpl
41622         });
41623         this.addTabItem(item);
41624         if(content){
41625             item.setContent(content);
41626         }
41627         return item;
41628     },
41629
41630     /**
41631      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41632      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41633      * @return {Roo.TabPanelItem}
41634      */
41635     getTab : function(id){
41636         return this.items[id];
41637     },
41638
41639     /**
41640      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41641      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41642      */
41643     hideTab : function(id){
41644         var t = this.items[id];
41645         if(!t.isHidden()){
41646            t.setHidden(true);
41647            this.hiddenCount++;
41648            this.autoSizeTabs();
41649         }
41650     },
41651
41652     /**
41653      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41654      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41655      */
41656     unhideTab : function(id){
41657         var t = this.items[id];
41658         if(t.isHidden()){
41659            t.setHidden(false);
41660            this.hiddenCount--;
41661            this.autoSizeTabs();
41662         }
41663     },
41664
41665     /**
41666      * Adds an existing {@link Roo.TabPanelItem}.
41667      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41668      */
41669     addTabItem : function(item)
41670     {
41671         this.items[item.id] = item;
41672         this.items.push(item);
41673         this.autoSizeTabs();
41674       //  if(this.resizeTabs){
41675     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41676   //         this.autoSizeTabs();
41677 //        }else{
41678 //            item.autoSize();
41679        // }
41680     },
41681
41682     /**
41683      * Removes a {@link Roo.TabPanelItem}.
41684      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41685      */
41686     removeTab : function(id){
41687         var items = this.items;
41688         var tab = items[id];
41689         if(!tab) { return; }
41690         var index = items.indexOf(tab);
41691         if(this.active == tab && items.length > 1){
41692             var newTab = this.getNextAvailable(index);
41693             if(newTab) {
41694                 newTab.activate();
41695             }
41696         }
41697         this.stripEl.dom.removeChild(tab.pnode.dom);
41698         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41699             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41700         }
41701         items.splice(index, 1);
41702         delete this.items[tab.id];
41703         tab.fireEvent("close", tab);
41704         tab.purgeListeners();
41705         this.autoSizeTabs();
41706     },
41707
41708     getNextAvailable : function(start){
41709         var items = this.items;
41710         var index = start;
41711         // look for a next tab that will slide over to
41712         // replace the one being removed
41713         while(index < items.length){
41714             var item = items[++index];
41715             if(item && !item.isHidden()){
41716                 return item;
41717             }
41718         }
41719         // if one isn't found select the previous tab (on the left)
41720         index = start;
41721         while(index >= 0){
41722             var item = items[--index];
41723             if(item && !item.isHidden()){
41724                 return item;
41725             }
41726         }
41727         return null;
41728     },
41729
41730     /**
41731      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41732      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41733      */
41734     disableTab : function(id){
41735         var tab = this.items[id];
41736         if(tab && this.active != tab){
41737             tab.disable();
41738         }
41739     },
41740
41741     /**
41742      * Enables a {@link Roo.TabPanelItem} that is disabled.
41743      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41744      */
41745     enableTab : function(id){
41746         var tab = this.items[id];
41747         tab.enable();
41748     },
41749
41750     /**
41751      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41752      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41753      * @return {Roo.TabPanelItem} The TabPanelItem.
41754      */
41755     activate : function(id)
41756     {
41757         //Roo.log('activite:'  + id);
41758         
41759         var tab = this.items[id];
41760         if(!tab){
41761             return null;
41762         }
41763         if(tab == this.active || tab.disabled){
41764             return tab;
41765         }
41766         var e = {};
41767         this.fireEvent("beforetabchange", this, e, tab);
41768         if(e.cancel !== true && !tab.disabled){
41769             if(this.active){
41770                 this.active.hide();
41771             }
41772             this.active = this.items[id];
41773             this.active.show();
41774             this.fireEvent("tabchange", this, this.active);
41775         }
41776         return tab;
41777     },
41778
41779     /**
41780      * Gets the active {@link Roo.TabPanelItem}.
41781      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41782      */
41783     getActiveTab : function(){
41784         return this.active;
41785     },
41786
41787     /**
41788      * Updates the tab body element to fit the height of the container element
41789      * for overflow scrolling
41790      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41791      */
41792     syncHeight : function(targetHeight){
41793         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41794         var bm = this.bodyEl.getMargins();
41795         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41796         this.bodyEl.setHeight(newHeight);
41797         return newHeight;
41798     },
41799
41800     onResize : function(){
41801         if(this.monitorResize){
41802             this.autoSizeTabs();
41803         }
41804     },
41805
41806     /**
41807      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41808      */
41809     beginUpdate : function(){
41810         this.updating = true;
41811     },
41812
41813     /**
41814      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41815      */
41816     endUpdate : function(){
41817         this.updating = false;
41818         this.autoSizeTabs();
41819     },
41820
41821     /**
41822      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41823      */
41824     autoSizeTabs : function()
41825     {
41826         var count = this.items.length;
41827         var vcount = count - this.hiddenCount;
41828         
41829         if (vcount < 2) {
41830             this.stripEl.hide();
41831         } else {
41832             this.stripEl.show();
41833         }
41834         
41835         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41836             return;
41837         }
41838         
41839         
41840         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41841         var availWidth = Math.floor(w / vcount);
41842         var b = this.stripBody;
41843         if(b.getWidth() > w){
41844             var tabs = this.items;
41845             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41846             if(availWidth < this.minTabWidth){
41847                 /*if(!this.sleft){    // incomplete scrolling code
41848                     this.createScrollButtons();
41849                 }
41850                 this.showScroll();
41851                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41852             }
41853         }else{
41854             if(this.currentTabWidth < this.preferredTabWidth){
41855                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41856             }
41857         }
41858     },
41859
41860     /**
41861      * Returns the number of tabs in this TabPanel.
41862      * @return {Number}
41863      */
41864      getCount : function(){
41865          return this.items.length;
41866      },
41867
41868     /**
41869      * Resizes all the tabs to the passed width
41870      * @param {Number} The new width
41871      */
41872     setTabWidth : function(width){
41873         this.currentTabWidth = width;
41874         for(var i = 0, len = this.items.length; i < len; i++) {
41875                 if(!this.items[i].isHidden()) {
41876                 this.items[i].setWidth(width);
41877             }
41878         }
41879     },
41880
41881     /**
41882      * Destroys this TabPanel
41883      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41884      */
41885     destroy : function(removeEl){
41886         Roo.EventManager.removeResizeListener(this.onResize, this);
41887         for(var i = 0, len = this.items.length; i < len; i++){
41888             this.items[i].purgeListeners();
41889         }
41890         if(removeEl === true){
41891             this.el.update("");
41892             this.el.remove();
41893         }
41894     },
41895     
41896     createStrip : function(container)
41897     {
41898         var strip = document.createElement("nav");
41899         strip.className = Roo.bootstrap.version == 4 ?
41900             "navbar-light bg-light" : 
41901             "navbar navbar-default"; //"x-tabs-wrap";
41902         container.appendChild(strip);
41903         return strip;
41904     },
41905     
41906     createStripList : function(strip)
41907     {
41908         // div wrapper for retard IE
41909         // returns the "tr" element.
41910         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41911         //'<div class="x-tabs-strip-wrap">'+
41912           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41913           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41914         return strip.firstChild; //.firstChild.firstChild.firstChild;
41915     },
41916     createBody : function(container)
41917     {
41918         var body = document.createElement("div");
41919         Roo.id(body, "tab-body");
41920         //Roo.fly(body).addClass("x-tabs-body");
41921         Roo.fly(body).addClass("tab-content");
41922         container.appendChild(body);
41923         return body;
41924     },
41925     createItemBody :function(bodyEl, id){
41926         var body = Roo.getDom(id);
41927         if(!body){
41928             body = document.createElement("div");
41929             body.id = id;
41930         }
41931         //Roo.fly(body).addClass("x-tabs-item-body");
41932         Roo.fly(body).addClass("tab-pane");
41933          bodyEl.insertBefore(body, bodyEl.firstChild);
41934         return body;
41935     },
41936     /** @private */
41937     createStripElements :  function(stripEl, text, closable, tpl)
41938     {
41939         var td = document.createElement("li"); // was td..
41940         td.className = 'nav-item';
41941         
41942         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41943         
41944         
41945         stripEl.appendChild(td);
41946         /*if(closable){
41947             td.className = "x-tabs-closable";
41948             if(!this.closeTpl){
41949                 this.closeTpl = new Roo.Template(
41950                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41951                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41952                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41953                 );
41954             }
41955             var el = this.closeTpl.overwrite(td, {"text": text});
41956             var close = el.getElementsByTagName("div")[0];
41957             var inner = el.getElementsByTagName("em")[0];
41958             return {"el": el, "close": close, "inner": inner};
41959         } else {
41960         */
41961         // not sure what this is..
41962 //            if(!this.tabTpl){
41963                 //this.tabTpl = new Roo.Template(
41964                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41965                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41966                 //);
41967 //                this.tabTpl = new Roo.Template(
41968 //                   '<a href="#">' +
41969 //                   '<span unselectable="on"' +
41970 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41971 //                            ' >{text}</span></a>'
41972 //                );
41973 //                
41974 //            }
41975
41976
41977             var template = tpl || this.tabTpl || false;
41978             
41979             if(!template){
41980                 template =  new Roo.Template(
41981                         Roo.bootstrap.version == 4 ? 
41982                             (
41983                                 '<a class="nav-link" href="#" unselectable="on"' +
41984                                      (this.disableTooltips ? '' : ' title="{text}"') +
41985                                      ' >{text}</a>'
41986                             ) : (
41987                                 '<a class="nav-link" href="#">' +
41988                                 '<span unselectable="on"' +
41989                                          (this.disableTooltips ? '' : ' title="{text}"') +
41990                                     ' >{text}</span></a>'
41991                             )
41992                 );
41993             }
41994             
41995             switch (typeof(template)) {
41996                 case 'object' :
41997                     break;
41998                 case 'string' :
41999                     template = new Roo.Template(template);
42000                     break;
42001                 default :
42002                     break;
42003             }
42004             
42005             var el = template.overwrite(td, {"text": text});
42006             
42007             var inner = el.getElementsByTagName("span")[0];
42008             
42009             return {"el": el, "inner": inner};
42010             
42011     }
42012         
42013     
42014 });
42015
42016 /**
42017  * @class Roo.TabPanelItem
42018  * @extends Roo.util.Observable
42019  * Represents an individual item (tab plus body) in a TabPanel.
42020  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42021  * @param {String} id The id of this TabPanelItem
42022  * @param {String} text The text for the tab of this TabPanelItem
42023  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42024  */
42025 Roo.bootstrap.panel.TabItem = function(config){
42026     /**
42027      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42028      * @type Roo.TabPanel
42029      */
42030     this.tabPanel = config.panel;
42031     /**
42032      * The id for this TabPanelItem
42033      * @type String
42034      */
42035     this.id = config.id;
42036     /** @private */
42037     this.disabled = false;
42038     /** @private */
42039     this.text = config.text;
42040     /** @private */
42041     this.loaded = false;
42042     this.closable = config.closable;
42043
42044     /**
42045      * The body element for this TabPanelItem.
42046      * @type Roo.Element
42047      */
42048     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42049     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42050     this.bodyEl.setStyle("display", "block");
42051     this.bodyEl.setStyle("zoom", "1");
42052     //this.hideAction();
42053
42054     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42055     /** @private */
42056     this.el = Roo.get(els.el);
42057     this.inner = Roo.get(els.inner, true);
42058      this.textEl = Roo.bootstrap.version == 4 ?
42059         this.el : Roo.get(this.el.dom.firstChild, true);
42060
42061     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42062     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42063
42064     
42065 //    this.el.on("mousedown", this.onTabMouseDown, this);
42066     this.el.on("click", this.onTabClick, this);
42067     /** @private */
42068     if(config.closable){
42069         var c = Roo.get(els.close, true);
42070         c.dom.title = this.closeText;
42071         c.addClassOnOver("close-over");
42072         c.on("click", this.closeClick, this);
42073      }
42074
42075     this.addEvents({
42076          /**
42077          * @event activate
42078          * Fires when this tab becomes the active tab.
42079          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42080          * @param {Roo.TabPanelItem} this
42081          */
42082         "activate": true,
42083         /**
42084          * @event beforeclose
42085          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42086          * @param {Roo.TabPanelItem} this
42087          * @param {Object} e Set cancel to true on this object to cancel the close.
42088          */
42089         "beforeclose": true,
42090         /**
42091          * @event close
42092          * Fires when this tab is closed.
42093          * @param {Roo.TabPanelItem} this
42094          */
42095          "close": true,
42096         /**
42097          * @event deactivate
42098          * Fires when this tab is no longer the active tab.
42099          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42100          * @param {Roo.TabPanelItem} this
42101          */
42102          "deactivate" : true
42103     });
42104     this.hidden = false;
42105
42106     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42107 };
42108
42109 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42110            {
42111     purgeListeners : function(){
42112        Roo.util.Observable.prototype.purgeListeners.call(this);
42113        this.el.removeAllListeners();
42114     },
42115     /**
42116      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42117      */
42118     show : function(){
42119         this.status_node.addClass("active");
42120         this.showAction();
42121         if(Roo.isOpera){
42122             this.tabPanel.stripWrap.repaint();
42123         }
42124         this.fireEvent("activate", this.tabPanel, this);
42125     },
42126
42127     /**
42128      * Returns true if this tab is the active tab.
42129      * @return {Boolean}
42130      */
42131     isActive : function(){
42132         return this.tabPanel.getActiveTab() == this;
42133     },
42134
42135     /**
42136      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42137      */
42138     hide : function(){
42139         this.status_node.removeClass("active");
42140         this.hideAction();
42141         this.fireEvent("deactivate", this.tabPanel, this);
42142     },
42143
42144     hideAction : function(){
42145         this.bodyEl.hide();
42146         this.bodyEl.setStyle("position", "absolute");
42147         this.bodyEl.setLeft("-20000px");
42148         this.bodyEl.setTop("-20000px");
42149     },
42150
42151     showAction : function(){
42152         this.bodyEl.setStyle("position", "relative");
42153         this.bodyEl.setTop("");
42154         this.bodyEl.setLeft("");
42155         this.bodyEl.show();
42156     },
42157
42158     /**
42159      * Set the tooltip for the tab.
42160      * @param {String} tooltip The tab's tooltip
42161      */
42162     setTooltip : function(text){
42163         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42164             this.textEl.dom.qtip = text;
42165             this.textEl.dom.removeAttribute('title');
42166         }else{
42167             this.textEl.dom.title = text;
42168         }
42169     },
42170
42171     onTabClick : function(e){
42172         e.preventDefault();
42173         this.tabPanel.activate(this.id);
42174     },
42175
42176     onTabMouseDown : function(e){
42177         e.preventDefault();
42178         this.tabPanel.activate(this.id);
42179     },
42180 /*
42181     getWidth : function(){
42182         return this.inner.getWidth();
42183     },
42184
42185     setWidth : function(width){
42186         var iwidth = width - this.linode.getPadding("lr");
42187         this.inner.setWidth(iwidth);
42188         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42189         this.linode.setWidth(width);
42190     },
42191 */
42192     /**
42193      * Show or hide the tab
42194      * @param {Boolean} hidden True to hide or false to show.
42195      */
42196     setHidden : function(hidden){
42197         this.hidden = hidden;
42198         this.linode.setStyle("display", hidden ? "none" : "");
42199     },
42200
42201     /**
42202      * Returns true if this tab is "hidden"
42203      * @return {Boolean}
42204      */
42205     isHidden : function(){
42206         return this.hidden;
42207     },
42208
42209     /**
42210      * Returns the text for this tab
42211      * @return {String}
42212      */
42213     getText : function(){
42214         return this.text;
42215     },
42216     /*
42217     autoSize : function(){
42218         //this.el.beginMeasure();
42219         this.textEl.setWidth(1);
42220         /*
42221          *  #2804 [new] Tabs in Roojs
42222          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42223          */
42224         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42225         //this.el.endMeasure();
42226     //},
42227
42228     /**
42229      * Sets the text for the tab (Note: this also sets the tooltip text)
42230      * @param {String} text The tab's text and tooltip
42231      */
42232     setText : function(text){
42233         this.text = text;
42234         this.textEl.update(text);
42235         this.setTooltip(text);
42236         //if(!this.tabPanel.resizeTabs){
42237         //    this.autoSize();
42238         //}
42239     },
42240     /**
42241      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42242      */
42243     activate : function(){
42244         this.tabPanel.activate(this.id);
42245     },
42246
42247     /**
42248      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42249      */
42250     disable : function(){
42251         if(this.tabPanel.active != this){
42252             this.disabled = true;
42253             this.status_node.addClass("disabled");
42254         }
42255     },
42256
42257     /**
42258      * Enables this TabPanelItem if it was previously disabled.
42259      */
42260     enable : function(){
42261         this.disabled = false;
42262         this.status_node.removeClass("disabled");
42263     },
42264
42265     /**
42266      * Sets the content for this TabPanelItem.
42267      * @param {String} content The content
42268      * @param {Boolean} loadScripts true to look for and load scripts
42269      */
42270     setContent : function(content, loadScripts){
42271         this.bodyEl.update(content, loadScripts);
42272     },
42273
42274     /**
42275      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42276      * @return {Roo.UpdateManager} The UpdateManager
42277      */
42278     getUpdateManager : function(){
42279         return this.bodyEl.getUpdateManager();
42280     },
42281
42282     /**
42283      * Set a URL to be used to load the content for this TabPanelItem.
42284      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42285      * @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)
42286      * @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)
42287      * @return {Roo.UpdateManager} The UpdateManager
42288      */
42289     setUrl : function(url, params, loadOnce){
42290         if(this.refreshDelegate){
42291             this.un('activate', this.refreshDelegate);
42292         }
42293         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42294         this.on("activate", this.refreshDelegate);
42295         return this.bodyEl.getUpdateManager();
42296     },
42297
42298     /** @private */
42299     _handleRefresh : function(url, params, loadOnce){
42300         if(!loadOnce || !this.loaded){
42301             var updater = this.bodyEl.getUpdateManager();
42302             updater.update(url, params, this._setLoaded.createDelegate(this));
42303         }
42304     },
42305
42306     /**
42307      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42308      *   Will fail silently if the setUrl method has not been called.
42309      *   This does not activate the panel, just updates its content.
42310      */
42311     refresh : function(){
42312         if(this.refreshDelegate){
42313            this.loaded = false;
42314            this.refreshDelegate();
42315         }
42316     },
42317
42318     /** @private */
42319     _setLoaded : function(){
42320         this.loaded = true;
42321     },
42322
42323     /** @private */
42324     closeClick : function(e){
42325         var o = {};
42326         e.stopEvent();
42327         this.fireEvent("beforeclose", this, o);
42328         if(o.cancel !== true){
42329             this.tabPanel.removeTab(this.id);
42330         }
42331     },
42332     /**
42333      * The text displayed in the tooltip for the close icon.
42334      * @type String
42335      */
42336     closeText : "Close this tab"
42337 });
42338 /**
42339 *    This script refer to:
42340 *    Title: International Telephone Input
42341 *    Author: Jack O'Connor
42342 *    Code version:  v12.1.12
42343 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42344 **/
42345
42346 Roo.bootstrap.PhoneInputData = function() {
42347     var d = [
42348       [
42349         "Afghanistan (‫افغانستان‬‎)",
42350         "af",
42351         "93"
42352       ],
42353       [
42354         "Albania (Shqipëri)",
42355         "al",
42356         "355"
42357       ],
42358       [
42359         "Algeria (‫الجزائر‬‎)",
42360         "dz",
42361         "213"
42362       ],
42363       [
42364         "American Samoa",
42365         "as",
42366         "1684"
42367       ],
42368       [
42369         "Andorra",
42370         "ad",
42371         "376"
42372       ],
42373       [
42374         "Angola",
42375         "ao",
42376         "244"
42377       ],
42378       [
42379         "Anguilla",
42380         "ai",
42381         "1264"
42382       ],
42383       [
42384         "Antigua and Barbuda",
42385         "ag",
42386         "1268"
42387       ],
42388       [
42389         "Argentina",
42390         "ar",
42391         "54"
42392       ],
42393       [
42394         "Armenia (Հայաստան)",
42395         "am",
42396         "374"
42397       ],
42398       [
42399         "Aruba",
42400         "aw",
42401         "297"
42402       ],
42403       [
42404         "Australia",
42405         "au",
42406         "61",
42407         0
42408       ],
42409       [
42410         "Austria (Österreich)",
42411         "at",
42412         "43"
42413       ],
42414       [
42415         "Azerbaijan (Azərbaycan)",
42416         "az",
42417         "994"
42418       ],
42419       [
42420         "Bahamas",
42421         "bs",
42422         "1242"
42423       ],
42424       [
42425         "Bahrain (‫البحرين‬‎)",
42426         "bh",
42427         "973"
42428       ],
42429       [
42430         "Bangladesh (বাংলাদেশ)",
42431         "bd",
42432         "880"
42433       ],
42434       [
42435         "Barbados",
42436         "bb",
42437         "1246"
42438       ],
42439       [
42440         "Belarus (Беларусь)",
42441         "by",
42442         "375"
42443       ],
42444       [
42445         "Belgium (België)",
42446         "be",
42447         "32"
42448       ],
42449       [
42450         "Belize",
42451         "bz",
42452         "501"
42453       ],
42454       [
42455         "Benin (Bénin)",
42456         "bj",
42457         "229"
42458       ],
42459       [
42460         "Bermuda",
42461         "bm",
42462         "1441"
42463       ],
42464       [
42465         "Bhutan (འབྲུག)",
42466         "bt",
42467         "975"
42468       ],
42469       [
42470         "Bolivia",
42471         "bo",
42472         "591"
42473       ],
42474       [
42475         "Bosnia and Herzegovina (Босна и Херцеговина)",
42476         "ba",
42477         "387"
42478       ],
42479       [
42480         "Botswana",
42481         "bw",
42482         "267"
42483       ],
42484       [
42485         "Brazil (Brasil)",
42486         "br",
42487         "55"
42488       ],
42489       [
42490         "British Indian Ocean Territory",
42491         "io",
42492         "246"
42493       ],
42494       [
42495         "British Virgin Islands",
42496         "vg",
42497         "1284"
42498       ],
42499       [
42500         "Brunei",
42501         "bn",
42502         "673"
42503       ],
42504       [
42505         "Bulgaria (България)",
42506         "bg",
42507         "359"
42508       ],
42509       [
42510         "Burkina Faso",
42511         "bf",
42512         "226"
42513       ],
42514       [
42515         "Burundi (Uburundi)",
42516         "bi",
42517         "257"
42518       ],
42519       [
42520         "Cambodia (កម្ពុជា)",
42521         "kh",
42522         "855"
42523       ],
42524       [
42525         "Cameroon (Cameroun)",
42526         "cm",
42527         "237"
42528       ],
42529       [
42530         "Canada",
42531         "ca",
42532         "1",
42533         1,
42534         ["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"]
42535       ],
42536       [
42537         "Cape Verde (Kabu Verdi)",
42538         "cv",
42539         "238"
42540       ],
42541       [
42542         "Caribbean Netherlands",
42543         "bq",
42544         "599",
42545         1
42546       ],
42547       [
42548         "Cayman Islands",
42549         "ky",
42550         "1345"
42551       ],
42552       [
42553         "Central African Republic (République centrafricaine)",
42554         "cf",
42555         "236"
42556       ],
42557       [
42558         "Chad (Tchad)",
42559         "td",
42560         "235"
42561       ],
42562       [
42563         "Chile",
42564         "cl",
42565         "56"
42566       ],
42567       [
42568         "China (中国)",
42569         "cn",
42570         "86"
42571       ],
42572       [
42573         "Christmas Island",
42574         "cx",
42575         "61",
42576         2
42577       ],
42578       [
42579         "Cocos (Keeling) Islands",
42580         "cc",
42581         "61",
42582         1
42583       ],
42584       [
42585         "Colombia",
42586         "co",
42587         "57"
42588       ],
42589       [
42590         "Comoros (‫جزر القمر‬‎)",
42591         "km",
42592         "269"
42593       ],
42594       [
42595         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42596         "cd",
42597         "243"
42598       ],
42599       [
42600         "Congo (Republic) (Congo-Brazzaville)",
42601         "cg",
42602         "242"
42603       ],
42604       [
42605         "Cook Islands",
42606         "ck",
42607         "682"
42608       ],
42609       [
42610         "Costa Rica",
42611         "cr",
42612         "506"
42613       ],
42614       [
42615         "Côte d’Ivoire",
42616         "ci",
42617         "225"
42618       ],
42619       [
42620         "Croatia (Hrvatska)",
42621         "hr",
42622         "385"
42623       ],
42624       [
42625         "Cuba",
42626         "cu",
42627         "53"
42628       ],
42629       [
42630         "Curaçao",
42631         "cw",
42632         "599",
42633         0
42634       ],
42635       [
42636         "Cyprus (Κύπρος)",
42637         "cy",
42638         "357"
42639       ],
42640       [
42641         "Czech Republic (Česká republika)",
42642         "cz",
42643         "420"
42644       ],
42645       [
42646         "Denmark (Danmark)",
42647         "dk",
42648         "45"
42649       ],
42650       [
42651         "Djibouti",
42652         "dj",
42653         "253"
42654       ],
42655       [
42656         "Dominica",
42657         "dm",
42658         "1767"
42659       ],
42660       [
42661         "Dominican Republic (República Dominicana)",
42662         "do",
42663         "1",
42664         2,
42665         ["809", "829", "849"]
42666       ],
42667       [
42668         "Ecuador",
42669         "ec",
42670         "593"
42671       ],
42672       [
42673         "Egypt (‫مصر‬‎)",
42674         "eg",
42675         "20"
42676       ],
42677       [
42678         "El Salvador",
42679         "sv",
42680         "503"
42681       ],
42682       [
42683         "Equatorial Guinea (Guinea Ecuatorial)",
42684         "gq",
42685         "240"
42686       ],
42687       [
42688         "Eritrea",
42689         "er",
42690         "291"
42691       ],
42692       [
42693         "Estonia (Eesti)",
42694         "ee",
42695         "372"
42696       ],
42697       [
42698         "Ethiopia",
42699         "et",
42700         "251"
42701       ],
42702       [
42703         "Falkland Islands (Islas Malvinas)",
42704         "fk",
42705         "500"
42706       ],
42707       [
42708         "Faroe Islands (Føroyar)",
42709         "fo",
42710         "298"
42711       ],
42712       [
42713         "Fiji",
42714         "fj",
42715         "679"
42716       ],
42717       [
42718         "Finland (Suomi)",
42719         "fi",
42720         "358",
42721         0
42722       ],
42723       [
42724         "France",
42725         "fr",
42726         "33"
42727       ],
42728       [
42729         "French Guiana (Guyane française)",
42730         "gf",
42731         "594"
42732       ],
42733       [
42734         "French Polynesia (Polynésie française)",
42735         "pf",
42736         "689"
42737       ],
42738       [
42739         "Gabon",
42740         "ga",
42741         "241"
42742       ],
42743       [
42744         "Gambia",
42745         "gm",
42746         "220"
42747       ],
42748       [
42749         "Georgia (საქართველო)",
42750         "ge",
42751         "995"
42752       ],
42753       [
42754         "Germany (Deutschland)",
42755         "de",
42756         "49"
42757       ],
42758       [
42759         "Ghana (Gaana)",
42760         "gh",
42761         "233"
42762       ],
42763       [
42764         "Gibraltar",
42765         "gi",
42766         "350"
42767       ],
42768       [
42769         "Greece (Ελλάδα)",
42770         "gr",
42771         "30"
42772       ],
42773       [
42774         "Greenland (Kalaallit Nunaat)",
42775         "gl",
42776         "299"
42777       ],
42778       [
42779         "Grenada",
42780         "gd",
42781         "1473"
42782       ],
42783       [
42784         "Guadeloupe",
42785         "gp",
42786         "590",
42787         0
42788       ],
42789       [
42790         "Guam",
42791         "gu",
42792         "1671"
42793       ],
42794       [
42795         "Guatemala",
42796         "gt",
42797         "502"
42798       ],
42799       [
42800         "Guernsey",
42801         "gg",
42802         "44",
42803         1
42804       ],
42805       [
42806         "Guinea (Guinée)",
42807         "gn",
42808         "224"
42809       ],
42810       [
42811         "Guinea-Bissau (Guiné Bissau)",
42812         "gw",
42813         "245"
42814       ],
42815       [
42816         "Guyana",
42817         "gy",
42818         "592"
42819       ],
42820       [
42821         "Haiti",
42822         "ht",
42823         "509"
42824       ],
42825       [
42826         "Honduras",
42827         "hn",
42828         "504"
42829       ],
42830       [
42831         "Hong Kong (香港)",
42832         "hk",
42833         "852"
42834       ],
42835       [
42836         "Hungary (Magyarország)",
42837         "hu",
42838         "36"
42839       ],
42840       [
42841         "Iceland (Ísland)",
42842         "is",
42843         "354"
42844       ],
42845       [
42846         "India (भारत)",
42847         "in",
42848         "91"
42849       ],
42850       [
42851         "Indonesia",
42852         "id",
42853         "62"
42854       ],
42855       [
42856         "Iran (‫ایران‬‎)",
42857         "ir",
42858         "98"
42859       ],
42860       [
42861         "Iraq (‫العراق‬‎)",
42862         "iq",
42863         "964"
42864       ],
42865       [
42866         "Ireland",
42867         "ie",
42868         "353"
42869       ],
42870       [
42871         "Isle of Man",
42872         "im",
42873         "44",
42874         2
42875       ],
42876       [
42877         "Israel (‫ישראל‬‎)",
42878         "il",
42879         "972"
42880       ],
42881       [
42882         "Italy (Italia)",
42883         "it",
42884         "39",
42885         0
42886       ],
42887       [
42888         "Jamaica",
42889         "jm",
42890         "1876"
42891       ],
42892       [
42893         "Japan (日本)",
42894         "jp",
42895         "81"
42896       ],
42897       [
42898         "Jersey",
42899         "je",
42900         "44",
42901         3
42902       ],
42903       [
42904         "Jordan (‫الأردن‬‎)",
42905         "jo",
42906         "962"
42907       ],
42908       [
42909         "Kazakhstan (Казахстан)",
42910         "kz",
42911         "7",
42912         1
42913       ],
42914       [
42915         "Kenya",
42916         "ke",
42917         "254"
42918       ],
42919       [
42920         "Kiribati",
42921         "ki",
42922         "686"
42923       ],
42924       [
42925         "Kosovo",
42926         "xk",
42927         "383"
42928       ],
42929       [
42930         "Kuwait (‫الكويت‬‎)",
42931         "kw",
42932         "965"
42933       ],
42934       [
42935         "Kyrgyzstan (Кыргызстан)",
42936         "kg",
42937         "996"
42938       ],
42939       [
42940         "Laos (ລາວ)",
42941         "la",
42942         "856"
42943       ],
42944       [
42945         "Latvia (Latvija)",
42946         "lv",
42947         "371"
42948       ],
42949       [
42950         "Lebanon (‫لبنان‬‎)",
42951         "lb",
42952         "961"
42953       ],
42954       [
42955         "Lesotho",
42956         "ls",
42957         "266"
42958       ],
42959       [
42960         "Liberia",
42961         "lr",
42962         "231"
42963       ],
42964       [
42965         "Libya (‫ليبيا‬‎)",
42966         "ly",
42967         "218"
42968       ],
42969       [
42970         "Liechtenstein",
42971         "li",
42972         "423"
42973       ],
42974       [
42975         "Lithuania (Lietuva)",
42976         "lt",
42977         "370"
42978       ],
42979       [
42980         "Luxembourg",
42981         "lu",
42982         "352"
42983       ],
42984       [
42985         "Macau (澳門)",
42986         "mo",
42987         "853"
42988       ],
42989       [
42990         "Macedonia (FYROM) (Македонија)",
42991         "mk",
42992         "389"
42993       ],
42994       [
42995         "Madagascar (Madagasikara)",
42996         "mg",
42997         "261"
42998       ],
42999       [
43000         "Malawi",
43001         "mw",
43002         "265"
43003       ],
43004       [
43005         "Malaysia",
43006         "my",
43007         "60"
43008       ],
43009       [
43010         "Maldives",
43011         "mv",
43012         "960"
43013       ],
43014       [
43015         "Mali",
43016         "ml",
43017         "223"
43018       ],
43019       [
43020         "Malta",
43021         "mt",
43022         "356"
43023       ],
43024       [
43025         "Marshall Islands",
43026         "mh",
43027         "692"
43028       ],
43029       [
43030         "Martinique",
43031         "mq",
43032         "596"
43033       ],
43034       [
43035         "Mauritania (‫موريتانيا‬‎)",
43036         "mr",
43037         "222"
43038       ],
43039       [
43040         "Mauritius (Moris)",
43041         "mu",
43042         "230"
43043       ],
43044       [
43045         "Mayotte",
43046         "yt",
43047         "262",
43048         1
43049       ],
43050       [
43051         "Mexico (México)",
43052         "mx",
43053         "52"
43054       ],
43055       [
43056         "Micronesia",
43057         "fm",
43058         "691"
43059       ],
43060       [
43061         "Moldova (Republica Moldova)",
43062         "md",
43063         "373"
43064       ],
43065       [
43066         "Monaco",
43067         "mc",
43068         "377"
43069       ],
43070       [
43071         "Mongolia (Монгол)",
43072         "mn",
43073         "976"
43074       ],
43075       [
43076         "Montenegro (Crna Gora)",
43077         "me",
43078         "382"
43079       ],
43080       [
43081         "Montserrat",
43082         "ms",
43083         "1664"
43084       ],
43085       [
43086         "Morocco (‫المغرب‬‎)",
43087         "ma",
43088         "212",
43089         0
43090       ],
43091       [
43092         "Mozambique (Moçambique)",
43093         "mz",
43094         "258"
43095       ],
43096       [
43097         "Myanmar (Burma) (မြန်မာ)",
43098         "mm",
43099         "95"
43100       ],
43101       [
43102         "Namibia (Namibië)",
43103         "na",
43104         "264"
43105       ],
43106       [
43107         "Nauru",
43108         "nr",
43109         "674"
43110       ],
43111       [
43112         "Nepal (नेपाल)",
43113         "np",
43114         "977"
43115       ],
43116       [
43117         "Netherlands (Nederland)",
43118         "nl",
43119         "31"
43120       ],
43121       [
43122         "New Caledonia (Nouvelle-Calédonie)",
43123         "nc",
43124         "687"
43125       ],
43126       [
43127         "New Zealand",
43128         "nz",
43129         "64"
43130       ],
43131       [
43132         "Nicaragua",
43133         "ni",
43134         "505"
43135       ],
43136       [
43137         "Niger (Nijar)",
43138         "ne",
43139         "227"
43140       ],
43141       [
43142         "Nigeria",
43143         "ng",
43144         "234"
43145       ],
43146       [
43147         "Niue",
43148         "nu",
43149         "683"
43150       ],
43151       [
43152         "Norfolk Island",
43153         "nf",
43154         "672"
43155       ],
43156       [
43157         "North Korea (조선 민주주의 인민 공화국)",
43158         "kp",
43159         "850"
43160       ],
43161       [
43162         "Northern Mariana Islands",
43163         "mp",
43164         "1670"
43165       ],
43166       [
43167         "Norway (Norge)",
43168         "no",
43169         "47",
43170         0
43171       ],
43172       [
43173         "Oman (‫عُمان‬‎)",
43174         "om",
43175         "968"
43176       ],
43177       [
43178         "Pakistan (‫پاکستان‬‎)",
43179         "pk",
43180         "92"
43181       ],
43182       [
43183         "Palau",
43184         "pw",
43185         "680"
43186       ],
43187       [
43188         "Palestine (‫فلسطين‬‎)",
43189         "ps",
43190         "970"
43191       ],
43192       [
43193         "Panama (Panamá)",
43194         "pa",
43195         "507"
43196       ],
43197       [
43198         "Papua New Guinea",
43199         "pg",
43200         "675"
43201       ],
43202       [
43203         "Paraguay",
43204         "py",
43205         "595"
43206       ],
43207       [
43208         "Peru (Perú)",
43209         "pe",
43210         "51"
43211       ],
43212       [
43213         "Philippines",
43214         "ph",
43215         "63"
43216       ],
43217       [
43218         "Poland (Polska)",
43219         "pl",
43220         "48"
43221       ],
43222       [
43223         "Portugal",
43224         "pt",
43225         "351"
43226       ],
43227       [
43228         "Puerto Rico",
43229         "pr",
43230         "1",
43231         3,
43232         ["787", "939"]
43233       ],
43234       [
43235         "Qatar (‫قطر‬‎)",
43236         "qa",
43237         "974"
43238       ],
43239       [
43240         "Réunion (La Réunion)",
43241         "re",
43242         "262",
43243         0
43244       ],
43245       [
43246         "Romania (România)",
43247         "ro",
43248         "40"
43249       ],
43250       [
43251         "Russia (Россия)",
43252         "ru",
43253         "7",
43254         0
43255       ],
43256       [
43257         "Rwanda",
43258         "rw",
43259         "250"
43260       ],
43261       [
43262         "Saint Barthélemy",
43263         "bl",
43264         "590",
43265         1
43266       ],
43267       [
43268         "Saint Helena",
43269         "sh",
43270         "290"
43271       ],
43272       [
43273         "Saint Kitts and Nevis",
43274         "kn",
43275         "1869"
43276       ],
43277       [
43278         "Saint Lucia",
43279         "lc",
43280         "1758"
43281       ],
43282       [
43283         "Saint Martin (Saint-Martin (partie française))",
43284         "mf",
43285         "590",
43286         2
43287       ],
43288       [
43289         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43290         "pm",
43291         "508"
43292       ],
43293       [
43294         "Saint Vincent and the Grenadines",
43295         "vc",
43296         "1784"
43297       ],
43298       [
43299         "Samoa",
43300         "ws",
43301         "685"
43302       ],
43303       [
43304         "San Marino",
43305         "sm",
43306         "378"
43307       ],
43308       [
43309         "São Tomé and Príncipe (São Tomé e Príncipe)",
43310         "st",
43311         "239"
43312       ],
43313       [
43314         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43315         "sa",
43316         "966"
43317       ],
43318       [
43319         "Senegal (Sénégal)",
43320         "sn",
43321         "221"
43322       ],
43323       [
43324         "Serbia (Србија)",
43325         "rs",
43326         "381"
43327       ],
43328       [
43329         "Seychelles",
43330         "sc",
43331         "248"
43332       ],
43333       [
43334         "Sierra Leone",
43335         "sl",
43336         "232"
43337       ],
43338       [
43339         "Singapore",
43340         "sg",
43341         "65"
43342       ],
43343       [
43344         "Sint Maarten",
43345         "sx",
43346         "1721"
43347       ],
43348       [
43349         "Slovakia (Slovensko)",
43350         "sk",
43351         "421"
43352       ],
43353       [
43354         "Slovenia (Slovenija)",
43355         "si",
43356         "386"
43357       ],
43358       [
43359         "Solomon Islands",
43360         "sb",
43361         "677"
43362       ],
43363       [
43364         "Somalia (Soomaaliya)",
43365         "so",
43366         "252"
43367       ],
43368       [
43369         "South Africa",
43370         "za",
43371         "27"
43372       ],
43373       [
43374         "South Korea (대한민국)",
43375         "kr",
43376         "82"
43377       ],
43378       [
43379         "South Sudan (‫جنوب السودان‬‎)",
43380         "ss",
43381         "211"
43382       ],
43383       [
43384         "Spain (España)",
43385         "es",
43386         "34"
43387       ],
43388       [
43389         "Sri Lanka (ශ්‍රී ලංකාව)",
43390         "lk",
43391         "94"
43392       ],
43393       [
43394         "Sudan (‫السودان‬‎)",
43395         "sd",
43396         "249"
43397       ],
43398       [
43399         "Suriname",
43400         "sr",
43401         "597"
43402       ],
43403       [
43404         "Svalbard and Jan Mayen",
43405         "sj",
43406         "47",
43407         1
43408       ],
43409       [
43410         "Swaziland",
43411         "sz",
43412         "268"
43413       ],
43414       [
43415         "Sweden (Sverige)",
43416         "se",
43417         "46"
43418       ],
43419       [
43420         "Switzerland (Schweiz)",
43421         "ch",
43422         "41"
43423       ],
43424       [
43425         "Syria (‫سوريا‬‎)",
43426         "sy",
43427         "963"
43428       ],
43429       [
43430         "Taiwan (台灣)",
43431         "tw",
43432         "886"
43433       ],
43434       [
43435         "Tajikistan",
43436         "tj",
43437         "992"
43438       ],
43439       [
43440         "Tanzania",
43441         "tz",
43442         "255"
43443       ],
43444       [
43445         "Thailand (ไทย)",
43446         "th",
43447         "66"
43448       ],
43449       [
43450         "Timor-Leste",
43451         "tl",
43452         "670"
43453       ],
43454       [
43455         "Togo",
43456         "tg",
43457         "228"
43458       ],
43459       [
43460         "Tokelau",
43461         "tk",
43462         "690"
43463       ],
43464       [
43465         "Tonga",
43466         "to",
43467         "676"
43468       ],
43469       [
43470         "Trinidad and Tobago",
43471         "tt",
43472         "1868"
43473       ],
43474       [
43475         "Tunisia (‫تونس‬‎)",
43476         "tn",
43477         "216"
43478       ],
43479       [
43480         "Turkey (Türkiye)",
43481         "tr",
43482         "90"
43483       ],
43484       [
43485         "Turkmenistan",
43486         "tm",
43487         "993"
43488       ],
43489       [
43490         "Turks and Caicos Islands",
43491         "tc",
43492         "1649"
43493       ],
43494       [
43495         "Tuvalu",
43496         "tv",
43497         "688"
43498       ],
43499       [
43500         "U.S. Virgin Islands",
43501         "vi",
43502         "1340"
43503       ],
43504       [
43505         "Uganda",
43506         "ug",
43507         "256"
43508       ],
43509       [
43510         "Ukraine (Україна)",
43511         "ua",
43512         "380"
43513       ],
43514       [
43515         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43516         "ae",
43517         "971"
43518       ],
43519       [
43520         "United Kingdom",
43521         "gb",
43522         "44",
43523         0
43524       ],
43525       [
43526         "United States",
43527         "us",
43528         "1",
43529         0
43530       ],
43531       [
43532         "Uruguay",
43533         "uy",
43534         "598"
43535       ],
43536       [
43537         "Uzbekistan (Oʻzbekiston)",
43538         "uz",
43539         "998"
43540       ],
43541       [
43542         "Vanuatu",
43543         "vu",
43544         "678"
43545       ],
43546       [
43547         "Vatican City (Città del Vaticano)",
43548         "va",
43549         "39",
43550         1
43551       ],
43552       [
43553         "Venezuela",
43554         "ve",
43555         "58"
43556       ],
43557       [
43558         "Vietnam (Việt Nam)",
43559         "vn",
43560         "84"
43561       ],
43562       [
43563         "Wallis and Futuna (Wallis-et-Futuna)",
43564         "wf",
43565         "681"
43566       ],
43567       [
43568         "Western Sahara (‫الصحراء الغربية‬‎)",
43569         "eh",
43570         "212",
43571         1
43572       ],
43573       [
43574         "Yemen (‫اليمن‬‎)",
43575         "ye",
43576         "967"
43577       ],
43578       [
43579         "Zambia",
43580         "zm",
43581         "260"
43582       ],
43583       [
43584         "Zimbabwe",
43585         "zw",
43586         "263"
43587       ],
43588       [
43589         "Åland Islands",
43590         "ax",
43591         "358",
43592         1
43593       ]
43594   ];
43595   
43596   return d;
43597 }/**
43598 *    This script refer to:
43599 *    Title: International Telephone Input
43600 *    Author: Jack O'Connor
43601 *    Code version:  v12.1.12
43602 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43603 **/
43604
43605 /**
43606  * @class Roo.bootstrap.PhoneInput
43607  * @extends Roo.bootstrap.TriggerField
43608  * An input with International dial-code selection
43609  
43610  * @cfg {String} defaultDialCode default '+852'
43611  * @cfg {Array} preferedCountries default []
43612   
43613  * @constructor
43614  * Create a new PhoneInput.
43615  * @param {Object} config Configuration options
43616  */
43617
43618 Roo.bootstrap.PhoneInput = function(config) {
43619     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43620 };
43621
43622 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43623         
43624         listWidth: undefined,
43625         
43626         selectedClass: 'active',
43627         
43628         invalidClass : "has-warning",
43629         
43630         validClass: 'has-success',
43631         
43632         allowed: '0123456789',
43633         
43634         max_length: 15,
43635         
43636         /**
43637          * @cfg {String} defaultDialCode The default dial code when initializing the input
43638          */
43639         defaultDialCode: '+852',
43640         
43641         /**
43642          * @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
43643          */
43644         preferedCountries: false,
43645         
43646         getAutoCreate : function()
43647         {
43648             var data = Roo.bootstrap.PhoneInputData();
43649             var align = this.labelAlign || this.parentLabelAlign();
43650             var id = Roo.id();
43651             
43652             this.allCountries = [];
43653             this.dialCodeMapping = [];
43654             
43655             for (var i = 0; i < data.length; i++) {
43656               var c = data[i];
43657               this.allCountries[i] = {
43658                 name: c[0],
43659                 iso2: c[1],
43660                 dialCode: c[2],
43661                 priority: c[3] || 0,
43662                 areaCodes: c[4] || null
43663               };
43664               this.dialCodeMapping[c[2]] = {
43665                   name: c[0],
43666                   iso2: c[1],
43667                   priority: c[3] || 0,
43668                   areaCodes: c[4] || null
43669               };
43670             }
43671             
43672             var cfg = {
43673                 cls: 'form-group',
43674                 cn: []
43675             };
43676             
43677             var input =  {
43678                 tag: 'input',
43679                 id : id,
43680                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43681                 maxlength: this.max_length,
43682                 cls : 'form-control tel-input',
43683                 autocomplete: 'new-password'
43684             };
43685             
43686             var hiddenInput = {
43687                 tag: 'input',
43688                 type: 'hidden',
43689                 cls: 'hidden-tel-input'
43690             };
43691             
43692             if (this.name) {
43693                 hiddenInput.name = this.name;
43694             }
43695             
43696             if (this.disabled) {
43697                 input.disabled = true;
43698             }
43699             
43700             var flag_container = {
43701                 tag: 'div',
43702                 cls: 'flag-box',
43703                 cn: [
43704                     {
43705                         tag: 'div',
43706                         cls: 'flag'
43707                     },
43708                     {
43709                         tag: 'div',
43710                         cls: 'caret'
43711                     }
43712                 ]
43713             };
43714             
43715             var box = {
43716                 tag: 'div',
43717                 cls: this.hasFeedback ? 'has-feedback' : '',
43718                 cn: [
43719                     hiddenInput,
43720                     input,
43721                     {
43722                         tag: 'input',
43723                         cls: 'dial-code-holder',
43724                         disabled: true
43725                     }
43726                 ]
43727             };
43728             
43729             var container = {
43730                 cls: 'roo-select2-container input-group',
43731                 cn: [
43732                     flag_container,
43733                     box
43734                 ]
43735             };
43736             
43737             if (this.fieldLabel.length) {
43738                 var indicator = {
43739                     tag: 'i',
43740                     tooltip: 'This field is required'
43741                 };
43742                 
43743                 var label = {
43744                     tag: 'label',
43745                     'for':  id,
43746                     cls: 'control-label',
43747                     cn: []
43748                 };
43749                 
43750                 var label_text = {
43751                     tag: 'span',
43752                     html: this.fieldLabel
43753                 };
43754                 
43755                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43756                 label.cn = [
43757                     indicator,
43758                     label_text
43759                 ];
43760                 
43761                 if(this.indicatorpos == 'right') {
43762                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43763                     label.cn = [
43764                         label_text,
43765                         indicator
43766                     ];
43767                 }
43768                 
43769                 if(align == 'left') {
43770                     container = {
43771                         tag: 'div',
43772                         cn: [
43773                             container
43774                         ]
43775                     };
43776                     
43777                     if(this.labelWidth > 12){
43778                         label.style = "width: " + this.labelWidth + 'px';
43779                     }
43780                     if(this.labelWidth < 13 && this.labelmd == 0){
43781                         this.labelmd = this.labelWidth;
43782                     }
43783                     if(this.labellg > 0){
43784                         label.cls += ' col-lg-' + this.labellg;
43785                         input.cls += ' col-lg-' + (12 - this.labellg);
43786                     }
43787                     if(this.labelmd > 0){
43788                         label.cls += ' col-md-' + this.labelmd;
43789                         container.cls += ' col-md-' + (12 - this.labelmd);
43790                     }
43791                     if(this.labelsm > 0){
43792                         label.cls += ' col-sm-' + this.labelsm;
43793                         container.cls += ' col-sm-' + (12 - this.labelsm);
43794                     }
43795                     if(this.labelxs > 0){
43796                         label.cls += ' col-xs-' + this.labelxs;
43797                         container.cls += ' col-xs-' + (12 - this.labelxs);
43798                     }
43799                 }
43800             }
43801             
43802             cfg.cn = [
43803                 label,
43804                 container
43805             ];
43806             
43807             var settings = this;
43808             
43809             ['xs','sm','md','lg'].map(function(size){
43810                 if (settings[size]) {
43811                     cfg.cls += ' col-' + size + '-' + settings[size];
43812                 }
43813             });
43814             
43815             this.store = new Roo.data.Store({
43816                 proxy : new Roo.data.MemoryProxy({}),
43817                 reader : new Roo.data.JsonReader({
43818                     fields : [
43819                         {
43820                             'name' : 'name',
43821                             'type' : 'string'
43822                         },
43823                         {
43824                             'name' : 'iso2',
43825                             'type' : 'string'
43826                         },
43827                         {
43828                             'name' : 'dialCode',
43829                             'type' : 'string'
43830                         },
43831                         {
43832                             'name' : 'priority',
43833                             'type' : 'string'
43834                         },
43835                         {
43836                             'name' : 'areaCodes',
43837                             'type' : 'string'
43838                         }
43839                     ]
43840                 })
43841             });
43842             
43843             if(!this.preferedCountries) {
43844                 this.preferedCountries = [
43845                     'hk',
43846                     'gb',
43847                     'us'
43848                 ];
43849             }
43850             
43851             var p = this.preferedCountries.reverse();
43852             
43853             if(p) {
43854                 for (var i = 0; i < p.length; i++) {
43855                     for (var j = 0; j < this.allCountries.length; j++) {
43856                         if(this.allCountries[j].iso2 == p[i]) {
43857                             var t = this.allCountries[j];
43858                             this.allCountries.splice(j,1);
43859                             this.allCountries.unshift(t);
43860                         }
43861                     } 
43862                 }
43863             }
43864             
43865             this.store.proxy.data = {
43866                 success: true,
43867                 data: this.allCountries
43868             };
43869             
43870             return cfg;
43871         },
43872         
43873         initEvents : function()
43874         {
43875             this.createList();
43876             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43877             
43878             this.indicator = this.indicatorEl();
43879             this.flag = this.flagEl();
43880             this.dialCodeHolder = this.dialCodeHolderEl();
43881             
43882             this.trigger = this.el.select('div.flag-box',true).first();
43883             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43884             
43885             var _this = this;
43886             
43887             (function(){
43888                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43889                 _this.list.setWidth(lw);
43890             }).defer(100);
43891             
43892             this.list.on('mouseover', this.onViewOver, this);
43893             this.list.on('mousemove', this.onViewMove, this);
43894             this.inputEl().on("keyup", this.onKeyUp, this);
43895             this.inputEl().on("keypress", this.onKeyPress, this);
43896             
43897             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43898
43899             this.view = new Roo.View(this.list, this.tpl, {
43900                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43901             });
43902             
43903             this.view.on('click', this.onViewClick, this);
43904             this.setValue(this.defaultDialCode);
43905         },
43906         
43907         onTriggerClick : function(e)
43908         {
43909             Roo.log('trigger click');
43910             if(this.disabled){
43911                 return;
43912             }
43913             
43914             if(this.isExpanded()){
43915                 this.collapse();
43916                 this.hasFocus = false;
43917             }else {
43918                 this.store.load({});
43919                 this.hasFocus = true;
43920                 this.expand();
43921             }
43922         },
43923         
43924         isExpanded : function()
43925         {
43926             return this.list.isVisible();
43927         },
43928         
43929         collapse : function()
43930         {
43931             if(!this.isExpanded()){
43932                 return;
43933             }
43934             this.list.hide();
43935             Roo.get(document).un('mousedown', this.collapseIf, this);
43936             Roo.get(document).un('mousewheel', this.collapseIf, this);
43937             this.fireEvent('collapse', this);
43938             this.validate();
43939         },
43940         
43941         expand : function()
43942         {
43943             Roo.log('expand');
43944
43945             if(this.isExpanded() || !this.hasFocus){
43946                 return;
43947             }
43948             
43949             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43950             this.list.setWidth(lw);
43951             
43952             this.list.show();
43953             this.restrictHeight();
43954             
43955             Roo.get(document).on('mousedown', this.collapseIf, this);
43956             Roo.get(document).on('mousewheel', this.collapseIf, this);
43957             
43958             this.fireEvent('expand', this);
43959         },
43960         
43961         restrictHeight : function()
43962         {
43963             this.list.alignTo(this.inputEl(), this.listAlign);
43964             this.list.alignTo(this.inputEl(), this.listAlign);
43965         },
43966         
43967         onViewOver : function(e, t)
43968         {
43969             if(this.inKeyMode){
43970                 return;
43971             }
43972             var item = this.view.findItemFromChild(t);
43973             
43974             if(item){
43975                 var index = this.view.indexOf(item);
43976                 this.select(index, false);
43977             }
43978         },
43979
43980         // private
43981         onViewClick : function(view, doFocus, el, e)
43982         {
43983             var index = this.view.getSelectedIndexes()[0];
43984             
43985             var r = this.store.getAt(index);
43986             
43987             if(r){
43988                 this.onSelect(r, index);
43989             }
43990             if(doFocus !== false && !this.blockFocus){
43991                 this.inputEl().focus();
43992             }
43993         },
43994         
43995         onViewMove : function(e, t)
43996         {
43997             this.inKeyMode = false;
43998         },
43999         
44000         select : function(index, scrollIntoView)
44001         {
44002             this.selectedIndex = index;
44003             this.view.select(index);
44004             if(scrollIntoView !== false){
44005                 var el = this.view.getNode(index);
44006                 if(el){
44007                     this.list.scrollChildIntoView(el, false);
44008                 }
44009             }
44010         },
44011         
44012         createList : function()
44013         {
44014             this.list = Roo.get(document.body).createChild({
44015                 tag: 'ul',
44016                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44017                 style: 'display:none'
44018             });
44019             
44020             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44021         },
44022         
44023         collapseIf : function(e)
44024         {
44025             var in_combo  = e.within(this.el);
44026             var in_list =  e.within(this.list);
44027             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44028             
44029             if (in_combo || in_list || is_list) {
44030                 return;
44031             }
44032             this.collapse();
44033         },
44034         
44035         onSelect : function(record, index)
44036         {
44037             if(this.fireEvent('beforeselect', this, record, index) !== false){
44038                 
44039                 this.setFlagClass(record.data.iso2);
44040                 this.setDialCode(record.data.dialCode);
44041                 this.hasFocus = false;
44042                 this.collapse();
44043                 this.fireEvent('select', this, record, index);
44044             }
44045         },
44046         
44047         flagEl : function()
44048         {
44049             var flag = this.el.select('div.flag',true).first();
44050             if(!flag){
44051                 return false;
44052             }
44053             return flag;
44054         },
44055         
44056         dialCodeHolderEl : function()
44057         {
44058             var d = this.el.select('input.dial-code-holder',true).first();
44059             if(!d){
44060                 return false;
44061             }
44062             return d;
44063         },
44064         
44065         setDialCode : function(v)
44066         {
44067             this.dialCodeHolder.dom.value = '+'+v;
44068         },
44069         
44070         setFlagClass : function(n)
44071         {
44072             this.flag.dom.className = 'flag '+n;
44073         },
44074         
44075         getValue : function()
44076         {
44077             var v = this.inputEl().getValue();
44078             if(this.dialCodeHolder) {
44079                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44080             }
44081             return v;
44082         },
44083         
44084         setValue : function(v)
44085         {
44086             var d = this.getDialCode(v);
44087             
44088             //invalid dial code
44089             if(v.length == 0 || !d || d.length == 0) {
44090                 if(this.rendered){
44091                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44092                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44093                 }
44094                 return;
44095             }
44096             
44097             //valid dial code
44098             this.setFlagClass(this.dialCodeMapping[d].iso2);
44099             this.setDialCode(d);
44100             this.inputEl().dom.value = v.replace('+'+d,'');
44101             this.hiddenEl().dom.value = this.getValue();
44102             
44103             this.validate();
44104         },
44105         
44106         getDialCode : function(v)
44107         {
44108             v = v ||  '';
44109             
44110             if (v.length == 0) {
44111                 return this.dialCodeHolder.dom.value;
44112             }
44113             
44114             var dialCode = "";
44115             if (v.charAt(0) != "+") {
44116                 return false;
44117             }
44118             var numericChars = "";
44119             for (var i = 1; i < v.length; i++) {
44120               var c = v.charAt(i);
44121               if (!isNaN(c)) {
44122                 numericChars += c;
44123                 if (this.dialCodeMapping[numericChars]) {
44124                   dialCode = v.substr(1, i);
44125                 }
44126                 if (numericChars.length == 4) {
44127                   break;
44128                 }
44129               }
44130             }
44131             return dialCode;
44132         },
44133         
44134         reset : function()
44135         {
44136             this.setValue(this.defaultDialCode);
44137             this.validate();
44138         },
44139         
44140         hiddenEl : function()
44141         {
44142             return this.el.select('input.hidden-tel-input',true).first();
44143         },
44144         
44145         // after setting val
44146         onKeyUp : function(e){
44147             this.setValue(this.getValue());
44148         },
44149         
44150         onKeyPress : function(e){
44151             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44152                 e.stopEvent();
44153             }
44154         }
44155         
44156 });
44157 /**
44158  * @class Roo.bootstrap.MoneyField
44159  * @extends Roo.bootstrap.ComboBox
44160  * Bootstrap MoneyField class
44161  * 
44162  * @constructor
44163  * Create a new MoneyField.
44164  * @param {Object} config Configuration options
44165  */
44166
44167 Roo.bootstrap.MoneyField = function(config) {
44168     
44169     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44170     
44171 };
44172
44173 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44174     
44175     /**
44176      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44177      */
44178     allowDecimals : true,
44179     /**
44180      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44181      */
44182     decimalSeparator : ".",
44183     /**
44184      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44185      */
44186     decimalPrecision : 0,
44187     /**
44188      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44189      */
44190     allowNegative : true,
44191     /**
44192      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44193      */
44194     allowZero: true,
44195     /**
44196      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44197      */
44198     minValue : Number.NEGATIVE_INFINITY,
44199     /**
44200      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44201      */
44202     maxValue : Number.MAX_VALUE,
44203     /**
44204      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44205      */
44206     minText : "The minimum value for this field is {0}",
44207     /**
44208      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44209      */
44210     maxText : "The maximum value for this field is {0}",
44211     /**
44212      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44213      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44214      */
44215     nanText : "{0} is not a valid number",
44216     /**
44217      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44218      */
44219     castInt : true,
44220     /**
44221      * @cfg {String} defaults currency of the MoneyField
44222      * value should be in lkey
44223      */
44224     defaultCurrency : false,
44225     /**
44226      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44227      */
44228     thousandsDelimiter : false,
44229     /**
44230      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44231      */
44232     max_length: false,
44233     
44234     inputlg : 9,
44235     inputmd : 9,
44236     inputsm : 9,
44237     inputxs : 6,
44238     
44239     store : false,
44240     
44241     getAutoCreate : function()
44242     {
44243         var align = this.labelAlign || this.parentLabelAlign();
44244         
44245         var id = Roo.id();
44246
44247         var cfg = {
44248             cls: 'form-group',
44249             cn: []
44250         };
44251
44252         var input =  {
44253             tag: 'input',
44254             id : id,
44255             cls : 'form-control roo-money-amount-input',
44256             autocomplete: 'new-password'
44257         };
44258         
44259         var hiddenInput = {
44260             tag: 'input',
44261             type: 'hidden',
44262             id: Roo.id(),
44263             cls: 'hidden-number-input'
44264         };
44265         
44266         if(this.max_length) {
44267             input.maxlength = this.max_length; 
44268         }
44269         
44270         if (this.name) {
44271             hiddenInput.name = this.name;
44272         }
44273
44274         if (this.disabled) {
44275             input.disabled = true;
44276         }
44277
44278         var clg = 12 - this.inputlg;
44279         var cmd = 12 - this.inputmd;
44280         var csm = 12 - this.inputsm;
44281         var cxs = 12 - this.inputxs;
44282         
44283         var container = {
44284             tag : 'div',
44285             cls : 'row roo-money-field',
44286             cn : [
44287                 {
44288                     tag : 'div',
44289                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44290                     cn : [
44291                         {
44292                             tag : 'div',
44293                             cls: 'roo-select2-container input-group',
44294                             cn: [
44295                                 {
44296                                     tag : 'input',
44297                                     cls : 'form-control roo-money-currency-input',
44298                                     autocomplete: 'new-password',
44299                                     readOnly : 1,
44300                                     name : this.currencyName
44301                                 },
44302                                 {
44303                                     tag :'span',
44304                                     cls : 'input-group-addon',
44305                                     cn : [
44306                                         {
44307                                             tag: 'span',
44308                                             cls: 'caret'
44309                                         }
44310                                     ]
44311                                 }
44312                             ]
44313                         }
44314                     ]
44315                 },
44316                 {
44317                     tag : 'div',
44318                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44319                     cn : [
44320                         {
44321                             tag: 'div',
44322                             cls: this.hasFeedback ? 'has-feedback' : '',
44323                             cn: [
44324                                 input
44325                             ]
44326                         }
44327                     ]
44328                 }
44329             ]
44330             
44331         };
44332         
44333         if (this.fieldLabel.length) {
44334             var indicator = {
44335                 tag: 'i',
44336                 tooltip: 'This field is required'
44337             };
44338
44339             var label = {
44340                 tag: 'label',
44341                 'for':  id,
44342                 cls: 'control-label',
44343                 cn: []
44344             };
44345
44346             var label_text = {
44347                 tag: 'span',
44348                 html: this.fieldLabel
44349             };
44350
44351             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44352             label.cn = [
44353                 indicator,
44354                 label_text
44355             ];
44356
44357             if(this.indicatorpos == 'right') {
44358                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44359                 label.cn = [
44360                     label_text,
44361                     indicator
44362                 ];
44363             }
44364
44365             if(align == 'left') {
44366                 container = {
44367                     tag: 'div',
44368                     cn: [
44369                         container
44370                     ]
44371                 };
44372
44373                 if(this.labelWidth > 12){
44374                     label.style = "width: " + this.labelWidth + 'px';
44375                 }
44376                 if(this.labelWidth < 13 && this.labelmd == 0){
44377                     this.labelmd = this.labelWidth;
44378                 }
44379                 if(this.labellg > 0){
44380                     label.cls += ' col-lg-' + this.labellg;
44381                     input.cls += ' col-lg-' + (12 - this.labellg);
44382                 }
44383                 if(this.labelmd > 0){
44384                     label.cls += ' col-md-' + this.labelmd;
44385                     container.cls += ' col-md-' + (12 - this.labelmd);
44386                 }
44387                 if(this.labelsm > 0){
44388                     label.cls += ' col-sm-' + this.labelsm;
44389                     container.cls += ' col-sm-' + (12 - this.labelsm);
44390                 }
44391                 if(this.labelxs > 0){
44392                     label.cls += ' col-xs-' + this.labelxs;
44393                     container.cls += ' col-xs-' + (12 - this.labelxs);
44394                 }
44395             }
44396         }
44397
44398         cfg.cn = [
44399             label,
44400             container,
44401             hiddenInput
44402         ];
44403         
44404         var settings = this;
44405
44406         ['xs','sm','md','lg'].map(function(size){
44407             if (settings[size]) {
44408                 cfg.cls += ' col-' + size + '-' + settings[size];
44409             }
44410         });
44411         
44412         return cfg;
44413     },
44414     
44415     initEvents : function()
44416     {
44417         this.indicator = this.indicatorEl();
44418         
44419         this.initCurrencyEvent();
44420         
44421         this.initNumberEvent();
44422     },
44423     
44424     initCurrencyEvent : function()
44425     {
44426         if (!this.store) {
44427             throw "can not find store for combo";
44428         }
44429         
44430         this.store = Roo.factory(this.store, Roo.data);
44431         this.store.parent = this;
44432         
44433         this.createList();
44434         
44435         this.triggerEl = this.el.select('.input-group-addon', true).first();
44436         
44437         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44438         
44439         var _this = this;
44440         
44441         (function(){
44442             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44443             _this.list.setWidth(lw);
44444         }).defer(100);
44445         
44446         this.list.on('mouseover', this.onViewOver, this);
44447         this.list.on('mousemove', this.onViewMove, this);
44448         this.list.on('scroll', this.onViewScroll, this);
44449         
44450         if(!this.tpl){
44451             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44452         }
44453         
44454         this.view = new Roo.View(this.list, this.tpl, {
44455             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44456         });
44457         
44458         this.view.on('click', this.onViewClick, this);
44459         
44460         this.store.on('beforeload', this.onBeforeLoad, this);
44461         this.store.on('load', this.onLoad, this);
44462         this.store.on('loadexception', this.onLoadException, this);
44463         
44464         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44465             "up" : function(e){
44466                 this.inKeyMode = true;
44467                 this.selectPrev();
44468             },
44469
44470             "down" : function(e){
44471                 if(!this.isExpanded()){
44472                     this.onTriggerClick();
44473                 }else{
44474                     this.inKeyMode = true;
44475                     this.selectNext();
44476                 }
44477             },
44478
44479             "enter" : function(e){
44480                 this.collapse();
44481                 
44482                 if(this.fireEvent("specialkey", this, e)){
44483                     this.onViewClick(false);
44484                 }
44485                 
44486                 return true;
44487             },
44488
44489             "esc" : function(e){
44490                 this.collapse();
44491             },
44492
44493             "tab" : function(e){
44494                 this.collapse();
44495                 
44496                 if(this.fireEvent("specialkey", this, e)){
44497                     this.onViewClick(false);
44498                 }
44499                 
44500                 return true;
44501             },
44502
44503             scope : this,
44504
44505             doRelay : function(foo, bar, hname){
44506                 if(hname == 'down' || this.scope.isExpanded()){
44507                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44508                 }
44509                 return true;
44510             },
44511
44512             forceKeyDown: true
44513         });
44514         
44515         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44516         
44517     },
44518     
44519     initNumberEvent : function(e)
44520     {
44521         this.inputEl().on("keydown" , this.fireKey,  this);
44522         this.inputEl().on("focus", this.onFocus,  this);
44523         this.inputEl().on("blur", this.onBlur,  this);
44524         
44525         this.inputEl().relayEvent('keyup', this);
44526         
44527         if(this.indicator){
44528             this.indicator.addClass('invisible');
44529         }
44530  
44531         this.originalValue = this.getValue();
44532         
44533         if(this.validationEvent == 'keyup'){
44534             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44535             this.inputEl().on('keyup', this.filterValidation, this);
44536         }
44537         else if(this.validationEvent !== false){
44538             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44539         }
44540         
44541         if(this.selectOnFocus){
44542             this.on("focus", this.preFocus, this);
44543             
44544         }
44545         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44546             this.inputEl().on("keypress", this.filterKeys, this);
44547         } else {
44548             this.inputEl().relayEvent('keypress', this);
44549         }
44550         
44551         var allowed = "0123456789";
44552         
44553         if(this.allowDecimals){
44554             allowed += this.decimalSeparator;
44555         }
44556         
44557         if(this.allowNegative){
44558             allowed += "-";
44559         }
44560         
44561         if(this.thousandsDelimiter) {
44562             allowed += ",";
44563         }
44564         
44565         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44566         
44567         var keyPress = function(e){
44568             
44569             var k = e.getKey();
44570             
44571             var c = e.getCharCode();
44572             
44573             if(
44574                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44575                     allowed.indexOf(String.fromCharCode(c)) === -1
44576             ){
44577                 e.stopEvent();
44578                 return;
44579             }
44580             
44581             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44582                 return;
44583             }
44584             
44585             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44586                 e.stopEvent();
44587             }
44588         };
44589         
44590         this.inputEl().on("keypress", keyPress, this);
44591         
44592     },
44593     
44594     onTriggerClick : function(e)
44595     {   
44596         if(this.disabled){
44597             return;
44598         }
44599         
44600         this.page = 0;
44601         this.loadNext = false;
44602         
44603         if(this.isExpanded()){
44604             this.collapse();
44605             return;
44606         }
44607         
44608         this.hasFocus = true;
44609         
44610         if(this.triggerAction == 'all') {
44611             this.doQuery(this.allQuery, true);
44612             return;
44613         }
44614         
44615         this.doQuery(this.getRawValue());
44616     },
44617     
44618     getCurrency : function()
44619     {   
44620         var v = this.currencyEl().getValue();
44621         
44622         return v;
44623     },
44624     
44625     restrictHeight : function()
44626     {
44627         this.list.alignTo(this.currencyEl(), this.listAlign);
44628         this.list.alignTo(this.currencyEl(), this.listAlign);
44629     },
44630     
44631     onViewClick : function(view, doFocus, el, e)
44632     {
44633         var index = this.view.getSelectedIndexes()[0];
44634         
44635         var r = this.store.getAt(index);
44636         
44637         if(r){
44638             this.onSelect(r, index);
44639         }
44640     },
44641     
44642     onSelect : function(record, index){
44643         
44644         if(this.fireEvent('beforeselect', this, record, index) !== false){
44645         
44646             this.setFromCurrencyData(index > -1 ? record.data : false);
44647             
44648             this.collapse();
44649             
44650             this.fireEvent('select', this, record, index);
44651         }
44652     },
44653     
44654     setFromCurrencyData : function(o)
44655     {
44656         var currency = '';
44657         
44658         this.lastCurrency = o;
44659         
44660         if (this.currencyField) {
44661             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44662         } else {
44663             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44664         }
44665         
44666         this.lastSelectionText = currency;
44667         
44668         //setting default currency
44669         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44670             this.setCurrency(this.defaultCurrency);
44671             return;
44672         }
44673         
44674         this.setCurrency(currency);
44675     },
44676     
44677     setFromData : function(o)
44678     {
44679         var c = {};
44680         
44681         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44682         
44683         this.setFromCurrencyData(c);
44684         
44685         var value = '';
44686         
44687         if (this.name) {
44688             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44689         } else {
44690             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44691         }
44692         
44693         this.setValue(value);
44694         
44695     },
44696     
44697     setCurrency : function(v)
44698     {   
44699         this.currencyValue = v;
44700         
44701         if(this.rendered){
44702             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44703             this.validate();
44704         }
44705     },
44706     
44707     setValue : function(v)
44708     {
44709         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44710         
44711         this.value = v;
44712         
44713         if(this.rendered){
44714             
44715             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44716             
44717             this.inputEl().dom.value = (v == '') ? '' :
44718                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44719             
44720             if(!this.allowZero && v === '0') {
44721                 this.hiddenEl().dom.value = '';
44722                 this.inputEl().dom.value = '';
44723             }
44724             
44725             this.validate();
44726         }
44727     },
44728     
44729     getRawValue : function()
44730     {
44731         var v = this.inputEl().getValue();
44732         
44733         return v;
44734     },
44735     
44736     getValue : function()
44737     {
44738         return this.fixPrecision(this.parseValue(this.getRawValue()));
44739     },
44740     
44741     parseValue : function(value)
44742     {
44743         if(this.thousandsDelimiter) {
44744             value += "";
44745             r = new RegExp(",", "g");
44746             value = value.replace(r, "");
44747         }
44748         
44749         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44750         return isNaN(value) ? '' : value;
44751         
44752     },
44753     
44754     fixPrecision : function(value)
44755     {
44756         if(this.thousandsDelimiter) {
44757             value += "";
44758             r = new RegExp(",", "g");
44759             value = value.replace(r, "");
44760         }
44761         
44762         var nan = isNaN(value);
44763         
44764         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44765             return nan ? '' : value;
44766         }
44767         return parseFloat(value).toFixed(this.decimalPrecision);
44768     },
44769     
44770     decimalPrecisionFcn : function(v)
44771     {
44772         return Math.floor(v);
44773     },
44774     
44775     validateValue : function(value)
44776     {
44777         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44778             return false;
44779         }
44780         
44781         var num = this.parseValue(value);
44782         
44783         if(isNaN(num)){
44784             this.markInvalid(String.format(this.nanText, value));
44785             return false;
44786         }
44787         
44788         if(num < this.minValue){
44789             this.markInvalid(String.format(this.minText, this.minValue));
44790             return false;
44791         }
44792         
44793         if(num > this.maxValue){
44794             this.markInvalid(String.format(this.maxText, this.maxValue));
44795             return false;
44796         }
44797         
44798         return true;
44799     },
44800     
44801     validate : function()
44802     {
44803         if(this.disabled || this.allowBlank){
44804             this.markValid();
44805             return true;
44806         }
44807         
44808         var currency = this.getCurrency();
44809         
44810         if(this.validateValue(this.getRawValue()) && currency.length){
44811             this.markValid();
44812             return true;
44813         }
44814         
44815         this.markInvalid();
44816         return false;
44817     },
44818     
44819     getName: function()
44820     {
44821         return this.name;
44822     },
44823     
44824     beforeBlur : function()
44825     {
44826         if(!this.castInt){
44827             return;
44828         }
44829         
44830         var v = this.parseValue(this.getRawValue());
44831         
44832         if(v || v == 0){
44833             this.setValue(v);
44834         }
44835     },
44836     
44837     onBlur : function()
44838     {
44839         this.beforeBlur();
44840         
44841         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44842             //this.el.removeClass(this.focusClass);
44843         }
44844         
44845         this.hasFocus = false;
44846         
44847         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44848             this.validate();
44849         }
44850         
44851         var v = this.getValue();
44852         
44853         if(String(v) !== String(this.startValue)){
44854             this.fireEvent('change', this, v, this.startValue);
44855         }
44856         
44857         this.fireEvent("blur", this);
44858     },
44859     
44860     inputEl : function()
44861     {
44862         return this.el.select('.roo-money-amount-input', true).first();
44863     },
44864     
44865     currencyEl : function()
44866     {
44867         return this.el.select('.roo-money-currency-input', true).first();
44868     },
44869     
44870     hiddenEl : function()
44871     {
44872         return this.el.select('input.hidden-number-input',true).first();
44873     }
44874     
44875 });/**
44876  * @class Roo.bootstrap.BezierSignature
44877  * @extends Roo.bootstrap.Component
44878  * Bootstrap BezierSignature class
44879  * This script refer to:
44880  *    Title: Signature Pad
44881  *    Author: szimek
44882  *    Availability: https://github.com/szimek/signature_pad
44883  *
44884  * @constructor
44885  * Create a new BezierSignature
44886  * @param {Object} config The config object
44887  */
44888
44889 Roo.bootstrap.BezierSignature = function(config){
44890     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44891     this.addEvents({
44892         "resize" : true
44893     });
44894 };
44895
44896 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44897 {
44898      
44899     curve_data: [],
44900     
44901     is_empty: true,
44902     
44903     mouse_btn_down: true,
44904     
44905     /**
44906      * @cfg {int} canvas height
44907      */
44908     canvas_height: '200px',
44909     
44910     /**
44911      * @cfg {float|function} Radius of a single dot.
44912      */ 
44913     dot_size: false,
44914     
44915     /**
44916      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44917      */
44918     min_width: 0.5,
44919     
44920     /**
44921      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44922      */
44923     max_width: 2.5,
44924     
44925     /**
44926      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44927      */
44928     throttle: 16,
44929     
44930     /**
44931      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44932      */
44933     min_distance: 5,
44934     
44935     /**
44936      * @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.
44937      */
44938     bg_color: 'rgba(0, 0, 0, 0)',
44939     
44940     /**
44941      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44942      */
44943     dot_color: 'black',
44944     
44945     /**
44946      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44947      */ 
44948     velocity_filter_weight: 0.7,
44949     
44950     /**
44951      * @cfg {function} Callback when stroke begin. 
44952      */
44953     onBegin: false,
44954     
44955     /**
44956      * @cfg {function} Callback when stroke end.
44957      */
44958     onEnd: false,
44959     
44960     getAutoCreate : function()
44961     {
44962         var cls = 'roo-signature column';
44963         
44964         if(this.cls){
44965             cls += ' ' + this.cls;
44966         }
44967         
44968         var col_sizes = [
44969             'lg',
44970             'md',
44971             'sm',
44972             'xs'
44973         ];
44974         
44975         for(var i = 0; i < col_sizes.length; i++) {
44976             if(this[col_sizes[i]]) {
44977                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44978             }
44979         }
44980         
44981         var cfg = {
44982             tag: 'div',
44983             cls: cls,
44984             cn: [
44985                 {
44986                     tag: 'div',
44987                     cls: 'roo-signature-body',
44988                     cn: [
44989                         {
44990                             tag: 'canvas',
44991                             cls: 'roo-signature-body-canvas',
44992                             height: this.canvas_height,
44993                             width: this.canvas_width
44994                         }
44995                     ]
44996                 },
44997                 {
44998                     tag: 'input',
44999                     type: 'file',
45000                     style: 'display: none'
45001                 }
45002             ]
45003         };
45004         
45005         return cfg;
45006     },
45007     
45008     initEvents: function() 
45009     {
45010         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45011         
45012         var canvas = this.canvasEl();
45013         
45014         // mouse && touch event swapping...
45015         canvas.dom.style.touchAction = 'none';
45016         canvas.dom.style.msTouchAction = 'none';
45017         
45018         this.mouse_btn_down = false;
45019         canvas.on('mousedown', this._handleMouseDown, this);
45020         canvas.on('mousemove', this._handleMouseMove, this);
45021         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45022         
45023         if (window.PointerEvent) {
45024             canvas.on('pointerdown', this._handleMouseDown, this);
45025             canvas.on('pointermove', this._handleMouseMove, this);
45026             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45027         }
45028         
45029         if ('ontouchstart' in window) {
45030             canvas.on('touchstart', this._handleTouchStart, this);
45031             canvas.on('touchmove', this._handleTouchMove, this);
45032             canvas.on('touchend', this._handleTouchEnd, this);
45033         }
45034         
45035         Roo.EventManager.onWindowResize(this.resize, this, true);
45036         
45037         // file input event
45038         this.fileEl().on('change', this.uploadImage, this);
45039         
45040         this.clear();
45041         
45042         this.resize();
45043     },
45044     
45045     resize: function(){
45046         
45047         var canvas = this.canvasEl().dom;
45048         var ctx = this.canvasElCtx();
45049         var img_data = false;
45050         
45051         if(canvas.width > 0) {
45052             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45053         }
45054         // setting canvas width will clean img data
45055         canvas.width = 0;
45056         
45057         var style = window.getComputedStyle ? 
45058             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45059             
45060         var padding_left = parseInt(style.paddingLeft) || 0;
45061         var padding_right = parseInt(style.paddingRight) || 0;
45062         
45063         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45064         
45065         if(img_data) {
45066             ctx.putImageData(img_data, 0, 0);
45067         }
45068     },
45069     
45070     _handleMouseDown: function(e)
45071     {
45072         if (e.browserEvent.which === 1) {
45073             this.mouse_btn_down = true;
45074             this.strokeBegin(e);
45075         }
45076     },
45077     
45078     _handleMouseMove: function (e)
45079     {
45080         if (this.mouse_btn_down) {
45081             this.strokeMoveUpdate(e);
45082         }
45083     },
45084     
45085     _handleMouseUp: function (e)
45086     {
45087         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45088             this.mouse_btn_down = false;
45089             this.strokeEnd(e);
45090         }
45091     },
45092     
45093     _handleTouchStart: function (e) {
45094         
45095         e.preventDefault();
45096         if (e.browserEvent.targetTouches.length === 1) {
45097             // var touch = e.browserEvent.changedTouches[0];
45098             // this.strokeBegin(touch);
45099             
45100              this.strokeBegin(e); // assume e catching the correct xy...
45101         }
45102     },
45103     
45104     _handleTouchMove: function (e) {
45105         e.preventDefault();
45106         // var touch = event.targetTouches[0];
45107         // _this._strokeMoveUpdate(touch);
45108         this.strokeMoveUpdate(e);
45109     },
45110     
45111     _handleTouchEnd: function (e) {
45112         var wasCanvasTouched = e.target === this.canvasEl().dom;
45113         if (wasCanvasTouched) {
45114             e.preventDefault();
45115             // var touch = event.changedTouches[0];
45116             // _this._strokeEnd(touch);
45117             this.strokeEnd(e);
45118         }
45119     },
45120     
45121     reset: function () {
45122         this._lastPoints = [];
45123         this._lastVelocity = 0;
45124         this._lastWidth = (this.min_width + this.max_width) / 2;
45125         this.canvasElCtx().fillStyle = this.dot_color;
45126     },
45127     
45128     strokeMoveUpdate: function(e)
45129     {
45130         this.strokeUpdate(e);
45131         
45132         if (this.throttle) {
45133             this.throttleStroke(this.strokeUpdate, this.throttle);
45134         }
45135         else {
45136             this.strokeUpdate(e);
45137         }
45138     },
45139     
45140     strokeBegin: function(e)
45141     {
45142         var newPointGroup = {
45143             color: this.dot_color,
45144             points: []
45145         };
45146         
45147         if (typeof this.onBegin === 'function') {
45148             this.onBegin(e);
45149         }
45150         
45151         this.curve_data.push(newPointGroup);
45152         this.reset();
45153         this.strokeUpdate(e);
45154     },
45155     
45156     strokeUpdate: function(e)
45157     {
45158         var rect = this.canvasEl().dom.getBoundingClientRect();
45159         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45160         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45161         var lastPoints = lastPointGroup.points;
45162         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45163         var isLastPointTooClose = lastPoint
45164             ? point.distanceTo(lastPoint) <= this.min_distance
45165             : false;
45166         var color = lastPointGroup.color;
45167         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45168             var curve = this.addPoint(point);
45169             if (!lastPoint) {
45170                 this.drawDot({color: color, point: point});
45171             }
45172             else if (curve) {
45173                 this.drawCurve({color: color, curve: curve});
45174             }
45175             lastPoints.push({
45176                 time: point.time,
45177                 x: point.x,
45178                 y: point.y
45179             });
45180         }
45181     },
45182     
45183     strokeEnd: function(e)
45184     {
45185         this.strokeUpdate(e);
45186         if (typeof this.onEnd === 'function') {
45187             this.onEnd(e);
45188         }
45189     },
45190     
45191     addPoint:  function (point) {
45192         var _lastPoints = this._lastPoints;
45193         _lastPoints.push(point);
45194         if (_lastPoints.length > 2) {
45195             if (_lastPoints.length === 3) {
45196                 _lastPoints.unshift(_lastPoints[0]);
45197             }
45198             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45199             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45200             _lastPoints.shift();
45201             return curve;
45202         }
45203         return null;
45204     },
45205     
45206     calculateCurveWidths: function (startPoint, endPoint) {
45207         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45208             (1 - this.velocity_filter_weight) * this._lastVelocity;
45209
45210         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45211         var widths = {
45212             end: newWidth,
45213             start: this._lastWidth
45214         };
45215         
45216         this._lastVelocity = velocity;
45217         this._lastWidth = newWidth;
45218         return widths;
45219     },
45220     
45221     drawDot: function (_a) {
45222         var color = _a.color, point = _a.point;
45223         var ctx = this.canvasElCtx();
45224         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45225         ctx.beginPath();
45226         this.drawCurveSegment(point.x, point.y, width);
45227         ctx.closePath();
45228         ctx.fillStyle = color;
45229         ctx.fill();
45230     },
45231     
45232     drawCurve: function (_a) {
45233         var color = _a.color, curve = _a.curve;
45234         var ctx = this.canvasElCtx();
45235         var widthDelta = curve.endWidth - curve.startWidth;
45236         var drawSteps = Math.floor(curve.length()) * 2;
45237         ctx.beginPath();
45238         ctx.fillStyle = color;
45239         for (var i = 0; i < drawSteps; i += 1) {
45240         var t = i / drawSteps;
45241         var tt = t * t;
45242         var ttt = tt * t;
45243         var u = 1 - t;
45244         var uu = u * u;
45245         var uuu = uu * u;
45246         var x = uuu * curve.startPoint.x;
45247         x += 3 * uu * t * curve.control1.x;
45248         x += 3 * u * tt * curve.control2.x;
45249         x += ttt * curve.endPoint.x;
45250         var y = uuu * curve.startPoint.y;
45251         y += 3 * uu * t * curve.control1.y;
45252         y += 3 * u * tt * curve.control2.y;
45253         y += ttt * curve.endPoint.y;
45254         var width = curve.startWidth + ttt * widthDelta;
45255         this.drawCurveSegment(x, y, width);
45256         }
45257         ctx.closePath();
45258         ctx.fill();
45259     },
45260     
45261     drawCurveSegment: function (x, y, width) {
45262         var ctx = this.canvasElCtx();
45263         ctx.moveTo(x, y);
45264         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45265         this.is_empty = false;
45266     },
45267     
45268     clear: function()
45269     {
45270         var ctx = this.canvasElCtx();
45271         var canvas = this.canvasEl().dom;
45272         ctx.fillStyle = this.bg_color;
45273         ctx.clearRect(0, 0, canvas.width, canvas.height);
45274         ctx.fillRect(0, 0, canvas.width, canvas.height);
45275         this.curve_data = [];
45276         this.reset();
45277         this.is_empty = true;
45278     },
45279     
45280     fileEl: function()
45281     {
45282         return  this.el.select('input',true).first();
45283     },
45284     
45285     canvasEl: function()
45286     {
45287         return this.el.select('canvas',true).first();
45288     },
45289     
45290     canvasElCtx: function()
45291     {
45292         return this.el.select('canvas',true).first().dom.getContext('2d');
45293     },
45294     
45295     getImage: function(type)
45296     {
45297         if(this.is_empty) {
45298             return false;
45299         }
45300         
45301         // encryption ?
45302         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45303     },
45304     
45305     drawFromImage: function(img_src)
45306     {
45307         var img = new Image();
45308         
45309         img.onload = function(){
45310             this.canvasElCtx().drawImage(img, 0, 0);
45311         }.bind(this);
45312         
45313         img.src = img_src;
45314         
45315         this.is_empty = false;
45316     },
45317     
45318     selectImage: function()
45319     {
45320         this.fileEl().dom.click();
45321     },
45322     
45323     uploadImage: function(e)
45324     {
45325         var reader = new FileReader();
45326         
45327         reader.onload = function(e){
45328             var img = new Image();
45329             img.onload = function(){
45330                 this.reset();
45331                 this.canvasElCtx().drawImage(img, 0, 0);
45332             }.bind(this);
45333             img.src = e.target.result;
45334         }.bind(this);
45335         
45336         reader.readAsDataURL(e.target.files[0]);
45337     },
45338     
45339     // Bezier Point Constructor
45340     Point: (function () {
45341         function Point(x, y, time) {
45342             this.x = x;
45343             this.y = y;
45344             this.time = time || Date.now();
45345         }
45346         Point.prototype.distanceTo = function (start) {
45347             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45348         };
45349         Point.prototype.equals = function (other) {
45350             return this.x === other.x && this.y === other.y && this.time === other.time;
45351         };
45352         Point.prototype.velocityFrom = function (start) {
45353             return this.time !== start.time
45354             ? this.distanceTo(start) / (this.time - start.time)
45355             : 0;
45356         };
45357         return Point;
45358     }()),
45359     
45360     
45361     // Bezier Constructor
45362     Bezier: (function () {
45363         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45364             this.startPoint = startPoint;
45365             this.control2 = control2;
45366             this.control1 = control1;
45367             this.endPoint = endPoint;
45368             this.startWidth = startWidth;
45369             this.endWidth = endWidth;
45370         }
45371         Bezier.fromPoints = function (points, widths, scope) {
45372             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45373             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45374             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45375         };
45376         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45377             var dx1 = s1.x - s2.x;
45378             var dy1 = s1.y - s2.y;
45379             var dx2 = s2.x - s3.x;
45380             var dy2 = s2.y - s3.y;
45381             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45382             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45383             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45384             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45385             var dxm = m1.x - m2.x;
45386             var dym = m1.y - m2.y;
45387             var k = l2 / (l1 + l2);
45388             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45389             var tx = s2.x - cm.x;
45390             var ty = s2.y - cm.y;
45391             return {
45392                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45393                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45394             };
45395         };
45396         Bezier.prototype.length = function () {
45397             var steps = 10;
45398             var length = 0;
45399             var px;
45400             var py;
45401             for (var i = 0; i <= steps; i += 1) {
45402                 var t = i / steps;
45403                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45404                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45405                 if (i > 0) {
45406                     var xdiff = cx - px;
45407                     var ydiff = cy - py;
45408                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45409                 }
45410                 px = cx;
45411                 py = cy;
45412             }
45413             return length;
45414         };
45415         Bezier.prototype.point = function (t, start, c1, c2, end) {
45416             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45417             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45418             + (3.0 * c2 * (1.0 - t) * t * t)
45419             + (end * t * t * t);
45420         };
45421         return Bezier;
45422     }()),
45423     
45424     throttleStroke: function(fn, wait) {
45425       if (wait === void 0) { wait = 250; }
45426       var previous = 0;
45427       var timeout = null;
45428       var result;
45429       var storedContext;
45430       var storedArgs;
45431       var later = function () {
45432           previous = Date.now();
45433           timeout = null;
45434           result = fn.apply(storedContext, storedArgs);
45435           if (!timeout) {
45436               storedContext = null;
45437               storedArgs = [];
45438           }
45439       };
45440       return function wrapper() {
45441           var args = [];
45442           for (var _i = 0; _i < arguments.length; _i++) {
45443               args[_i] = arguments[_i];
45444           }
45445           var now = Date.now();
45446           var remaining = wait - (now - previous);
45447           storedContext = this;
45448           storedArgs = args;
45449           if (remaining <= 0 || remaining > wait) {
45450               if (timeout) {
45451                   clearTimeout(timeout);
45452                   timeout = null;
45453               }
45454               previous = now;
45455               result = fn.apply(storedContext, storedArgs);
45456               if (!timeout) {
45457                   storedContext = null;
45458                   storedArgs = [];
45459               }
45460           }
45461           else if (!timeout) {
45462               timeout = window.setTimeout(later, remaining);
45463           }
45464           return result;
45465       };
45466   }
45467   
45468 });
45469
45470  
45471
45472