roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     mode: false,
99     /**
100      * @cfg {String} offset
101      * The number of pixels to offset the shadow from the element (defaults to 4)
102      */
103     offset: 4,
104
105     // private
106     defaultMode: "drop",
107
108     /**
109      * Displays the shadow under the target element
110      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
111      */
112     show : function(target){
113         target = Roo.get(target);
114         if(!this.el){
115             this.el = Roo.Shadow.Pool.pull();
116             if(this.el.dom.nextSibling != target.dom){
117                 this.el.insertBefore(target);
118             }
119         }
120         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
121         if(Roo.isIE){
122             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
123         }
124         this.realign(
125             target.getLeft(true),
126             target.getTop(true),
127             target.getWidth(),
128             target.getHeight()
129         );
130         this.el.dom.style.display = "block";
131     },
132
133     /**
134      * Returns true if the shadow is visible, else false
135      */
136     isVisible : function(){
137         return this.el ? true : false;  
138     },
139
140     /**
141      * Direct alignment when values are already available. Show must be called at least once before
142      * calling this method to ensure it is initialized.
143      * @param {Number} left The target element left position
144      * @param {Number} top The target element top position
145      * @param {Number} width The target element width
146      * @param {Number} height The target element height
147      */
148     realign : function(l, t, w, h){
149         if(!this.el){
150             return;
151         }
152         var a = this.adjusts, d = this.el.dom, s = d.style;
153         var iea = 0;
154         s.left = (l+a.l)+"px";
155         s.top = (t+a.t)+"px";
156         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
157  
158         if(s.width != sws || s.height != shs){
159             s.width = sws;
160             s.height = shs;
161             if(!Roo.isIE){
162                 var cn = d.childNodes;
163                 var sww = Math.max(0, (sw-12))+"px";
164                 cn[0].childNodes[1].style.width = sww;
165                 cn[1].childNodes[1].style.width = sww;
166                 cn[2].childNodes[1].style.width = sww;
167                 cn[1].style.height = Math.max(0, (sh-12))+"px";
168             }
169         }
170     },
171
172     /**
173      * Hides this shadow
174      */
175     hide : function(){
176         if(this.el){
177             this.el.dom.style.display = "none";
178             Roo.Shadow.Pool.push(this.el);
179             delete this.el;
180         }
181     },
182
183     /**
184      * Adjust the z-index of this shadow
185      * @param {Number} zindex The new z-index
186      */
187     setZIndex : function(z){
188         this.zIndex = z;
189         if(this.el){
190             this.el.setStyle("z-index", z);
191         }
192     }
193 };
194
195 // Private utility class that manages the internal Shadow cache
196 Roo.Shadow.Pool = function(){
197     var p = [];
198     var markup = Roo.isIE ?
199                  '<div class="x-ie-shadow"></div>' :
200                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
201     return {
202         pull : function(){
203             var sh = p.shift();
204             if(!sh){
205                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
206                 sh.autoBoxAdjust = false;
207             }
208             return sh;
209         },
210
211         push : function(sh){
212             p.push(sh);
213         }
214     };
215 }();/*
216  * - LGPL
217  *
218  * base class for bootstrap elements.
219  * 
220  */
221
222 Roo.bootstrap = Roo.bootstrap || {};
223 /**
224  * @class Roo.bootstrap.Component
225  * @extends Roo.Component
226  * Bootstrap Component base class
227  * @cfg {String} cls css class
228  * @cfg {String} style any extra css
229  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
230  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
231  * @cfg {string} dataId cutomer id
232  * @cfg {string} name Specifies name attribute
233  * @cfg {string} tooltip  Text for the tooltip
234  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
235  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
236  
237  * @constructor
238  * Do not use directly - it does not do anything..
239  * @param {Object} config The config object
240  */
241
242
243
244 Roo.bootstrap.Component = function(config){
245     Roo.bootstrap.Component.superclass.constructor.call(this, config);
246        
247     this.addEvents({
248         /**
249          * @event childrenrendered
250          * Fires when the children have been rendered..
251          * @param {Roo.bootstrap.Component} this
252          */
253         "childrenrendered" : true
254         
255         
256         
257     });
258     
259     
260 };
261
262 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
263     
264     
265     allowDomMove : false, // to stop relocations in parent onRender...
266     
267     cls : false,
268     
269     style : false,
270     
271     autoCreate : false,
272     
273     tooltip : null,
274     /**
275      * Initialize Events for the element
276      */
277     initEvents : function() { },
278     
279     xattr : false,
280     
281     parentId : false,
282     
283     can_build_overlaid : true,
284     
285     container_method : false,
286     
287     dataId : false,
288     
289     name : false,
290     
291     parent: function() {
292         // returns the parent component..
293         return Roo.ComponentMgr.get(this.parentId)
294         
295         
296     },
297     
298     // private
299     onRender : function(ct, position)
300     {
301        // Roo.log("Call onRender: " + this.xtype);
302         
303         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
304         
305         if(this.el){
306             if (this.el.attr('xtype')) {
307                 this.el.attr('xtypex', this.el.attr('xtype'));
308                 this.el.dom.removeAttribute('xtype');
309                 
310                 this.initEvents();
311             }
312             
313             return;
314         }
315         
316          
317         
318         var cfg = Roo.apply({},  this.getAutoCreate());
319         
320         cfg.id = this.id || Roo.id();
321         
322         // fill in the extra attributes 
323         if (this.xattr && typeof(this.xattr) =='object') {
324             for (var i in this.xattr) {
325                 cfg[i] = this.xattr[i];
326             }
327         }
328         
329         if(this.dataId){
330             cfg.dataId = this.dataId;
331         }
332         
333         if (this.cls) {
334             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
335         }
336         
337         if (this.style) { // fixme needs to support more complex style data.
338             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
339         }
340         
341         if(this.name){
342             cfg.name = this.name;
343         }
344         
345         this.el = ct.createChild(cfg, position);
346         
347         if (this.tooltip) {
348             this.tooltipEl().attr('tooltip', this.tooltip);
349         }
350         
351         if(this.tabIndex !== undefined){
352             this.el.dom.setAttribute('tabIndex', this.tabIndex);
353         }
354         
355         this.initEvents();
356         
357     },
358     /**
359      * Fetch the element to add children to
360      * @return {Roo.Element} defaults to this.el
361      */
362     getChildContainer : function()
363     {
364         return this.el;
365     },
366     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
367     {
368         return Roo.get(document.body);
369     },
370     
371     /**
372      * Fetch the element to display the tooltip on.
373      * @return {Roo.Element} defaults to this.el
374      */
375     tooltipEl : function()
376     {
377         return this.el;
378     },
379         
380     addxtype  : function(tree,cntr)
381     {
382         var cn = this;
383         
384         cn = Roo.factory(tree);
385         //Roo.log(['addxtype', cn]);
386            
387         cn.parentType = this.xtype; //??
388         cn.parentId = this.id;
389         
390         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
391         if (typeof(cn.container_method) == 'string') {
392             cntr = cn.container_method;
393         }
394         
395         
396         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
397         
398         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
399         
400         var build_from_html =  Roo.XComponent.build_from_html;
401           
402         var is_body  = (tree.xtype == 'Body') ;
403           
404         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
405           
406         var self_cntr_el = Roo.get(this[cntr](false));
407         
408         // do not try and build conditional elements 
409         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
410             return false;
411         }
412         
413         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
414             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
415                 return this.addxtypeChild(tree,cntr, is_body);
416             }
417             
418             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
419                 
420             if(echild){
421                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
422             }
423             
424             Roo.log('skipping render');
425             return cn;
426             
427         }
428         
429         var ret = false;
430         if (!build_from_html) {
431             return false;
432         }
433         
434         // this i think handles overlaying multiple children of the same type
435         // with the sam eelement.. - which might be buggy..
436         while (true) {
437             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
438             
439             if (!echild) {
440                 break;
441             }
442             
443             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
444                 break;
445             }
446             
447             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
448         }
449        
450         return ret;
451     },
452     
453     
454     addxtypeChild : function (tree, cntr, is_body)
455     {
456         Roo.debug && Roo.log('addxtypeChild:' + cntr);
457         var cn = this;
458         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
459         
460         
461         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
462                     (typeof(tree['flexy:foreach']) != 'undefined');
463           
464     
465         
466         skip_children = false;
467         // render the element if it's not BODY.
468         if (!is_body) {
469             
470             // if parent was disabled, then do not try and create the children..
471             if(!this[cntr](true)){
472                 tree.items = [];
473                 return tree;
474             }
475            
476             cn = Roo.factory(tree);
477            
478             cn.parentType = this.xtype; //??
479             cn.parentId = this.id;
480             
481             var build_from_html =  Roo.XComponent.build_from_html;
482             
483             
484             // does the container contain child eleemnts with 'xtype' attributes.
485             // that match this xtype..
486             // note - when we render we create these as well..
487             // so we should check to see if body has xtype set.
488             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
489                
490                 var self_cntr_el = Roo.get(this[cntr](false));
491                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
492                 if (echild) { 
493                     //Roo.log(Roo.XComponent.build_from_html);
494                     //Roo.log("got echild:");
495                     //Roo.log(echild);
496                 }
497                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
498                 // and are not displayed -this causes this to use up the wrong element when matching.
499                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
500                 
501                 
502                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
503                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
504                   
505                   
506                   
507                     cn.el = echild;
508                   //  Roo.log("GOT");
509                     //echild.dom.removeAttribute('xtype');
510                 } else {
511                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
512                     Roo.debug && Roo.log(self_cntr_el);
513                     Roo.debug && Roo.log(echild);
514                     Roo.debug && Roo.log(cn);
515                 }
516             }
517            
518             
519            
520             // if object has flexy:if - then it may or may not be rendered.
521             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
522                 // skip a flexy if element.
523                 Roo.debug && Roo.log('skipping render');
524                 Roo.debug && Roo.log(tree);
525                 if (!cn.el) {
526                     Roo.debug && Roo.log('skipping all children');
527                     skip_children = true;
528                 }
529                 
530              } else {
531                  
532                 // actually if flexy:foreach is found, we really want to create 
533                 // multiple copies here...
534                 //Roo.log('render');
535                 //Roo.log(this[cntr]());
536                 // some elements do not have render methods.. like the layouts...
537                 /*
538                 if(this[cntr](true) === false){
539                     cn.items = [];
540                     return cn;
541                 }
542                 */
543                 cn.render && cn.render(this[cntr](true));
544                 
545              }
546             // then add the element..
547         }
548          
549         // handle the kids..
550         
551         var nitems = [];
552         /*
553         if (typeof (tree.menu) != 'undefined') {
554             tree.menu.parentType = cn.xtype;
555             tree.menu.triggerEl = cn.el;
556             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
557             
558         }
559         */
560         if (!tree.items || !tree.items.length) {
561             cn.items = nitems;
562             //Roo.log(["no children", this]);
563             
564             return cn;
565         }
566          
567         var items = tree.items;
568         delete tree.items;
569         
570         //Roo.log(items.length);
571             // add the items..
572         if (!skip_children) {    
573             for(var i =0;i < items.length;i++) {
574               //  Roo.log(['add child', items[i]]);
575                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
576             }
577         }
578         
579         cn.items = nitems;
580         
581         //Roo.log("fire childrenrendered");
582         
583         cn.fireEvent('childrenrendered', this);
584         
585         return cn;
586     },
587     
588     /**
589      * Set the element that will be used to show or hide
590      */
591     setVisibilityEl : function(el)
592     {
593         this.visibilityEl = el;
594     },
595     
596      /**
597      * Get the element that will be used to show or hide
598      */
599     getVisibilityEl : function()
600     {
601         if (typeof(this.visibilityEl) == 'object') {
602             return this.visibilityEl;
603         }
604         
605         if (typeof(this.visibilityEl) == 'string') {
606             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
607         }
608         
609         return this.getEl();
610     },
611     
612     /**
613      * Show a component - removes 'hidden' class
614      */
615     show : function()
616     {
617         if(!this.getVisibilityEl()){
618             return;
619         }
620          
621         this.getVisibilityEl().removeClass(['hidden','d-none']);
622         
623         this.fireEvent('show', this);
624         
625         
626     },
627     /**
628      * Hide a component - adds 'hidden' class
629      */
630     hide: function()
631     {
632         if(!this.getVisibilityEl()){
633             return;
634         }
635         
636         this.getVisibilityEl().addClass(['hidden','d-none']);
637         
638         this.fireEvent('hide', this);
639         
640     }
641 });
642
643  /*
644  * - LGPL
645  *
646  * element
647  * 
648  */
649
650 /**
651  * @class Roo.bootstrap.Element
652  * @extends Roo.bootstrap.Component
653  * Bootstrap Element class
654  * @cfg {String} html contents of the element
655  * @cfg {String} tag tag of the element
656  * @cfg {String} cls class of the element
657  * @cfg {Boolean} preventDefault (true|false) default false
658  * @cfg {Boolean} clickable (true|false) default false
659  * @cfg {String} role default blank - set to button to force cursor pointer
660  
661  * 
662  * @constructor
663  * Create a new Element
664  * @param {Object} config The config object
665  */
666
667 Roo.bootstrap.Element = function(config){
668     Roo.bootstrap.Element.superclass.constructor.call(this, config);
669     
670     this.addEvents({
671         // raw events
672         /**
673          * @event click
674          * When a element is chick
675          * @param {Roo.bootstrap.Element} this
676          * @param {Roo.EventObject} e
677          */
678         "click" : true 
679         
680       
681     });
682 };
683
684 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
685     
686     tag: 'div',
687     cls: '',
688     html: '',
689     preventDefault: false, 
690     clickable: false,
691     tapedTwice : false,
692     role : false,
693     
694     getAutoCreate : function(){
695         
696         var cfg = {
697             tag: this.tag,
698             // cls: this.cls, double assign in parent class Component.js :: onRender
699             html: this.html
700         };
701         if (this.role !== false) {
702             cfg.role = this.role;
703         }
704         
705         return cfg;
706     },
707     
708     initEvents: function() 
709     {
710         Roo.bootstrap.Element.superclass.initEvents.call(this);
711         
712         if(this.clickable){
713             this.el.on('click', this.onClick, this);
714         }
715         
716         
717     },
718     
719     onClick : function(e)
720     {
721         if(this.preventDefault){
722             e.preventDefault();
723         }
724         
725         this.fireEvent('click', this, e); // why was this double click before?
726     },
727     
728     
729     
730
731     
732     
733     getValue : function()
734     {
735         return this.el.dom.innerHTML;
736     },
737     
738     setValue : function(value)
739     {
740         this.el.dom.innerHTML = value;
741     }
742    
743 });
744
745  
746
747  /*
748  * - LGPL
749  *
750  * dropable area
751  * 
752  */
753
754 /**
755  * @class Roo.bootstrap.DropTarget
756  * @extends Roo.bootstrap.Element
757  * Bootstrap DropTarget class
758  
759  * @cfg {string} name dropable name
760  * 
761  * @constructor
762  * Create a new Dropable Area
763  * @param {Object} config The config object
764  */
765
766 Roo.bootstrap.DropTarget = function(config){
767     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
768     
769     this.addEvents({
770         // raw events
771         /**
772          * @event click
773          * When a element is chick
774          * @param {Roo.bootstrap.Element} this
775          * @param {Roo.EventObject} e
776          */
777         "drop" : true
778     });
779 };
780
781 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
782     
783     
784     getAutoCreate : function(){
785         
786          
787     },
788     
789     initEvents: function() 
790     {
791         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
792         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
793             ddGroup: this.name,
794             listeners : {
795                 drop : this.dragDrop.createDelegate(this),
796                 enter : this.dragEnter.createDelegate(this),
797                 out : this.dragOut.createDelegate(this),
798                 over : this.dragOver.createDelegate(this)
799             }
800             
801         });
802         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
803     },
804     
805     dragDrop : function(source,e,data)
806     {
807         // user has to decide how to impliment this.
808         Roo.log('drop');
809         Roo.log(this);
810         //this.fireEvent('drop', this, source, e ,data);
811         return false;
812     },
813     
814     dragEnter : function(n, dd, e, data)
815     {
816         // probably want to resize the element to match the dropped element..
817         Roo.log("enter");
818         this.originalSize = this.el.getSize();
819         this.el.setSize( n.el.getSize());
820         this.dropZone.DDM.refreshCache(this.name);
821         Roo.log([n, dd, e, data]);
822     },
823     
824     dragOut : function(value)
825     {
826         // resize back to normal
827         Roo.log("out");
828         this.el.setSize(this.originalSize);
829         this.dropZone.resetConstraints();
830     },
831     
832     dragOver : function()
833     {
834         // ??? do nothing?
835     }
836    
837 });
838
839  
840
841  /*
842  * - LGPL
843  *
844  * Body
845  *
846  */
847
848 /**
849  * @class Roo.bootstrap.Body
850  * @extends Roo.bootstrap.Component
851  * Bootstrap Body class
852  *
853  * @constructor
854  * Create a new body
855  * @param {Object} config The config object
856  */
857
858 Roo.bootstrap.Body = function(config){
859
860     config = config || {};
861
862     Roo.bootstrap.Body.superclass.constructor.call(this, config);
863     this.el = Roo.get(config.el ? config.el : document.body );
864     if (this.cls && this.cls.length) {
865         Roo.get(document.body).addClass(this.cls);
866     }
867 };
868
869 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
870
871     is_body : true,// just to make sure it's constructed?
872
873         autoCreate : {
874         cls: 'container'
875     },
876     onRender : function(ct, position)
877     {
878        /* Roo.log("Roo.bootstrap.Body - onRender");
879         if (this.cls && this.cls.length) {
880             Roo.get(document.body).addClass(this.cls);
881         }
882         // style??? xttr???
883         */
884     }
885
886
887
888
889 });
890 /*
891  * - LGPL
892  *
893  * button group
894  * 
895  */
896
897
898 /**
899  * @class Roo.bootstrap.ButtonGroup
900  * @extends Roo.bootstrap.Component
901  * Bootstrap ButtonGroup class
902  * @cfg {String} size lg | sm | xs (default empty normal)
903  * @cfg {String} align vertical | justified  (default none)
904  * @cfg {String} direction up | down (default down)
905  * @cfg {Boolean} toolbar false | true
906  * @cfg {Boolean} btn true | false
907  * 
908  * 
909  * @constructor
910  * Create a new Input
911  * @param {Object} config The config object
912  */
913
914 Roo.bootstrap.ButtonGroup = function(config){
915     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
916 };
917
918 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
919     
920     size: '',
921     align: '',
922     direction: '',
923     toolbar: false,
924     btn: true,
925
926     getAutoCreate : function(){
927         var cfg = {
928             cls: 'btn-group',
929             html : null
930         };
931         
932         cfg.html = this.html || cfg.html;
933         
934         if (this.toolbar) {
935             cfg = {
936                 cls: 'btn-toolbar',
937                 html: null
938             };
939             
940             return cfg;
941         }
942         
943         if (['vertical','justified'].indexOf(this.align)!==-1) {
944             cfg.cls = 'btn-group-' + this.align;
945             
946             if (this.align == 'justified') {
947                 console.log(this.items);
948             }
949         }
950         
951         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
952             cfg.cls += ' btn-group-' + this.size;
953         }
954         
955         if (this.direction == 'up') {
956             cfg.cls += ' dropup' ;
957         }
958         
959         return cfg;
960     },
961     /**
962      * Add a button to the group (similar to NavItem API.)
963      */
964     addItem : function(cfg)
965     {
966         var cn = new Roo.bootstrap.Button(cfg);
967         //this.register(cn);
968         cn.parentId = this.id;
969         cn.onRender(this.el, null);
970         return cn;
971     }
972    
973 });
974
975  /*
976  * - LGPL
977  *
978  * button
979  * 
980  */
981
982 /**
983  * @class Roo.bootstrap.Button
984  * @extends Roo.bootstrap.Component
985  * Bootstrap Button class
986  * @cfg {String} html The button content
987  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
988  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
989  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
990  * @cfg {String} size (lg|sm|xs)
991  * @cfg {String} tag (a|input|submit)
992  * @cfg {String} href empty or href
993  * @cfg {Boolean} disabled default false;
994  * @cfg {Boolean} isClose default false;
995  * @cfg {String} glyphicon depricated - use fa
996  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
997  * @cfg {String} badge text for badge
998  * @cfg {String} theme (default|glow)  
999  * @cfg {Boolean} inverse dark themed version
1000  * @cfg {Boolean} toggle is it a slidy toggle button
1001  * @cfg {Boolean} pressed   default null - if the button ahs active state
1002  * @cfg {String} ontext text for on slidy toggle state
1003  * @cfg {String} offtext text for off slidy toggle state
1004  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1005  * @cfg {Boolean} removeClass remove the standard class..
1006  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1007  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1008  * 
1009  * @constructor
1010  * Create a new button
1011  * @param {Object} config The config object
1012  */
1013
1014
1015 Roo.bootstrap.Button = function(config){
1016     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1017     
1018     this.addEvents({
1019         // raw events
1020         /**
1021          * @event click
1022          * When a button is pressed
1023          * @param {Roo.bootstrap.Button} btn
1024          * @param {Roo.EventObject} e
1025          */
1026         "click" : true,
1027         /**
1028          * @event dblclick
1029          * When a button is double clicked
1030          * @param {Roo.bootstrap.Button} btn
1031          * @param {Roo.EventObject} e
1032          */
1033         "dblclick" : true,
1034          /**
1035          * @event toggle
1036          * After the button has been toggles
1037          * @param {Roo.bootstrap.Button} btn
1038          * @param {Roo.EventObject} e
1039          * @param {boolean} pressed (also available as button.pressed)
1040          */
1041         "toggle" : true
1042     });
1043 };
1044
1045 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1046     html: false,
1047     active: false,
1048     weight: '',
1049     badge_weight: '',
1050     outline : false,
1051     size: '',
1052     tag: 'button',
1053     href: '',
1054     disabled: false,
1055     isClose: false,
1056     glyphicon: '',
1057     fa: '',
1058     badge: '',
1059     theme: 'default',
1060     inverse: false,
1061     
1062     toggle: false,
1063     ontext: 'ON',
1064     offtext: 'OFF',
1065     defaulton: true,
1066     preventDefault: true,
1067     removeClass: false,
1068     name: false,
1069     target: false,
1070     group : false,
1071      
1072     pressed : null,
1073      
1074     
1075     getAutoCreate : function(){
1076         
1077         var cfg = {
1078             tag : 'button',
1079             cls : 'roo-button',
1080             html: ''
1081         };
1082         
1083         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1084             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1085             this.tag = 'button';
1086         } else {
1087             cfg.tag = this.tag;
1088         }
1089         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1090         
1091         if (this.toggle == true) {
1092             cfg={
1093                 tag: 'div',
1094                 cls: 'slider-frame roo-button',
1095                 cn: [
1096                     {
1097                         tag: 'span',
1098                         'data-on-text':'ON',
1099                         'data-off-text':'OFF',
1100                         cls: 'slider-button',
1101                         html: this.offtext
1102                     }
1103                 ]
1104             };
1105             // why are we validating the weights?
1106             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1107                 cfg.cls +=  ' ' + this.weight;
1108             }
1109             
1110             return cfg;
1111         }
1112         
1113         if (this.isClose) {
1114             cfg.cls += ' close';
1115             
1116             cfg["aria-hidden"] = true;
1117             
1118             cfg.html = "&times;";
1119             
1120             return cfg;
1121         }
1122              
1123         
1124         if (this.theme==='default') {
1125             cfg.cls = 'btn roo-button';
1126             
1127             //if (this.parentType != 'Navbar') {
1128             this.weight = this.weight.length ?  this.weight : 'default';
1129             //}
1130             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1131                 
1132                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1133                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1134                 cfg.cls += ' btn-' + outline + weight;
1135                 if (this.weight == 'default') {
1136                     // BC
1137                     cfg.cls += ' btn-' + this.weight;
1138                 }
1139             }
1140         } else if (this.theme==='glow') {
1141             
1142             cfg.tag = 'a';
1143             cfg.cls = 'btn-glow roo-button';
1144             
1145             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1146                 
1147                 cfg.cls += ' ' + this.weight;
1148             }
1149         }
1150    
1151         
1152         if (this.inverse) {
1153             this.cls += ' inverse';
1154         }
1155         
1156         
1157         if (this.active || this.pressed === true) {
1158             cfg.cls += ' active';
1159         }
1160         
1161         if (this.disabled) {
1162             cfg.disabled = 'disabled';
1163         }
1164         
1165         if (this.items) {
1166             Roo.log('changing to ul' );
1167             cfg.tag = 'ul';
1168             this.glyphicon = 'caret';
1169             if (Roo.bootstrap.version == 4) {
1170                 this.fa = 'caret-down';
1171             }
1172             
1173         }
1174         
1175         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1176          
1177         //gsRoo.log(this.parentType);
1178         if (this.parentType === 'Navbar' && !this.parent().bar) {
1179             Roo.log('changing to li?');
1180             
1181             cfg.tag = 'li';
1182             
1183             cfg.cls = '';
1184             cfg.cn =  [{
1185                 tag : 'a',
1186                 cls : 'roo-button',
1187                 html : this.html,
1188                 href : this.href || '#'
1189             }];
1190             if (this.menu) {
1191                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1192                 cfg.cls += ' dropdown';
1193             }   
1194             
1195             delete cfg.html;
1196             
1197         }
1198         
1199        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1200         
1201         if (this.glyphicon) {
1202             cfg.html = ' ' + cfg.html;
1203             
1204             cfg.cn = [
1205                 {
1206                     tag: 'span',
1207                     cls: 'glyphicon glyphicon-' + this.glyphicon
1208                 }
1209             ];
1210         }
1211         if (this.fa) {
1212             cfg.html = ' ' + cfg.html;
1213             
1214             cfg.cn = [
1215                 {
1216                     tag: 'i',
1217                     cls: 'fa fas fa-' + this.fa
1218                 }
1219             ];
1220         }
1221         
1222         if (this.badge) {
1223             cfg.html += ' ';
1224             
1225             cfg.tag = 'a';
1226             
1227 //            cfg.cls='btn roo-button';
1228             
1229             cfg.href=this.href;
1230             
1231             var value = cfg.html;
1232             
1233             if(this.glyphicon){
1234                 value = {
1235                     tag: 'span',
1236                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1237                     html: this.html
1238                 };
1239             }
1240             if(this.fa){
1241                 value = {
1242                     tag: 'i',
1243                     cls: 'fa fas fa-' + this.fa,
1244                     html: this.html
1245                 };
1246             }
1247             
1248             var bw = this.badge_weight.length ? this.badge_weight :
1249                 (this.weight.length ? this.weight : 'secondary');
1250             bw = bw == 'default' ? 'secondary' : bw;
1251             
1252             cfg.cn = [
1253                 value,
1254                 {
1255                     tag: 'span',
1256                     cls: 'badge badge-' + bw,
1257                     html: this.badge
1258                 }
1259             ];
1260             
1261             cfg.html='';
1262         }
1263         
1264         if (this.menu) {
1265             cfg.cls += ' dropdown';
1266             cfg.html = typeof(cfg.html) != 'undefined' ?
1267                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1268         }
1269         
1270         if (cfg.tag !== 'a' && this.href !== '') {
1271             throw "Tag must be a to set href.";
1272         } else if (this.href.length > 0) {
1273             cfg.href = this.href;
1274         }
1275         
1276         if(this.removeClass){
1277             cfg.cls = '';
1278         }
1279         
1280         if(this.target){
1281             cfg.target = this.target;
1282         }
1283         
1284         return cfg;
1285     },
1286     initEvents: function() {
1287        // Roo.log('init events?');
1288 //        Roo.log(this.el.dom);
1289         // add the menu...
1290         
1291         if (typeof (this.menu) != 'undefined') {
1292             this.menu.parentType = this.xtype;
1293             this.menu.triggerEl = this.el;
1294             this.addxtype(Roo.apply({}, this.menu));
1295         }
1296
1297
1298         if (this.el.hasClass('roo-button')) {
1299              this.el.on('click', this.onClick, this);
1300              this.el.on('dblclick', this.onDblClick, this);
1301         } else {
1302              this.el.select('.roo-button').on('click', this.onClick, this);
1303              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1304              
1305         }
1306         // why?
1307         if(this.removeClass){
1308             this.el.on('click', this.onClick, this);
1309         }
1310         
1311         if (this.group === true) {
1312              if (this.pressed === false || this.pressed === true) {
1313                 // nothing
1314             } else {
1315                 this.pressed = false;
1316                 this.setActive(this.pressed);
1317             }
1318             
1319         }
1320         
1321         this.el.enableDisplayMode();
1322         
1323     },
1324     onClick : function(e)
1325     {
1326         if (this.disabled) {
1327             return;
1328         }
1329         
1330         Roo.log('button on click ');
1331         if(this.preventDefault){
1332             e.preventDefault();
1333         }
1334         
1335         if (this.group) {
1336             if (this.pressed) {
1337                 // do nothing -
1338                 return;
1339             }
1340             this.setActive(true);
1341             var pi = this.parent().items;
1342             for (var i = 0;i < pi.length;i++) {
1343                 if (this == pi[i]) {
1344                     continue;
1345                 }
1346                 if (pi[i].el.hasClass('roo-button')) {
1347                     pi[i].setActive(false);
1348                 }
1349             }
1350             this.fireEvent('click', this, e);            
1351             return;
1352         }
1353         
1354         if (this.pressed === true || this.pressed === false) {
1355             this.toggleActive(e);
1356         }
1357         
1358         
1359         this.fireEvent('click', this, e);
1360     },
1361     onDblClick: function(e)
1362     {
1363         if (this.disabled) {
1364             return;
1365         }
1366         if(this.preventDefault){
1367             e.preventDefault();
1368         }
1369         this.fireEvent('dblclick', this, e);
1370     },
1371     /**
1372      * Enables this button
1373      */
1374     enable : function()
1375     {
1376         this.disabled = false;
1377         this.el.removeClass('disabled');
1378         this.el.dom.removeAttribute("disabled");
1379     },
1380     
1381     /**
1382      * Disable this button
1383      */
1384     disable : function()
1385     {
1386         this.disabled = true;
1387         this.el.addClass('disabled');
1388         this.el.attr("disabled", "disabled")
1389     },
1390      /**
1391      * sets the active state on/off, 
1392      * @param {Boolean} state (optional) Force a particular state
1393      */
1394     setActive : function(v) {
1395         
1396         this.el[v ? 'addClass' : 'removeClass']('active');
1397         this.pressed = v;
1398     },
1399      /**
1400      * toggles the current active state 
1401      */
1402     toggleActive : function(e)
1403     {
1404         this.setActive(!this.pressed); // this modifies pressed...
1405         this.fireEvent('toggle', this, e, this.pressed);
1406     },
1407      /**
1408      * get the current active state
1409      * @return {boolean} true if it's active
1410      */
1411     isActive : function()
1412     {
1413         return this.el.hasClass('active');
1414     },
1415     /**
1416      * set the text of the first selected button
1417      */
1418     setText : function(str)
1419     {
1420         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1421     },
1422     /**
1423      * get the text of the first selected button
1424      */
1425     getText : function()
1426     {
1427         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1428     },
1429     
1430     setWeight : function(str)
1431     {
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1433         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1434         this.weight = str;
1435         var outline = this.outline ? 'outline-' : '';
1436         if (str == 'default') {
1437             this.el.addClass('btn-default btn-outline-secondary');        
1438             return;
1439         }
1440         this.el.addClass('btn-' + outline + str);        
1441     }
1442     
1443     
1444 });
1445 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1446
1447 Roo.bootstrap.Button.weights = [
1448     'default',
1449     'secondary' ,
1450     'primary',
1451     'success',
1452     'info',
1453     'warning',
1454     'danger',
1455     'link',
1456     'light',
1457     'dark'              
1458    
1459 ];/*
1460  * - LGPL
1461  *
1462  * column
1463  * 
1464  */
1465
1466 /**
1467  * @class Roo.bootstrap.Column
1468  * @extends Roo.bootstrap.Component
1469  * Bootstrap Column class
1470  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1471  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1472  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1473  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1474  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1475  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1476  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1477  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1478  *
1479  * 
1480  * @cfg {Boolean} hidden (true|false) hide the element
1481  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1482  * @cfg {String} fa (ban|check|...) font awesome icon
1483  * @cfg {Number} fasize (1|2|....) font awsome size
1484
1485  * @cfg {String} icon (info-sign|check|...) glyphicon name
1486
1487  * @cfg {String} html content of column.
1488  * 
1489  * @constructor
1490  * Create a new Column
1491  * @param {Object} config The config object
1492  */
1493
1494 Roo.bootstrap.Column = function(config){
1495     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1496 };
1497
1498 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1499     
1500     xs: false,
1501     sm: false,
1502     md: false,
1503     lg: false,
1504     xsoff: false,
1505     smoff: false,
1506     mdoff: false,
1507     lgoff: false,
1508     html: '',
1509     offset: 0,
1510     alert: false,
1511     fa: false,
1512     icon : false,
1513     hidden : false,
1514     fasize : 1,
1515     
1516     getAutoCreate : function(){
1517         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1518         
1519         cfg = {
1520             tag: 'div',
1521             cls: 'column'
1522         };
1523         
1524         var settings=this;
1525         var sizes =   ['xs','sm','md','lg'];
1526         sizes.map(function(size ,ix){
1527             //Roo.log( size + ':' + settings[size]);
1528             
1529             if (settings[size+'off'] !== false) {
1530                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1531             }
1532             
1533             if (settings[size] === false) {
1534                 return;
1535             }
1536             
1537             if (!settings[size]) { // 0 = hidden
1538                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1539                 // bootsrap4
1540                 for (var i = ix; i > -1; i--) {
1541                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1542                 }
1543                 
1544                 
1545                 return;
1546             }
1547             cfg.cls += ' col-' + size + '-' + settings[size] + (
1548                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1549             );
1550             
1551         });
1552         
1553         if (this.hidden) {
1554             cfg.cls += ' hidden';
1555         }
1556         
1557         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1558             cfg.cls +=' alert alert-' + this.alert;
1559         }
1560         
1561         
1562         if (this.html.length) {
1563             cfg.html = this.html;
1564         }
1565         if (this.fa) {
1566             var fasize = '';
1567             if (this.fasize > 1) {
1568                 fasize = ' fa-' + this.fasize + 'x';
1569             }
1570             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1571             
1572             
1573         }
1574         if (this.icon) {
1575             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1576         }
1577         
1578         return cfg;
1579     }
1580    
1581 });
1582
1583  
1584
1585  /*
1586  * - LGPL
1587  *
1588  * page container.
1589  * 
1590  */
1591
1592
1593 /**
1594  * @class Roo.bootstrap.Container
1595  * @extends Roo.bootstrap.Component
1596  * Bootstrap Container class
1597  * @cfg {Boolean} jumbotron is it a jumbotron element
1598  * @cfg {String} html content of element
1599  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1600  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1601  * @cfg {String} header content of header (for panel)
1602  * @cfg {String} footer content of footer (for panel)
1603  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1604  * @cfg {String} tag (header|aside|section) type of HTML tag.
1605  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1606  * @cfg {String} fa font awesome icon
1607  * @cfg {String} icon (info-sign|check|...) glyphicon name
1608  * @cfg {Boolean} hidden (true|false) hide the element
1609  * @cfg {Boolean} expandable (true|false) default false
1610  * @cfg {Boolean} expanded (true|false) default true
1611  * @cfg {String} rheader contet on the right of header
1612  * @cfg {Boolean} clickable (true|false) default false
1613
1614  *     
1615  * @constructor
1616  * Create a new Container
1617  * @param {Object} config The config object
1618  */
1619
1620 Roo.bootstrap.Container = function(config){
1621     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1622     
1623     this.addEvents({
1624         // raw events
1625          /**
1626          * @event expand
1627          * After the panel has been expand
1628          * 
1629          * @param {Roo.bootstrap.Container} this
1630          */
1631         "expand" : true,
1632         /**
1633          * @event collapse
1634          * After the panel has been collapsed
1635          * 
1636          * @param {Roo.bootstrap.Container} this
1637          */
1638         "collapse" : true,
1639         /**
1640          * @event click
1641          * When a element is chick
1642          * @param {Roo.bootstrap.Container} this
1643          * @param {Roo.EventObject} e
1644          */
1645         "click" : true
1646     });
1647 };
1648
1649 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1650     
1651     jumbotron : false,
1652     well: '',
1653     panel : '',
1654     header: '',
1655     footer : '',
1656     sticky: '',
1657     tag : false,
1658     alert : false,
1659     fa: false,
1660     icon : false,
1661     expandable : false,
1662     rheader : '',
1663     expanded : true,
1664     clickable: false,
1665   
1666      
1667     getChildContainer : function() {
1668         
1669         if(!this.el){
1670             return false;
1671         }
1672         
1673         if (this.panel.length) {
1674             return this.el.select('.panel-body',true).first();
1675         }
1676         
1677         return this.el;
1678     },
1679     
1680     
1681     getAutoCreate : function(){
1682         
1683         var cfg = {
1684             tag : this.tag || 'div',
1685             html : '',
1686             cls : ''
1687         };
1688         if (this.jumbotron) {
1689             cfg.cls = 'jumbotron';
1690         }
1691         
1692         
1693         
1694         // - this is applied by the parent..
1695         //if (this.cls) {
1696         //    cfg.cls = this.cls + '';
1697         //}
1698         
1699         if (this.sticky.length) {
1700             
1701             var bd = Roo.get(document.body);
1702             if (!bd.hasClass('bootstrap-sticky')) {
1703                 bd.addClass('bootstrap-sticky');
1704                 Roo.select('html',true).setStyle('height', '100%');
1705             }
1706              
1707             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1708         }
1709         
1710         
1711         if (this.well.length) {
1712             switch (this.well) {
1713                 case 'lg':
1714                 case 'sm':
1715                     cfg.cls +=' well well-' +this.well;
1716                     break;
1717                 default:
1718                     cfg.cls +=' well';
1719                     break;
1720             }
1721         }
1722         
1723         if (this.hidden) {
1724             cfg.cls += ' hidden';
1725         }
1726         
1727         
1728         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1729             cfg.cls +=' alert alert-' + this.alert;
1730         }
1731         
1732         var body = cfg;
1733         
1734         if (this.panel.length) {
1735             cfg.cls += ' panel panel-' + this.panel;
1736             cfg.cn = [];
1737             if (this.header.length) {
1738                 
1739                 var h = [];
1740                 
1741                 if(this.expandable){
1742                     
1743                     cfg.cls = cfg.cls + ' expandable';
1744                     
1745                     h.push({
1746                         tag: 'i',
1747                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1748                     });
1749                     
1750                 }
1751                 
1752                 h.push(
1753                     {
1754                         tag: 'span',
1755                         cls : 'panel-title',
1756                         html : (this.expandable ? '&nbsp;' : '') + this.header
1757                     },
1758                     {
1759                         tag: 'span',
1760                         cls: 'panel-header-right',
1761                         html: this.rheader
1762                     }
1763                 );
1764                 
1765                 cfg.cn.push({
1766                     cls : 'panel-heading',
1767                     style : this.expandable ? 'cursor: pointer' : '',
1768                     cn : h
1769                 });
1770                 
1771             }
1772             
1773             body = false;
1774             cfg.cn.push({
1775                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1776                 html : this.html
1777             });
1778             
1779             
1780             if (this.footer.length) {
1781                 cfg.cn.push({
1782                     cls : 'panel-footer',
1783                     html : this.footer
1784                     
1785                 });
1786             }
1787             
1788         }
1789         
1790         if (body) {
1791             body.html = this.html || cfg.html;
1792             // prefix with the icons..
1793             if (this.fa) {
1794                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1795             }
1796             if (this.icon) {
1797                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1798             }
1799             
1800             
1801         }
1802         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1803             cfg.cls =  'container';
1804         }
1805         
1806         return cfg;
1807     },
1808     
1809     initEvents: function() 
1810     {
1811         if(this.expandable){
1812             var headerEl = this.headerEl();
1813         
1814             if(headerEl){
1815                 headerEl.on('click', this.onToggleClick, this);
1816             }
1817         }
1818         
1819         if(this.clickable){
1820             this.el.on('click', this.onClick, this);
1821         }
1822         
1823     },
1824     
1825     onToggleClick : function()
1826     {
1827         var headerEl = this.headerEl();
1828         
1829         if(!headerEl){
1830             return;
1831         }
1832         
1833         if(this.expanded){
1834             this.collapse();
1835             return;
1836         }
1837         
1838         this.expand();
1839     },
1840     
1841     expand : function()
1842     {
1843         if(this.fireEvent('expand', this)) {
1844             
1845             this.expanded = true;
1846             
1847             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1848             
1849             this.el.select('.panel-body',true).first().removeClass('hide');
1850             
1851             var toggleEl = this.toggleEl();
1852
1853             if(!toggleEl){
1854                 return;
1855             }
1856
1857             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1858         }
1859         
1860     },
1861     
1862     collapse : function()
1863     {
1864         if(this.fireEvent('collapse', this)) {
1865             
1866             this.expanded = false;
1867             
1868             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1869             this.el.select('.panel-body',true).first().addClass('hide');
1870         
1871             var toggleEl = this.toggleEl();
1872
1873             if(!toggleEl){
1874                 return;
1875             }
1876
1877             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1878         }
1879     },
1880     
1881     toggleEl : function()
1882     {
1883         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1884             return;
1885         }
1886         
1887         return this.el.select('.panel-heading .fa',true).first();
1888     },
1889     
1890     headerEl : function()
1891     {
1892         if(!this.el || !this.panel.length || !this.header.length){
1893             return;
1894         }
1895         
1896         return this.el.select('.panel-heading',true).first()
1897     },
1898     
1899     bodyEl : function()
1900     {
1901         if(!this.el || !this.panel.length){
1902             return;
1903         }
1904         
1905         return this.el.select('.panel-body',true).first()
1906     },
1907     
1908     titleEl : function()
1909     {
1910         if(!this.el || !this.panel.length || !this.header.length){
1911             return;
1912         }
1913         
1914         return this.el.select('.panel-title',true).first();
1915     },
1916     
1917     setTitle : function(v)
1918     {
1919         var titleEl = this.titleEl();
1920         
1921         if(!titleEl){
1922             return;
1923         }
1924         
1925         titleEl.dom.innerHTML = v;
1926     },
1927     
1928     getTitle : function()
1929     {
1930         
1931         var titleEl = this.titleEl();
1932         
1933         if(!titleEl){
1934             return '';
1935         }
1936         
1937         return titleEl.dom.innerHTML;
1938     },
1939     
1940     setRightTitle : function(v)
1941     {
1942         var t = this.el.select('.panel-header-right',true).first();
1943         
1944         if(!t){
1945             return;
1946         }
1947         
1948         t.dom.innerHTML = v;
1949     },
1950     
1951     onClick : function(e)
1952     {
1953         e.preventDefault();
1954         
1955         this.fireEvent('click', this, e);
1956     }
1957 });
1958
1959  /*
1960  *  - LGPL
1961  *
1962  *  This is BS4's Card element.. - similar to our containers probably..
1963  * 
1964  */
1965 /**
1966  * @class Roo.bootstrap.Card
1967  * @extends Roo.bootstrap.Component
1968  * Bootstrap Card class
1969  *
1970  *
1971  * possible... may not be implemented..
1972  * @cfg {String} header_image  src url of image.
1973  * @cfg {String|Object} header
1974  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1975  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1976  * 
1977  * @cfg {String} title
1978  * @cfg {String} subtitle
1979  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1980  * @cfg {String} footer
1981  
1982  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1983  * 
1984  * @cfg {String} margin (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1990  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1991  *
1992  * @cfg {String} padding (0|1|2|3|4|5)
1993  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1994  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1995  * @cfg {String} padding_left (0|1|2|3|4|5)
1996  * @cfg {String} padding_right (0|1|2|3|4|5)
1997  * @cfg {String} padding_x (0|1|2|3|4|5)
1998  * @cfg {String} padding_y (0|1|2|3|4|5)
1999  *
2000  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2005  
2006  * @config {Boolean} dragable  if this card can be dragged.
2007  * @config {String} drag_group  group for drag
2008  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2009  * @config {String} drop_group  group for drag
2010  * 
2011  * @config {Boolean} collapsable can the body be collapsed.
2012  * @config {Boolean} collapsed is the body collapsed when rendered...
2013  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2014  * @config {Boolean} rotated is the body rotated when rendered...
2015  * 
2016  * @constructor
2017  * Create a new Container
2018  * @param {Object} config The config object
2019  */
2020
2021 Roo.bootstrap.Card = function(config){
2022     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2023     
2024     this.addEvents({
2025          // raw events
2026         /**
2027          * @event drop
2028          * When a element a card is dropped
2029          * @param {Roo.bootstrap.Card} this
2030          *
2031          * 
2032          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2033          * @param {String} position 'above' or 'below'
2034          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2035         
2036          */
2037         'drop' : true,
2038          /**
2039          * @event rotate
2040          * When a element a card is rotate
2041          * @param {Roo.bootstrap.Card} this
2042          * @param {Roo.Element} n the node being dropped?
2043          * @param {Boolean} rotate status
2044          */
2045         'rotate' : true,
2046         /**
2047          * @event cardover
2048          * When a card element is dragged over ready to drop (return false to block dropable)
2049          * @param {Roo.bootstrap.Card} this
2050          * @param {Object} data from dragdrop 
2051          */
2052          'cardover' : true
2053          
2054     });
2055 };
2056
2057
2058 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2059     
2060     
2061     weight : '',
2062     
2063     margin: '', /// may be better in component?
2064     margin_top: '', 
2065     margin_bottom: '', 
2066     margin_left: '',
2067     margin_right: '',
2068     margin_x: '',
2069     margin_y: '',
2070     
2071     padding : '',
2072     padding_top: '', 
2073     padding_bottom: '', 
2074     padding_left: '',
2075     padding_right: '',
2076     padding_x: '',
2077     padding_y: '',
2078     
2079     display: '', 
2080     display_xs: '', 
2081     display_sm: '', 
2082     display_lg: '',
2083     display_xl: '',
2084  
2085     header_image  : '',
2086     header : '',
2087     header_size : 0,
2088     title : '',
2089     subtitle : '',
2090     html : '',
2091     footer: '',
2092
2093     collapsable : false,
2094     collapsed : false,
2095     rotateable : false,
2096     rotated : false,
2097     
2098     dragable : false,
2099     drag_group : false,
2100     dropable : false,
2101     drop_group : false,
2102     childContainer : false,
2103     dropEl : false, /// the dom placeholde element that indicates drop location.
2104     containerEl: false, // body container
2105     bodyEl: false, // card-body
2106     headerContainerEl : false, //
2107     headerEl : false,
2108     header_imageEl : false,
2109     
2110     
2111     layoutCls : function()
2112     {
2113         var cls = '';
2114         var t = this;
2115         Roo.log(this.margin_bottom.length);
2116         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2117             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2118             
2119             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2120                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2121             }
2122             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2123                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2124             }
2125         });
2126         
2127         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2128             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2129                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2130             }
2131         });
2132         
2133         // more generic support?
2134         if (this.hidden) {
2135             cls += ' d-none';
2136         }
2137         
2138         return cls;
2139     },
2140  
2141        // Roo.log("Call onRender: " + this.xtype);
2142         /*  We are looking at something like this.
2143 <div class="card">
2144     <img src="..." class="card-img-top" alt="...">
2145     <div class="card-body">
2146         <h5 class="card-title">Card title</h5>
2147          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2148
2149         >> this bit is really the body...
2150         <div> << we will ad dthis in hopefully it will not break shit.
2151         
2152         ** card text does not actually have any styling...
2153         
2154             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2155         
2156         </div> <<
2157           <a href="#" class="card-link">Card link</a>
2158           
2159     </div>
2160     <div class="card-footer">
2161         <small class="text-muted">Last updated 3 mins ago</small>
2162     </div>
2163 </div>
2164          */
2165     getAutoCreate : function(){
2166         
2167         var cfg = {
2168             tag : 'div',
2169             cls : 'card',
2170             cn : [ ]
2171         };
2172         
2173         if (this.weight.length && this.weight != 'light') {
2174             cfg.cls += ' text-white';
2175         } else {
2176             cfg.cls += ' text-dark'; // need as it's nested..
2177         }
2178         if (this.weight.length) {
2179             cfg.cls += ' bg-' + this.weight;
2180         }
2181         
2182         cfg.cls += ' ' + this.layoutCls(); 
2183         
2184         var hdr = false;
2185         var hdr_ctr = false;
2186         if (this.header.length) {
2187             hdr = {
2188                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2189                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2190                 cn : []
2191             };
2192             cfg.cn.push(hdr);
2193             hdr_ctr = hdr;
2194         } else {
2195             hdr = {
2196                 tag : 'div',
2197                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2198                 cn : []
2199             };
2200             cfg.cn.push(hdr);
2201             hdr_ctr = hdr;
2202         }
2203         if (this.collapsable) {
2204             hdr_ctr = {
2205             tag : 'a',
2206             cls : 'd-block user-select-none',
2207             cn: [
2208                     {
2209                         tag: 'i',
2210                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2211                     }
2212                    
2213                 ]
2214             };
2215             hdr.cn.push(hdr_ctr);
2216         }
2217         
2218         hdr_ctr.cn.push(        {
2219             tag: 'span',
2220             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2221             html : this.header
2222         });
2223         
2224         
2225         if (this.header_image.length) {
2226             cfg.cn.push({
2227                 tag : 'img',
2228                 cls : 'card-img-top',
2229                 src: this.header_image // escape?
2230             });
2231         } else {
2232             cfg.cn.push({
2233                     tag : 'div',
2234                     cls : 'card-img-top d-none' 
2235                 });
2236         }
2237             
2238         var body = {
2239             tag : 'div',
2240             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2241             cn : []
2242         };
2243         var obody = body;
2244         if (this.collapsable || this.rotateable) {
2245             obody = {
2246                 tag: 'div',
2247                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2248                 cn : [  body ]
2249             };
2250         }
2251         
2252         cfg.cn.push(obody);
2253         
2254         if (this.title.length) {
2255             body.cn.push({
2256                 tag : 'div',
2257                 cls : 'card-title',
2258                 src: this.title // escape?
2259             });
2260         }  
2261         
2262         if (this.subtitle.length) {
2263             body.cn.push({
2264                 tag : 'div',
2265                 cls : 'card-title',
2266                 src: this.subtitle // escape?
2267             });
2268         }
2269         
2270         body.cn.push({
2271             tag : 'div',
2272             cls : 'roo-card-body-ctr'
2273         });
2274         
2275         if (this.html.length) {
2276             body.cn.push({
2277                 tag: 'div',
2278                 html : this.html
2279             });
2280         }
2281         // fixme ? handle objects?
2282         
2283         if (this.footer.length) {
2284            
2285             cfg.cn.push({
2286                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2287                 html : this.footer
2288             });
2289             
2290         } else {
2291             cfg.cn.push({cls : 'card-footer d-none'});
2292         }
2293         
2294         // footer...
2295         
2296         return cfg;
2297     },
2298     
2299     
2300     getCardHeader : function()
2301     {
2302         var  ret = this.el.select('.card-header',true).first();
2303         if (ret.hasClass('d-none')) {
2304             ret.removeClass('d-none');
2305         }
2306         
2307         return ret;
2308     },
2309     getCardFooter : function()
2310     {
2311         var  ret = this.el.select('.card-footer',true).first();
2312         if (ret.hasClass('d-none')) {
2313             ret.removeClass('d-none');
2314         }
2315         
2316         return ret;
2317     },
2318     getCardImageTop : function()
2319     {
2320         var  ret = this.header_imageEl;
2321         if (ret.hasClass('d-none')) {
2322             ret.removeClass('d-none');
2323         }
2324             
2325         return ret;
2326     },
2327     
2328     getChildContainer : function()
2329     {
2330         
2331         if(!this.el){
2332             return false;
2333         }
2334         return this.el.select('.roo-card-body-ctr',true).first();    
2335     },
2336     
2337     initEvents: function() 
2338     {
2339         this.bodyEl = this.el.select('.card-body',true).first(); 
2340         this.containerEl = this.getChildContainer();
2341         if(this.dragable){
2342             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2343                     containerScroll: true,
2344                     ddGroup: this.drag_group || 'default_card_drag_group'
2345             });
2346             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2347         }
2348         if (this.dropable) {
2349             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2350                 containerScroll: true,
2351                 ddGroup: this.drop_group || 'default_card_drag_group'
2352             });
2353             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2354             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2355             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2356             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2357             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2358         }
2359         
2360         if (this.collapsable) {
2361             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2362         }
2363         if (this.rotateable) {
2364             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2365         }
2366         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2367          
2368         this.footerEl = this.el.select('.card-footer',true).first();
2369         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2370         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2371         this.headerEl = this.el.select('.card-header',true).first();
2372         
2373         if (this.rotated) {
2374             this.el.addClass('roo-card-rotated');
2375             this.fireEvent('rotate', this, true);
2376         }
2377         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2378         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2379         
2380     },
2381     getDragData : function(e)
2382     {
2383         var target = this.getEl();
2384         if (target) {
2385             //this.handleSelection(e);
2386             
2387             var dragData = {
2388                 source: this,
2389                 copy: false,
2390                 nodes: this.getEl(),
2391                 records: []
2392             };
2393             
2394             
2395             dragData.ddel = target.dom ;    // the div element
2396             Roo.log(target.getWidth( ));
2397             dragData.ddel.style.width = target.getWidth() + 'px';
2398             
2399             return dragData;
2400         }
2401         return false;
2402     },
2403     /**
2404     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2405     *    whole Element becomes the target, and this causes the drop gesture to append.
2406     *
2407     *    Returns an object:
2408     *     {
2409            
2410            position : 'below' or 'above'
2411            card  : relateive to card OBJECT (or true for no cards listed)
2412            items_n : relative to nth item in list
2413            card_n : relative to  nth card in list
2414     }
2415     *
2416     *    
2417     */
2418     getTargetFromEvent : function(e, dragged_card_el)
2419     {
2420         var target = e.getTarget();
2421         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2422             target = target.parentNode;
2423         }
2424         
2425         var ret = {
2426             position: '',
2427             cards : [],
2428             card_n : -1,
2429             items_n : -1,
2430             card : false 
2431         };
2432         
2433         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2434         // see if target is one of the 'cards'...
2435         
2436         
2437         //Roo.log(this.items.length);
2438         var pos = false;
2439         
2440         var last_card_n = 0;
2441         var cards_len  = 0;
2442         for (var i = 0;i< this.items.length;i++) {
2443             
2444             if (!this.items[i].el.hasClass('card')) {
2445                  continue;
2446             }
2447             pos = this.getDropPoint(e, this.items[i].el.dom);
2448             
2449             cards_len = ret.cards.length;
2450             //Roo.log(this.items[i].el.dom.id);
2451             ret.cards.push(this.items[i]);
2452             last_card_n  = i;
2453             if (ret.card_n < 0 && pos == 'above') {
2454                 ret.position = cards_len > 0 ? 'below' : pos;
2455                 ret.items_n = i > 0 ? i - 1 : 0;
2456                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2457                 ret.card = ret.cards[ret.card_n];
2458             }
2459         }
2460         if (!ret.cards.length) {
2461             ret.card = true;
2462             ret.position = 'below';
2463             ret.items_n;
2464             return ret;
2465         }
2466         // could not find a card.. stick it at the end..
2467         if (ret.card_n < 0) {
2468             ret.card_n = last_card_n;
2469             ret.card = ret.cards[last_card_n];
2470             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2471             ret.position = 'below';
2472         }
2473         
2474         if (this.items[ret.items_n].el == dragged_card_el) {
2475             return false;
2476         }
2477         
2478         if (ret.position == 'below') {
2479             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2480             
2481             if (card_after  && card_after.el == dragged_card_el) {
2482                 return false;
2483             }
2484             return ret;
2485         }
2486         
2487         // its's after ..
2488         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2489         
2490         if (card_before  && card_before.el == dragged_card_el) {
2491             return false;
2492         }
2493         
2494         return ret;
2495     },
2496     
2497     onNodeEnter : function(n, dd, e, data){
2498         return false;
2499     },
2500     onNodeOver : function(n, dd, e, data)
2501     {
2502        
2503         var target_info = this.getTargetFromEvent(e,data.source.el);
2504         if (target_info === false) {
2505             this.dropPlaceHolder('hide');
2506             return false;
2507         }
2508         Roo.log(['getTargetFromEvent', target_info ]);
2509         
2510         
2511         if (this.fireEvent('cardover', this, [ data ]) === false) {
2512             return false;
2513         }
2514         
2515         this.dropPlaceHolder('show', target_info,data);
2516         
2517         return false; 
2518     },
2519     onNodeOut : function(n, dd, e, data){
2520         this.dropPlaceHolder('hide');
2521      
2522     },
2523     onNodeDrop : function(n, dd, e, data)
2524     {
2525         
2526         // call drop - return false if
2527         
2528         // this could actually fail - if the Network drops..
2529         // we will ignore this at present..- client should probably reload
2530         // the whole set of cards if stuff like that fails.
2531         
2532         
2533         var info = this.getTargetFromEvent(e,data.source.el);
2534         if (info === false) {
2535             return false;
2536         }
2537         this.dropPlaceHolder('hide');
2538   
2539           
2540     
2541         this.acceptCard(data.source, info.position, info.card, info.items_n);
2542         return true;
2543          
2544     },
2545     firstChildCard : function()
2546     {
2547         for (var i = 0;i< this.items.length;i++) {
2548             
2549             if (!this.items[i].el.hasClass('card')) {
2550                  continue;
2551             }
2552             return this.items[i];
2553         }
2554         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2555     },
2556     /**
2557      * accept card
2558      *
2559      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2560      */
2561     acceptCard : function(move_card,  position, next_to_card )
2562     {
2563         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2564             return false;
2565         }
2566         
2567         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2568         
2569         move_card.parent().removeCard(move_card);
2570         
2571         
2572         var dom = move_card.el.dom;
2573         dom.style.width = ''; // clear with - which is set by drag.
2574         
2575         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2576             var cardel = next_to_card.el.dom;
2577             
2578             if (position == 'above' ) {
2579                 cardel.parentNode.insertBefore(dom, cardel);
2580             } else if (cardel.nextSibling) {
2581                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2582             } else {
2583                 cardel.parentNode.append(dom);
2584             }
2585         } else {
2586             // card container???
2587             this.containerEl.dom.append(dom);
2588         }
2589         
2590         //FIXME HANDLE card = true 
2591         
2592         // add this to the correct place in items.
2593         
2594         // remove Card from items.
2595         
2596        
2597         if (this.items.length) {
2598             var nitems = [];
2599             //Roo.log([info.items_n, info.position, this.items.length]);
2600             for (var i =0; i < this.items.length; i++) {
2601                 if (i == to_items_n && position == 'above') {
2602                     nitems.push(move_card);
2603                 }
2604                 nitems.push(this.items[i]);
2605                 if (i == to_items_n && position == 'below') {
2606                     nitems.push(move_card);
2607                 }
2608             }
2609             this.items = nitems;
2610             Roo.log(this.items);
2611         } else {
2612             this.items.push(move_card);
2613         }
2614         
2615         move_card.parentId = this.id;
2616         
2617         return true;
2618         
2619         
2620     },
2621     removeCard : function(c)
2622     {
2623         this.items = this.items.filter(function(e) { return e != c });
2624  
2625         var dom = c.el.dom;
2626         dom.parentNode.removeChild(dom);
2627         dom.style.width = ''; // clear with - which is set by drag.
2628         c.parentId = false;
2629         
2630     },
2631     
2632     /**    Decide whether to drop above or below a View node. */
2633     getDropPoint : function(e, n, dd)
2634     {
2635         if (dd) {
2636              return false;
2637         }
2638         if (n == this.containerEl.dom) {
2639             return "above";
2640         }
2641         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2642         var c = t + (b - t) / 2;
2643         var y = Roo.lib.Event.getPageY(e);
2644         if(y <= c) {
2645             return "above";
2646         }else{
2647             return "below";
2648         }
2649     },
2650     onToggleCollapse : function(e)
2651         {
2652         if (this.collapsed) {
2653             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2654             this.collapsableEl.addClass('show');
2655             this.collapsed = false;
2656             return;
2657         }
2658         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2659         this.collapsableEl.removeClass('show');
2660         this.collapsed = true;
2661         
2662     
2663     },
2664     
2665     onToggleRotate : function(e)
2666     {
2667         this.collapsableEl.removeClass('show');
2668         this.footerEl.removeClass('d-none');
2669         this.el.removeClass('roo-card-rotated');
2670         this.el.removeClass('d-none');
2671         if (this.rotated) {
2672             
2673             this.collapsableEl.addClass('show');
2674             this.rotated = false;
2675             this.fireEvent('rotate', this, this.rotated);
2676             return;
2677         }
2678         this.el.addClass('roo-card-rotated');
2679         this.footerEl.addClass('d-none');
2680         this.el.select('.roo-collapsable').removeClass('show');
2681         
2682         this.rotated = true;
2683         this.fireEvent('rotate', this, this.rotated);
2684     
2685     },
2686     
2687     dropPlaceHolder: function (action, info, data)
2688     {
2689         if (this.dropEl === false) {
2690             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2691             cls : 'd-none'
2692             },true);
2693         }
2694         this.dropEl.removeClass(['d-none', 'd-block']);        
2695         if (action == 'hide') {
2696             
2697             this.dropEl.addClass('d-none');
2698             return;
2699         }
2700         // FIXME - info.card == true!!!
2701         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2702         
2703         if (info.card !== true) {
2704             var cardel = info.card.el.dom;
2705             
2706             if (info.position == 'above') {
2707                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2708             } else if (cardel.nextSibling) {
2709                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2710             } else {
2711                 cardel.parentNode.append(this.dropEl.dom);
2712             }
2713         } else {
2714             // card container???
2715             this.containerEl.dom.append(this.dropEl.dom);
2716         }
2717         
2718         this.dropEl.addClass('d-block roo-card-dropzone');
2719         
2720         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2721         
2722         
2723     
2724     
2725     
2726     },
2727     setHeaderText: function(html)
2728     {
2729         this.header = html;
2730         if (this.headerContainerEl) {
2731             this.headerContainerEl.dom.innerHTML = html;
2732         }
2733     },
2734     onHeaderImageLoad : function(ev, he)
2735     {
2736         if (!this.header_image_fit_square) {
2737             return;
2738         }
2739         
2740         var hw = he.naturalHeight / he.naturalWidth;
2741         // wide image = < 0
2742         // tall image = > 1
2743         //var w = he.dom.naturalWidth;
2744         var ww = he.width;
2745         he.style.left =  0;
2746         he.style.position =  'relative';
2747         if (hw > 1) {
2748             var nw = (ww * (1/hw));
2749             Roo.get(he).setSize( ww * (1/hw),  ww);
2750             he.style.left =  ((ww - nw)/ 2) + 'px';
2751             he.style.position =  'relative';
2752         }
2753
2754     }
2755
2756     
2757 });
2758
2759 /*
2760  * - LGPL
2761  *
2762  * Card header - holder for the card header elements.
2763  * 
2764  */
2765
2766 /**
2767  * @class Roo.bootstrap.CardHeader
2768  * @extends Roo.bootstrap.Element
2769  * Bootstrap CardHeader class
2770  * @constructor
2771  * Create a new Card Header - that you can embed children into
2772  * @param {Object} config The config object
2773  */
2774
2775 Roo.bootstrap.CardHeader = function(config){
2776     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2777 };
2778
2779 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2780     
2781     
2782     container_method : 'getCardHeader' 
2783     
2784      
2785     
2786     
2787    
2788 });
2789
2790  
2791
2792  /*
2793  * - LGPL
2794  *
2795  * Card footer - holder for the card footer elements.
2796  * 
2797  */
2798
2799 /**
2800  * @class Roo.bootstrap.CardFooter
2801  * @extends Roo.bootstrap.Element
2802  * Bootstrap CardFooter class
2803  * @constructor
2804  * Create a new Card Footer - that you can embed children into
2805  * @param {Object} config The config object
2806  */
2807
2808 Roo.bootstrap.CardFooter = function(config){
2809     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2810 };
2811
2812 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2813     
2814     
2815     container_method : 'getCardFooter' 
2816     
2817      
2818     
2819     
2820    
2821 });
2822
2823  
2824
2825  /*
2826  * - LGPL
2827  *
2828  * Card header - holder for the card header elements.
2829  * 
2830  */
2831
2832 /**
2833  * @class Roo.bootstrap.CardImageTop
2834  * @extends Roo.bootstrap.Element
2835  * Bootstrap CardImageTop class
2836  * @constructor
2837  * Create a new Card Image Top container
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.CardImageTop = function(config){
2842     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2843 };
2844
2845 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2846     
2847    
2848     container_method : 'getCardImageTop' 
2849     
2850      
2851     
2852    
2853 });
2854
2855  
2856
2857  
2858 /*
2859 * Licence: LGPL
2860 */
2861
2862 /**
2863  * @class Roo.bootstrap.ButtonUploader
2864  * @extends Roo.bootstrap.Button
2865  * Bootstrap Button Uploader class - it's a button which when you add files to it
2866  *
2867  * 
2868  * @cfg {Number} errorTimeout default 3000
2869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2870  * @cfg {Array}  html The button text.
2871  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2872  *
2873  * @constructor
2874  * Create a new CardUploader
2875  * @param {Object} config The config object
2876  */
2877
2878 Roo.bootstrap.ButtonUploader = function(config){
2879     
2880  
2881     
2882     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2883     
2884      
2885      this.addEvents({
2886          // raw events
2887         /**
2888          * @event beforeselect
2889          * When button is pressed, before show upload files dialog is shown
2890          * @param {Roo.bootstrap.UploaderButton} this
2891          *
2892          */
2893         'beforeselect' : true,
2894          /**
2895          * @event fired when files have been selected, 
2896          * When a the download link is clicked
2897          * @param {Roo.bootstrap.UploaderButton} this
2898          * @param {Array} Array of files that have been uploaded
2899          */
2900         'uploaded' : true
2901         
2902     });
2903 };
2904  
2905 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2906     
2907      
2908     errorTimeout : 3000,
2909      
2910     images : false,
2911    
2912     fileCollection : false,
2913     allowBlank : true,
2914     
2915     multiple : true,
2916     
2917     getAutoCreate : function()
2918     {
2919         var im = {
2920             tag: 'input',
2921             type : 'file',
2922             cls : 'd-none  roo-card-upload-selector' 
2923           
2924         };
2925         if (this.multiple) {
2926             im.multiple = 'multiple';
2927         }
2928         
2929         return  {
2930             cls :'div' ,
2931             cn : [
2932                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2933                 im
2934
2935             ]
2936         };
2937            
2938          
2939     },
2940      
2941    
2942     initEvents : function()
2943     {
2944         
2945         Roo.bootstrap.Button.prototype.initEvents.call(this);
2946         
2947         
2948         
2949         
2950         
2951         this.urlAPI = (window.createObjectURL && window) || 
2952                                 (window.URL && URL.revokeObjectURL && URL) || 
2953                                 (window.webkitURL && webkitURL);
2954                         
2955          
2956          
2957          
2958         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2959         
2960         this.selectorEl.on('change', this.onFileSelected, this);
2961          
2962          
2963        
2964     },
2965     
2966    
2967     onClick : function(e)
2968     {
2969         e.preventDefault();
2970         
2971         if ( this.fireEvent('beforeselect', this) === false) {
2972             return;
2973         }
2974          
2975         this.selectorEl.dom.click();
2976          
2977     },
2978     
2979     onFileSelected : function(e)
2980     {
2981         e.preventDefault();
2982         
2983         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2984             return;
2985         }
2986         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2987         this.selectorEl.dom.value  = '';// hopefully reset..
2988         
2989         this.fireEvent('uploaded', this,  files );
2990         
2991     },
2992     
2993        
2994    
2995     
2996     /**
2997      * addCard - add an Attachment to the uploader
2998      * @param data - the data about the image to upload
2999      *
3000      * {
3001           id : 123
3002           title : "Title of file",
3003           is_uploaded : false,
3004           src : "http://.....",
3005           srcfile : { the File upload object },
3006           mimetype : file.type,
3007           preview : false,
3008           is_deleted : 0
3009           .. any other data...
3010         }
3011      *
3012      * 
3013     */
3014      
3015     reset: function()
3016     {
3017          
3018          this.selectorEl
3019     } 
3020     
3021     
3022     
3023     
3024 });
3025  /*
3026  * - LGPL
3027  *
3028  * image
3029  * 
3030  */
3031
3032
3033 /**
3034  * @class Roo.bootstrap.Img
3035  * @extends Roo.bootstrap.Component
3036  * Bootstrap Img class
3037  * @cfg {Boolean} imgResponsive false | true
3038  * @cfg {String} border rounded | circle | thumbnail
3039  * @cfg {String} src image source
3040  * @cfg {String} alt image alternative text
3041  * @cfg {String} href a tag href
3042  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3043  * @cfg {String} xsUrl xs image source
3044  * @cfg {String} smUrl sm image source
3045  * @cfg {String} mdUrl md image source
3046  * @cfg {String} lgUrl lg image source
3047  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3048  * 
3049  * @constructor
3050  * Create a new Input
3051  * @param {Object} config The config object
3052  */
3053
3054 Roo.bootstrap.Img = function(config){
3055     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3056     
3057     this.addEvents({
3058         // img events
3059         /**
3060          * @event click
3061          * The img click event for the img.
3062          * @param {Roo.EventObject} e
3063          */
3064         "click" : true,
3065         /**
3066          * @event load
3067          * The when any image loads
3068          * @param {Roo.EventObject} e
3069          */
3070         "load" : true
3071     });
3072 };
3073
3074 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3075     
3076     imgResponsive: true,
3077     border: '',
3078     src: 'about:blank',
3079     href: false,
3080     target: false,
3081     xsUrl: '',
3082     smUrl: '',
3083     mdUrl: '',
3084     lgUrl: '',
3085     backgroundContain : false,
3086
3087     getAutoCreate : function()
3088     {   
3089         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3090             return this.createSingleImg();
3091         }
3092         
3093         var cfg = {
3094             tag: 'div',
3095             cls: 'roo-image-responsive-group',
3096             cn: []
3097         };
3098         var _this = this;
3099         
3100         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3101             
3102             if(!_this[size + 'Url']){
3103                 return;
3104             }
3105             
3106             var img = {
3107                 tag: 'img',
3108                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3109                 html: _this.html || cfg.html,
3110                 src: _this[size + 'Url']
3111             };
3112             
3113             img.cls += ' roo-image-responsive-' + size;
3114             
3115             var s = ['xs', 'sm', 'md', 'lg'];
3116             
3117             s.splice(s.indexOf(size), 1);
3118             
3119             Roo.each(s, function(ss){
3120                 img.cls += ' hidden-' + ss;
3121             });
3122             
3123             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3124                 cfg.cls += ' img-' + _this.border;
3125             }
3126             
3127             if(_this.alt){
3128                 cfg.alt = _this.alt;
3129             }
3130             
3131             if(_this.href){
3132                 var a = {
3133                     tag: 'a',
3134                     href: _this.href,
3135                     cn: [
3136                         img
3137                     ]
3138                 };
3139
3140                 if(this.target){
3141                     a.target = _this.target;
3142                 }
3143             }
3144             
3145             cfg.cn.push((_this.href) ? a : img);
3146             
3147         });
3148         
3149         return cfg;
3150     },
3151     
3152     createSingleImg : function()
3153     {
3154         var cfg = {
3155             tag: 'img',
3156             cls: (this.imgResponsive) ? 'img-responsive' : '',
3157             html : null,
3158             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3159         };
3160         
3161         if (this.backgroundContain) {
3162             cfg.cls += ' background-contain';
3163         }
3164         
3165         cfg.html = this.html || cfg.html;
3166         
3167         if (this.backgroundContain) {
3168             cfg.style="background-image: url(" + this.src + ')';
3169         } else {
3170             cfg.src = this.src || cfg.src;
3171         }
3172         
3173         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3174             cfg.cls += ' img-' + this.border;
3175         }
3176         
3177         if(this.alt){
3178             cfg.alt = this.alt;
3179         }
3180         
3181         if(this.href){
3182             var a = {
3183                 tag: 'a',
3184                 href: this.href,
3185                 cn: [
3186                     cfg
3187                 ]
3188             };
3189             
3190             if(this.target){
3191                 a.target = this.target;
3192             }
3193             
3194         }
3195         
3196         return (this.href) ? a : cfg;
3197     },
3198     
3199     initEvents: function() 
3200     {
3201         if(!this.href){
3202             this.el.on('click', this.onClick, this);
3203         }
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.on('load', this.onImageLoad, this);
3206         } else {
3207             // not sure if this works.. not tested
3208             this.el.select('img', true).on('load', this.onImageLoad, this);
3209         }
3210         
3211     },
3212     
3213     onClick : function(e)
3214     {
3215         Roo.log('img onclick');
3216         this.fireEvent('click', this, e);
3217     },
3218     onImageLoad: function(e)
3219     {
3220         Roo.log('img load');
3221         this.fireEvent('load', this, e);
3222     },
3223     
3224     /**
3225      * Sets the url of the image - used to update it
3226      * @param {String} url the url of the image
3227      */
3228     
3229     setSrc : function(url)
3230     {
3231         this.src =  url;
3232         
3233         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3234             if (this.backgroundContain) {
3235                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3236             } else {
3237                 this.el.dom.src =  url;
3238             }
3239             return;
3240         }
3241         
3242         this.el.select('img', true).first().dom.src =  url;
3243     }
3244     
3245     
3246    
3247 });
3248
3249  /*
3250  * - LGPL
3251  *
3252  * image
3253  * 
3254  */
3255
3256
3257 /**
3258  * @class Roo.bootstrap.Link
3259  * @extends Roo.bootstrap.Component
3260  * Bootstrap Link Class
3261  * @cfg {String} alt image alternative text
3262  * @cfg {String} href a tag href
3263  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3264  * @cfg {String} html the content of the link.
3265  * @cfg {String} anchor name for the anchor link
3266  * @cfg {String} fa - favicon
3267
3268  * @cfg {Boolean} preventDefault (true | false) default false
3269
3270  * 
3271  * @constructor
3272  * Create a new Input
3273  * @param {Object} config The config object
3274  */
3275
3276 Roo.bootstrap.Link = function(config){
3277     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3278     
3279     this.addEvents({
3280         // img events
3281         /**
3282          * @event click
3283          * The img click event for the img.
3284          * @param {Roo.EventObject} e
3285          */
3286         "click" : true
3287     });
3288 };
3289
3290 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3291     
3292     href: false,
3293     target: false,
3294     preventDefault: false,
3295     anchor : false,
3296     alt : false,
3297     fa: false,
3298
3299
3300     getAutoCreate : function()
3301     {
3302         var html = this.html || '';
3303         
3304         if (this.fa !== false) {
3305             html = '<i class="fa fa-' + this.fa + '"></i>';
3306         }
3307         var cfg = {
3308             tag: 'a'
3309         };
3310         // anchor's do not require html/href...
3311         if (this.anchor === false) {
3312             cfg.html = html;
3313             cfg.href = this.href || '#';
3314         } else {
3315             cfg.name = this.anchor;
3316             if (this.html !== false || this.fa !== false) {
3317                 cfg.html = html;
3318             }
3319             if (this.href !== false) {
3320                 cfg.href = this.href;
3321             }
3322         }
3323         
3324         if(this.alt !== false){
3325             cfg.alt = this.alt;
3326         }
3327         
3328         
3329         if(this.target !== false) {
3330             cfg.target = this.target;
3331         }
3332         
3333         return cfg;
3334     },
3335     
3336     initEvents: function() {
3337         
3338         if(!this.href || this.preventDefault){
3339             this.el.on('click', this.onClick, this);
3340         }
3341     },
3342     
3343     onClick : function(e)
3344     {
3345         if(this.preventDefault){
3346             e.preventDefault();
3347         }
3348         //Roo.log('img onclick');
3349         this.fireEvent('click', this, e);
3350     }
3351    
3352 });
3353
3354  /*
3355  * - LGPL
3356  *
3357  * header
3358  * 
3359  */
3360
3361 /**
3362  * @class Roo.bootstrap.Header
3363  * @extends Roo.bootstrap.Component
3364  * Bootstrap Header class
3365  * @cfg {String} html content of header
3366  * @cfg {Number} level (1|2|3|4|5|6) default 1
3367  * 
3368  * @constructor
3369  * Create a new Header
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Header  = function(config){
3375     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3376 };
3377
3378 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3379     
3380     //href : false,
3381     html : false,
3382     level : 1,
3383     
3384     
3385     
3386     getAutoCreate : function(){
3387         
3388         
3389         
3390         var cfg = {
3391             tag: 'h' + (1 *this.level),
3392             html: this.html || ''
3393         } ;
3394         
3395         return cfg;
3396     }
3397    
3398 });
3399
3400  
3401
3402  /*
3403  * Based on:
3404  * Ext JS Library 1.1.1
3405  * Copyright(c) 2006-2007, Ext JS, LLC.
3406  *
3407  * Originally Released Under LGPL - original licence link has changed is not relivant.
3408  *
3409  * Fork - LGPL
3410  * <script type="text/javascript">
3411  */
3412  
3413 /**
3414  * @class Roo.bootstrap.MenuMgr
3415  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3416  * @singleton
3417  */
3418 Roo.bootstrap.MenuMgr = function(){
3419    var menus, active, groups = {}, attached = false, lastShow = new Date();
3420
3421    // private - called when first menu is created
3422    function init(){
3423        menus = {};
3424        active = new Roo.util.MixedCollection();
3425        Roo.get(document).addKeyListener(27, function(){
3426            if(active.length > 0){
3427                hideAll();
3428            }
3429        });
3430    }
3431
3432    // private
3433    function hideAll(){
3434        if(active && active.length > 0){
3435            var c = active.clone();
3436            c.each(function(m){
3437                m.hide();
3438            });
3439        }
3440    }
3441
3442    // private
3443    function onHide(m){
3444        active.remove(m);
3445        if(active.length < 1){
3446            Roo.get(document).un("mouseup", onMouseDown);
3447             
3448            attached = false;
3449        }
3450    }
3451
3452    // private
3453    function onShow(m){
3454        var last = active.last();
3455        lastShow = new Date();
3456        active.add(m);
3457        if(!attached){
3458           Roo.get(document).on("mouseup", onMouseDown);
3459            
3460            attached = true;
3461        }
3462        if(m.parentMenu){
3463           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3464           m.parentMenu.activeChild = m;
3465        }else if(last && last.isVisible()){
3466           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3467        }
3468    }
3469
3470    // private
3471    function onBeforeHide(m){
3472        if(m.activeChild){
3473            m.activeChild.hide();
3474        }
3475        if(m.autoHideTimer){
3476            clearTimeout(m.autoHideTimer);
3477            delete m.autoHideTimer;
3478        }
3479    }
3480
3481    // private
3482    function onBeforeShow(m){
3483        var pm = m.parentMenu;
3484        if(!pm && !m.allowOtherMenus){
3485            hideAll();
3486        }else if(pm && pm.activeChild && active != m){
3487            pm.activeChild.hide();
3488        }
3489    }
3490
3491    // private this should really trigger on mouseup..
3492    function onMouseDown(e){
3493         Roo.log("on Mouse Up");
3494         
3495         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3496             Roo.log("MenuManager hideAll");
3497             hideAll();
3498             e.stopEvent();
3499         }
3500         
3501         
3502    }
3503
3504    // private
3505    function onBeforeCheck(mi, state){
3506        if(state){
3507            var g = groups[mi.group];
3508            for(var i = 0, l = g.length; i < l; i++){
3509                if(g[i] != mi){
3510                    g[i].setChecked(false);
3511                }
3512            }
3513        }
3514    }
3515
3516    return {
3517
3518        /**
3519         * Hides all menus that are currently visible
3520         */
3521        hideAll : function(){
3522             hideAll();  
3523        },
3524
3525        // private
3526        register : function(menu){
3527            if(!menus){
3528                init();
3529            }
3530            menus[menu.id] = menu;
3531            menu.on("beforehide", onBeforeHide);
3532            menu.on("hide", onHide);
3533            menu.on("beforeshow", onBeforeShow);
3534            menu.on("show", onShow);
3535            var g = menu.group;
3536            if(g && menu.events["checkchange"]){
3537                if(!groups[g]){
3538                    groups[g] = [];
3539                }
3540                groups[g].push(menu);
3541                menu.on("checkchange", onCheck);
3542            }
3543        },
3544
3545         /**
3546          * Returns a {@link Roo.menu.Menu} object
3547          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3548          * be used to generate and return a new Menu instance.
3549          */
3550        get : function(menu){
3551            if(typeof menu == "string"){ // menu id
3552                return menus[menu];
3553            }else if(menu.events){  // menu instance
3554                return menu;
3555            }
3556            /*else if(typeof menu.length == 'number'){ // array of menu items?
3557                return new Roo.bootstrap.Menu({items:menu});
3558            }else{ // otherwise, must be a config
3559                return new Roo.bootstrap.Menu(menu);
3560            }
3561            */
3562            return false;
3563        },
3564
3565        // private
3566        unregister : function(menu){
3567            delete menus[menu.id];
3568            menu.un("beforehide", onBeforeHide);
3569            menu.un("hide", onHide);
3570            menu.un("beforeshow", onBeforeShow);
3571            menu.un("show", onShow);
3572            var g = menu.group;
3573            if(g && menu.events["checkchange"]){
3574                groups[g].remove(menu);
3575                menu.un("checkchange", onCheck);
3576            }
3577        },
3578
3579        // private
3580        registerCheckable : function(menuItem){
3581            var g = menuItem.group;
3582            if(g){
3583                if(!groups[g]){
3584                    groups[g] = [];
3585                }
3586                groups[g].push(menuItem);
3587                menuItem.on("beforecheckchange", onBeforeCheck);
3588            }
3589        },
3590
3591        // private
3592        unregisterCheckable : function(menuItem){
3593            var g = menuItem.group;
3594            if(g){
3595                groups[g].remove(menuItem);
3596                menuItem.un("beforecheckchange", onBeforeCheck);
3597            }
3598        }
3599    };
3600 }();/*
3601  * - LGPL
3602  *
3603  * menu
3604  * 
3605  */
3606
3607 /**
3608  * @class Roo.bootstrap.Menu
3609  * @extends Roo.bootstrap.Component
3610  * Bootstrap Menu class - container for MenuItems
3611  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3612  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3613  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3614  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3615   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3616   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3617  
3618  * @constructor
3619  * Create a new Menu
3620  * @param {Object} config The config object
3621  */
3622
3623
3624 Roo.bootstrap.Menu = function(config){
3625     
3626     if (config.type == 'treeview') {
3627         // normally menu's are drawn attached to the document to handle layering etc..
3628         // however treeview (used by the docs menu is drawn into the parent element)
3629         this.container_method = 'getChildContainer'; 
3630     }
3631     
3632     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3633     if (this.registerMenu && this.type != 'treeview')  {
3634         Roo.bootstrap.MenuMgr.register(this);
3635     }
3636     
3637     
3638     this.addEvents({
3639         /**
3640          * @event beforeshow
3641          * Fires before this menu is displayed (return false to block)
3642          * @param {Roo.menu.Menu} this
3643          */
3644         beforeshow : true,
3645         /**
3646          * @event beforehide
3647          * Fires before this menu is hidden (return false to block)
3648          * @param {Roo.menu.Menu} this
3649          */
3650         beforehide : true,
3651         /**
3652          * @event show
3653          * Fires after this menu is displayed
3654          * @param {Roo.menu.Menu} this
3655          */
3656         show : true,
3657         /**
3658          * @event hide
3659          * Fires after this menu is hidden
3660          * @param {Roo.menu.Menu} this
3661          */
3662         hide : true,
3663         /**
3664          * @event click
3665          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3666          * @param {Roo.menu.Menu} this
3667          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3668          * @param {Roo.EventObject} e
3669          */
3670         click : true,
3671         /**
3672          * @event mouseover
3673          * Fires when the mouse is hovering over this menu
3674          * @param {Roo.menu.Menu} this
3675          * @param {Roo.EventObject} e
3676          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3677          */
3678         mouseover : true,
3679         /**
3680          * @event mouseout
3681          * Fires when the mouse exits this menu
3682          * @param {Roo.menu.Menu} this
3683          * @param {Roo.EventObject} e
3684          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3685          */
3686         mouseout : true,
3687         /**
3688          * @event itemclick
3689          * Fires when a menu item contained in this menu is clicked
3690          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3691          * @param {Roo.EventObject} e
3692          */
3693         itemclick: true
3694     });
3695     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3696 };
3697
3698 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3699     
3700    /// html : false,
3701    
3702     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3703     type: false,
3704     /**
3705      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3706      */
3707     registerMenu : true,
3708     
3709     menuItems :false, // stores the menu items..
3710     
3711     hidden:true,
3712         
3713     parentMenu : false,
3714     
3715     stopEvent : true,
3716     
3717     isLink : false,
3718     
3719     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3720     
3721     hideTrigger : false,
3722     
3723     align : 'tl-bl?',
3724     
3725     
3726     getChildContainer : function() {
3727         return this.el;  
3728     },
3729     
3730     getAutoCreate : function(){
3731          
3732         //if (['right'].indexOf(this.align)!==-1) {
3733         //    cfg.cn[1].cls += ' pull-right'
3734         //}
3735          
3736         var cfg = {
3737             tag : 'ul',
3738             cls : 'dropdown-menu shadow' ,
3739             style : 'z-index:1000'
3740             
3741         };
3742         
3743         if (this.type === 'submenu') {
3744             cfg.cls = 'submenu active';
3745         }
3746         if (this.type === 'treeview') {
3747             cfg.cls = 'treeview-menu';
3748         }
3749         
3750         return cfg;
3751     },
3752     initEvents : function() {
3753         
3754        // Roo.log("ADD event");
3755        // Roo.log(this.triggerEl.dom);
3756         if (this.triggerEl) {
3757             
3758             this.triggerEl.on('click', this.onTriggerClick, this);
3759             
3760             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3761             
3762             if (!this.hideTrigger) {
3763                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3764                     // dropdown toggle on the 'a' in BS4?
3765                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3766                 } else {
3767                     this.triggerEl.addClass('dropdown-toggle');
3768                 }
3769             }
3770         }
3771         
3772         if (Roo.isTouch) {
3773             this.el.on('touchstart'  , this.onTouch, this);
3774         }
3775         this.el.on('click' , this.onClick, this);
3776
3777         this.el.on("mouseover", this.onMouseOver, this);
3778         this.el.on("mouseout", this.onMouseOut, this);
3779         
3780     },
3781     
3782     findTargetItem : function(e)
3783     {
3784         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3785         if(!t){
3786             return false;
3787         }
3788         //Roo.log(t);         Roo.log(t.id);
3789         if(t && t.id){
3790             //Roo.log(this.menuitems);
3791             return this.menuitems.get(t.id);
3792             
3793             //return this.items.get(t.menuItemId);
3794         }
3795         
3796         return false;
3797     },
3798     
3799     onTouch : function(e) 
3800     {
3801         Roo.log("menu.onTouch");
3802         //e.stopEvent(); this make the user popdown broken
3803         this.onClick(e);
3804     },
3805     
3806     onClick : function(e)
3807     {
3808         Roo.log("menu.onClick");
3809         
3810         var t = this.findTargetItem(e);
3811         if(!t || t.isContainer){
3812             return;
3813         }
3814         Roo.log(e);
3815         /*
3816         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3817             if(t == this.activeItem && t.shouldDeactivate(e)){
3818                 this.activeItem.deactivate();
3819                 delete this.activeItem;
3820                 return;
3821             }
3822             if(t.canActivate){
3823                 this.setActiveItem(t, true);
3824             }
3825             return;
3826             
3827             
3828         }
3829         */
3830        
3831         Roo.log('pass click event');
3832         
3833         t.onClick(e);
3834         
3835         this.fireEvent("click", this, t, e);
3836         
3837         var _this = this;
3838         
3839         if(!t.href.length || t.href == '#'){
3840             (function() { _this.hide(); }).defer(100);
3841         }
3842         
3843     },
3844     
3845     onMouseOver : function(e){
3846         var t  = this.findTargetItem(e);
3847         //Roo.log(t);
3848         //if(t){
3849         //    if(t.canActivate && !t.disabled){
3850         //        this.setActiveItem(t, true);
3851         //    }
3852         //}
3853         
3854         this.fireEvent("mouseover", this, e, t);
3855     },
3856     isVisible : function(){
3857         return !this.hidden;
3858     },
3859     onMouseOut : function(e){
3860         var t  = this.findTargetItem(e);
3861         
3862         //if(t ){
3863         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3864         //        this.activeItem.deactivate();
3865         //        delete this.activeItem;
3866         //    }
3867         //}
3868         this.fireEvent("mouseout", this, e, t);
3869     },
3870     
3871     
3872     /**
3873      * Displays this menu relative to another element
3874      * @param {String/HTMLElement/Roo.Element} element The element to align to
3875      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3876      * the element (defaults to this.defaultAlign)
3877      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3878      */
3879     show : function(el, pos, parentMenu)
3880     {
3881         if (false === this.fireEvent("beforeshow", this)) {
3882             Roo.log("show canceled");
3883             return;
3884         }
3885         this.parentMenu = parentMenu;
3886         if(!this.el){
3887             this.render();
3888         }
3889         this.el.addClass('show'); // show otherwise we do not know how big we are..
3890          
3891         var xy = this.el.getAlignToXY(el, pos);
3892         
3893         // bl-tl << left align  below
3894         // tl-bl << left align 
3895         
3896         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3897             // if it goes to far to the right.. -> align left.
3898             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3899         }
3900         if(xy[0] < 0){
3901             // was left align - go right?
3902             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3903         }
3904         
3905         // goes down the bottom
3906         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3907            xy[1]  < 0 ){
3908             var a = this.align.replace('?', '').split('-');
3909             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3910             
3911         }
3912         
3913         this.showAt(  xy , parentMenu, false);
3914     },
3915      /**
3916      * Displays this menu at a specific xy position
3917      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3918      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3919      */
3920     showAt : function(xy, parentMenu, /* private: */_e){
3921         this.parentMenu = parentMenu;
3922         if(!this.el){
3923             this.render();
3924         }
3925         if(_e !== false){
3926             this.fireEvent("beforeshow", this);
3927             //xy = this.el.adjustForConstraints(xy);
3928         }
3929         
3930         //this.el.show();
3931         this.hideMenuItems();
3932         this.hidden = false;
3933         if (this.triggerEl) {
3934             this.triggerEl.addClass('open');
3935         }
3936         
3937         this.el.addClass('show');
3938         
3939         
3940         
3941         // reassign x when hitting right
3942         
3943         // reassign y when hitting bottom
3944         
3945         // but the list may align on trigger left or trigger top... should it be a properity?
3946         
3947         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3948             this.el.setXY(xy);
3949         }
3950         
3951         this.focus();
3952         this.fireEvent("show", this);
3953     },
3954     
3955     focus : function(){
3956         return;
3957         if(!this.hidden){
3958             this.doFocus.defer(50, this);
3959         }
3960     },
3961
3962     doFocus : function(){
3963         if(!this.hidden){
3964             this.focusEl.focus();
3965         }
3966     },
3967
3968     /**
3969      * Hides this menu and optionally all parent menus
3970      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3971      */
3972     hide : function(deep)
3973     {
3974         if (false === this.fireEvent("beforehide", this)) {
3975             Roo.log("hide canceled");
3976             return;
3977         }
3978         this.hideMenuItems();
3979         if(this.el && this.isVisible()){
3980            
3981             if(this.activeItem){
3982                 this.activeItem.deactivate();
3983                 this.activeItem = null;
3984             }
3985             if (this.triggerEl) {
3986                 this.triggerEl.removeClass('open');
3987             }
3988             
3989             this.el.removeClass('show');
3990             this.hidden = true;
3991             this.fireEvent("hide", this);
3992         }
3993         if(deep === true && this.parentMenu){
3994             this.parentMenu.hide(true);
3995         }
3996     },
3997     
3998     onTriggerClick : function(e)
3999     {
4000         Roo.log('trigger click');
4001         
4002         var target = e.getTarget();
4003         
4004         Roo.log(target.nodeName.toLowerCase());
4005         
4006         if(target.nodeName.toLowerCase() === 'i'){
4007             e.preventDefault();
4008         }
4009         
4010     },
4011     
4012     onTriggerPress  : function(e)
4013     {
4014         Roo.log('trigger press');
4015         //Roo.log(e.getTarget());
4016        // Roo.log(this.triggerEl.dom);
4017        
4018         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4019         var pel = Roo.get(e.getTarget());
4020         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4021             Roo.log('is treeview or dropdown?');
4022             return;
4023         }
4024         
4025         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4026             return;
4027         }
4028         
4029         if (this.isVisible()) {
4030             Roo.log('hide');
4031             this.hide();
4032         } else {
4033             Roo.log('show');
4034             
4035             this.show(this.triggerEl, this.align, false);
4036         }
4037         
4038         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4039             e.stopEvent();
4040         }
4041         
4042     },
4043        
4044     
4045     hideMenuItems : function()
4046     {
4047         Roo.log("hide Menu Items");
4048         if (!this.el) { 
4049             return;
4050         }
4051         
4052         this.el.select('.open',true).each(function(aa) {
4053             
4054             aa.removeClass('open');
4055          
4056         });
4057     },
4058     addxtypeChild : function (tree, cntr) {
4059         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4060           
4061         this.menuitems.add(comp);
4062         return comp;
4063
4064     },
4065     getEl : function()
4066     {
4067         Roo.log(this.el);
4068         return this.el;
4069     },
4070     
4071     clear : function()
4072     {
4073         this.getEl().dom.innerHTML = '';
4074         this.menuitems.clear();
4075     }
4076 });
4077
4078  
4079  /*
4080  * - LGPL
4081  *
4082  * menu item
4083  * 
4084  */
4085
4086
4087 /**
4088  * @class Roo.bootstrap.MenuItem
4089  * @extends Roo.bootstrap.Component
4090  * Bootstrap MenuItem class
4091  * @cfg {String} html the menu label
4092  * @cfg {String} href the link
4093  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4094  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4095  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4096  * @cfg {String} fa favicon to show on left of menu item.
4097  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4098  * 
4099  * 
4100  * @constructor
4101  * Create a new MenuItem
4102  * @param {Object} config The config object
4103  */
4104
4105
4106 Roo.bootstrap.MenuItem = function(config){
4107     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4108     this.addEvents({
4109         // raw events
4110         /**
4111          * @event click
4112          * The raw click event for the entire grid.
4113          * @param {Roo.bootstrap.MenuItem} this
4114          * @param {Roo.EventObject} e
4115          */
4116         "click" : true
4117     });
4118 };
4119
4120 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4121     
4122     href : false,
4123     html : false,
4124     preventDefault: false,
4125     isContainer : false,
4126     active : false,
4127     fa: false,
4128     
4129     getAutoCreate : function(){
4130         
4131         if(this.isContainer){
4132             return {
4133                 tag: 'li',
4134                 cls: 'dropdown-menu-item '
4135             };
4136         }
4137         var ctag = {
4138             tag: 'span',
4139             html: 'Link'
4140         };
4141         
4142         var anc = {
4143             tag : 'a',
4144             cls : 'dropdown-item',
4145             href : '#',
4146             cn : [  ]
4147         };
4148         
4149         if (this.fa !== false) {
4150             anc.cn.push({
4151                 tag : 'i',
4152                 cls : 'fa fa-' + this.fa
4153             });
4154         }
4155         
4156         anc.cn.push(ctag);
4157         
4158         
4159         var cfg= {
4160             tag: 'li',
4161             cls: 'dropdown-menu-item',
4162             cn: [ anc ]
4163         };
4164         if (this.parent().type == 'treeview') {
4165             cfg.cls = 'treeview-menu';
4166         }
4167         if (this.active) {
4168             cfg.cls += ' active';
4169         }
4170         
4171         
4172         
4173         anc.href = this.href || cfg.cn[0].href ;
4174         ctag.html = this.html || cfg.cn[0].html ;
4175         return cfg;
4176     },
4177     
4178     initEvents: function()
4179     {
4180         if (this.parent().type == 'treeview') {
4181             this.el.select('a').on('click', this.onClick, this);
4182         }
4183         
4184         if (this.menu) {
4185             this.menu.parentType = this.xtype;
4186             this.menu.triggerEl = this.el;
4187             this.menu = this.addxtype(Roo.apply({}, this.menu));
4188         }
4189         
4190     },
4191     onClick : function(e)
4192     {
4193         Roo.log('item on click ');
4194         
4195         if(this.preventDefault){
4196             e.preventDefault();
4197         }
4198         //this.parent().hideMenuItems();
4199         
4200         this.fireEvent('click', this, e);
4201     },
4202     getEl : function()
4203     {
4204         return this.el;
4205     } 
4206 });
4207
4208  
4209
4210  /*
4211  * - LGPL
4212  *
4213  * menu separator
4214  * 
4215  */
4216
4217
4218 /**
4219  * @class Roo.bootstrap.MenuSeparator
4220  * @extends Roo.bootstrap.Component
4221  * Bootstrap MenuSeparator class
4222  * 
4223  * @constructor
4224  * Create a new MenuItem
4225  * @param {Object} config The config object
4226  */
4227
4228
4229 Roo.bootstrap.MenuSeparator = function(config){
4230     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4231 };
4232
4233 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4234     
4235     getAutoCreate : function(){
4236         var cfg = {
4237             cls: 'divider',
4238             tag : 'li'
4239         };
4240         
4241         return cfg;
4242     }
4243    
4244 });
4245
4246  
4247
4248  
4249 /*
4250 * Licence: LGPL
4251 */
4252
4253 /**
4254  * @class Roo.bootstrap.Modal
4255  * @extends Roo.bootstrap.Component
4256  * Bootstrap Modal class
4257  * @cfg {String} title Title of dialog
4258  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4259  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4260  * @cfg {Boolean} specificTitle default false
4261  * @cfg {Array} buttons Array of buttons or standard button set..
4262  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4263  * @cfg {Boolean} animate default true
4264  * @cfg {Boolean} allow_close default true
4265  * @cfg {Boolean} fitwindow default false
4266  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4267  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4268  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4269  * @cfg {String} size (sm|lg|xl) default empty
4270  * @cfg {Number} max_width set the max width of modal
4271  * @cfg {Boolean} editableTitle can the title be edited
4272
4273  *
4274  *
4275  * @constructor
4276  * Create a new Modal Dialog
4277  * @param {Object} config The config object
4278  */
4279
4280 Roo.bootstrap.Modal = function(config){
4281     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4282     this.addEvents({
4283         // raw events
4284         /**
4285          * @event btnclick
4286          * The raw btnclick event for the button
4287          * @param {Roo.EventObject} e
4288          */
4289         "btnclick" : true,
4290         /**
4291          * @event resize
4292          * Fire when dialog resize
4293          * @param {Roo.bootstrap.Modal} this
4294          * @param {Roo.EventObject} e
4295          */
4296         "resize" : true,
4297         /**
4298          * @event titlechanged
4299          * Fire when the editable title has been changed
4300          * @param {Roo.bootstrap.Modal} this
4301          * @param {Roo.EventObject} value
4302          */
4303         "titlechanged" : true 
4304         
4305     });
4306     this.buttons = this.buttons || [];
4307
4308     if (this.tmpl) {
4309         this.tmpl = Roo.factory(this.tmpl);
4310     }
4311
4312 };
4313
4314 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4315
4316     title : 'test dialog',
4317
4318     buttons : false,
4319
4320     // set on load...
4321
4322     html: false,
4323
4324     tmp: false,
4325
4326     specificTitle: false,
4327
4328     buttonPosition: 'right',
4329
4330     allow_close : true,
4331
4332     animate : true,
4333
4334     fitwindow: false,
4335     
4336      // private
4337     dialogEl: false,
4338     bodyEl:  false,
4339     footerEl:  false,
4340     titleEl:  false,
4341     closeEl:  false,
4342
4343     size: '',
4344     
4345     max_width: 0,
4346     
4347     max_height: 0,
4348     
4349     fit_content: false,
4350     editableTitle  : false,
4351
4352     onRender : function(ct, position)
4353     {
4354         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4355
4356         if(!this.el){
4357             var cfg = Roo.apply({},  this.getAutoCreate());
4358             cfg.id = Roo.id();
4359             //if(!cfg.name){
4360             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4361             //}
4362             //if (!cfg.name.length) {
4363             //    delete cfg.name;
4364            // }
4365             if (this.cls) {
4366                 cfg.cls += ' ' + this.cls;
4367             }
4368             if (this.style) {
4369                 cfg.style = this.style;
4370             }
4371             this.el = Roo.get(document.body).createChild(cfg, position);
4372         }
4373         //var type = this.el.dom.type;
4374
4375
4376         if(this.tabIndex !== undefined){
4377             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4378         }
4379
4380         this.dialogEl = this.el.select('.modal-dialog',true).first();
4381         this.bodyEl = this.el.select('.modal-body',true).first();
4382         this.closeEl = this.el.select('.modal-header .close', true).first();
4383         this.headerEl = this.el.select('.modal-header',true).first();
4384         this.titleEl = this.el.select('.modal-title',true).first();
4385         this.footerEl = this.el.select('.modal-footer',true).first();
4386
4387         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4388         
4389         //this.el.addClass("x-dlg-modal");
4390
4391         if (this.buttons.length) {
4392             Roo.each(this.buttons, function(bb) {
4393                 var b = Roo.apply({}, bb);
4394                 b.xns = b.xns || Roo.bootstrap;
4395                 b.xtype = b.xtype || 'Button';
4396                 if (typeof(b.listeners) == 'undefined') {
4397                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4398                 }
4399
4400                 var btn = Roo.factory(b);
4401
4402                 btn.render(this.getButtonContainer());
4403
4404             },this);
4405         }
4406         // render the children.
4407         var nitems = [];
4408
4409         if(typeof(this.items) != 'undefined'){
4410             var items = this.items;
4411             delete this.items;
4412
4413             for(var i =0;i < items.length;i++) {
4414                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4415             }
4416         }
4417
4418         this.items = nitems;
4419
4420         // where are these used - they used to be body/close/footer
4421
4422
4423         this.initEvents();
4424         //this.el.addClass([this.fieldClass, this.cls]);
4425
4426     },
4427
4428     getAutoCreate : function()
4429     {
4430         // we will default to modal-body-overflow - might need to remove or make optional later.
4431         var bdy = {
4432                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4433                 html : this.html || ''
4434         };
4435
4436         var title = {
4437             tag: 'h5',
4438             cls : 'modal-title',
4439             html : this.title
4440         };
4441
4442         if(this.specificTitle){ // WTF is this?
4443             title = this.title;
4444         }
4445
4446         var header = [];
4447         if (this.allow_close && Roo.bootstrap.version == 3) {
4448             header.push({
4449                 tag: 'button',
4450                 cls : 'close',
4451                 html : '&times'
4452             });
4453         }
4454
4455         header.push(title);
4456
4457         if (this.editableTitle) {
4458             header.push({
4459                 cls: 'form-control roo-editable-title d-none',
4460                 tag: 'input',
4461                 type: 'text'
4462             });
4463         }
4464         
4465         if (this.allow_close && Roo.bootstrap.version == 4) {
4466             header.push({
4467                 tag: 'button',
4468                 cls : 'close',
4469                 html : '&times'
4470             });
4471         }
4472         
4473         var size = '';
4474
4475         if(this.size.length){
4476             size = 'modal-' + this.size;
4477         }
4478         
4479         var footer = Roo.bootstrap.version == 3 ?
4480             {
4481                 cls : 'modal-footer',
4482                 cn : [
4483                     {
4484                         tag: 'div',
4485                         cls: 'btn-' + this.buttonPosition
4486                     }
4487                 ]
4488
4489             } :
4490             {  // BS4 uses mr-auto on left buttons....
4491                 cls : 'modal-footer'
4492             };
4493
4494             
4495
4496         
4497         
4498         var modal = {
4499             cls: "modal",
4500              cn : [
4501                 {
4502                     cls: "modal-dialog " + size,
4503                     cn : [
4504                         {
4505                             cls : "modal-content",
4506                             cn : [
4507                                 {
4508                                     cls : 'modal-header',
4509                                     cn : header
4510                                 },
4511                                 bdy,
4512                                 footer
4513                             ]
4514
4515                         }
4516                     ]
4517
4518                 }
4519             ]
4520         };
4521
4522         if(this.animate){
4523             modal.cls += ' fade';
4524         }
4525
4526         return modal;
4527
4528     },
4529     getChildContainer : function() {
4530
4531          return this.bodyEl;
4532
4533     },
4534     getButtonContainer : function() {
4535         
4536          return Roo.bootstrap.version == 4 ?
4537             this.el.select('.modal-footer',true).first()
4538             : this.el.select('.modal-footer div',true).first();
4539
4540     },
4541     initEvents : function()
4542     {
4543         if (this.allow_close) {
4544             this.closeEl.on('click', this.hide, this);
4545         }
4546         Roo.EventManager.onWindowResize(this.resize, this, true);
4547         if (this.editableTitle) {
4548             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4549             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4550             this.headerEditEl.on('keyup', function(e) {
4551                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4552                         this.toggleHeaderInput(false)
4553                     }
4554                 }, this);
4555             this.headerEditEl.on('blur', function(e) {
4556                 this.toggleHeaderInput(false)
4557             },this);
4558         }
4559
4560     },
4561   
4562
4563     resize : function()
4564     {
4565         this.maskEl.setSize(
4566             Roo.lib.Dom.getViewWidth(true),
4567             Roo.lib.Dom.getViewHeight(true)
4568         );
4569         
4570         if (this.fitwindow) {
4571             
4572            this.dialogEl.setStyle( { 'max-width' : '100%' });
4573             this.setSize(
4574                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4575                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4576             );
4577             return;
4578         }
4579         
4580         if(this.max_width !== 0) {
4581             
4582             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4583             
4584             if(this.height) {
4585                 this.setSize(w, this.height);
4586                 return;
4587             }
4588             
4589             if(this.max_height) {
4590                 this.setSize(w,Math.min(
4591                     this.max_height,
4592                     Roo.lib.Dom.getViewportHeight(true) - 60
4593                 ));
4594                 
4595                 return;
4596             }
4597             
4598             if(!this.fit_content) {
4599                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4600                 return;
4601             }
4602             
4603             this.setSize(w, Math.min(
4604                 60 +
4605                 this.headerEl.getHeight() + 
4606                 this.footerEl.getHeight() + 
4607                 this.getChildHeight(this.bodyEl.dom.childNodes),
4608                 Roo.lib.Dom.getViewportHeight(true) - 60)
4609             );
4610         }
4611         
4612     },
4613
4614     setSize : function(w,h)
4615     {
4616         if (!w && !h) {
4617             return;
4618         }
4619         
4620         this.resizeTo(w,h);
4621     },
4622
4623     show : function() {
4624
4625         if (!this.rendered) {
4626             this.render();
4627         }
4628         this.toggleHeaderInput(false);
4629         //this.el.setStyle('display', 'block');
4630         this.el.removeClass('hideing');
4631         this.el.dom.style.display='block';
4632         
4633         Roo.get(document.body).addClass('modal-open');
4634  
4635         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4636             
4637             (function(){
4638                 this.el.addClass('show');
4639                 this.el.addClass('in');
4640             }).defer(50, this);
4641         }else{
4642             this.el.addClass('show');
4643             this.el.addClass('in');
4644         }
4645
4646         // not sure how we can show data in here..
4647         //if (this.tmpl) {
4648         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4649         //}
4650
4651         Roo.get(document.body).addClass("x-body-masked");
4652         
4653         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4654         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4655         this.maskEl.dom.style.display = 'block';
4656         this.maskEl.addClass('show');
4657         
4658         
4659         this.resize();
4660         
4661         this.fireEvent('show', this);
4662
4663         // set zindex here - otherwise it appears to be ignored...
4664         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4665
4666         (function () {
4667             this.items.forEach( function(e) {
4668                 e.layout ? e.layout() : false;
4669
4670             });
4671         }).defer(100,this);
4672
4673     },
4674     hide : function()
4675     {
4676         if(this.fireEvent("beforehide", this) !== false){
4677             
4678             this.maskEl.removeClass('show');
4679             
4680             this.maskEl.dom.style.display = '';
4681             Roo.get(document.body).removeClass("x-body-masked");
4682             this.el.removeClass('in');
4683             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4684
4685             if(this.animate){ // why
4686                 this.el.addClass('hideing');
4687                 this.el.removeClass('show');
4688                 (function(){
4689                     if (!this.el.hasClass('hideing')) {
4690                         return; // it's been shown again...
4691                     }
4692                     
4693                     this.el.dom.style.display='';
4694
4695                     Roo.get(document.body).removeClass('modal-open');
4696                     this.el.removeClass('hideing');
4697                 }).defer(150,this);
4698                 
4699             }else{
4700                 this.el.removeClass('show');
4701                 this.el.dom.style.display='';
4702                 Roo.get(document.body).removeClass('modal-open');
4703
4704             }
4705             this.fireEvent('hide', this);
4706         }
4707     },
4708     isVisible : function()
4709     {
4710         
4711         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4712         
4713     },
4714
4715     addButton : function(str, cb)
4716     {
4717
4718
4719         var b = Roo.apply({}, { html : str } );
4720         b.xns = b.xns || Roo.bootstrap;
4721         b.xtype = b.xtype || 'Button';
4722         if (typeof(b.listeners) == 'undefined') {
4723             b.listeners = { click : cb.createDelegate(this)  };
4724         }
4725
4726         var btn = Roo.factory(b);
4727
4728         btn.render(this.getButtonContainer());
4729
4730         return btn;
4731
4732     },
4733
4734     setDefaultButton : function(btn)
4735     {
4736         //this.el.select('.modal-footer').()
4737     },
4738
4739     resizeTo: function(w,h)
4740     {
4741         this.dialogEl.setWidth(w);
4742         
4743         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4744
4745         this.bodyEl.setHeight(h - diff);
4746         
4747         this.fireEvent('resize', this);
4748     },
4749     
4750     setContentSize  : function(w, h)
4751     {
4752
4753     },
4754     onButtonClick: function(btn,e)
4755     {
4756         //Roo.log([a,b,c]);
4757         this.fireEvent('btnclick', btn.name, e);
4758     },
4759      /**
4760      * Set the title of the Dialog
4761      * @param {String} str new Title
4762      */
4763     setTitle: function(str) {
4764         this.titleEl.dom.innerHTML = str;
4765         this.title = str;
4766     },
4767     /**
4768      * Set the body of the Dialog
4769      * @param {String} str new Title
4770      */
4771     setBody: function(str) {
4772         this.bodyEl.dom.innerHTML = str;
4773     },
4774     /**
4775      * Set the body of the Dialog using the template
4776      * @param {Obj} data - apply this data to the template and replace the body contents.
4777      */
4778     applyBody: function(obj)
4779     {
4780         if (!this.tmpl) {
4781             Roo.log("Error - using apply Body without a template");
4782             //code
4783         }
4784         this.tmpl.overwrite(this.bodyEl, obj);
4785     },
4786     
4787     getChildHeight : function(child_nodes)
4788     {
4789         if(
4790             !child_nodes ||
4791             child_nodes.length == 0
4792         ) {
4793             return 0;
4794         }
4795         
4796         var child_height = 0;
4797         
4798         for(var i = 0; i < child_nodes.length; i++) {
4799             
4800             /*
4801             * for modal with tabs...
4802             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4803                 
4804                 var layout_childs = child_nodes[i].childNodes;
4805                 
4806                 for(var j = 0; j < layout_childs.length; j++) {
4807                     
4808                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4809                         
4810                         var layout_body_childs = layout_childs[j].childNodes;
4811                         
4812                         for(var k = 0; k < layout_body_childs.length; k++) {
4813                             
4814                             if(layout_body_childs[k].classList.contains('navbar')) {
4815                                 child_height += layout_body_childs[k].offsetHeight;
4816                                 continue;
4817                             }
4818                             
4819                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4820                                 
4821                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4822                                 
4823                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4824                                     
4825                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4826                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4827                                         continue;
4828                                     }
4829                                     
4830                                 }
4831                                 
4832                             }
4833                             
4834                         }
4835                     }
4836                 }
4837                 continue;
4838             }
4839             */
4840             
4841             child_height += child_nodes[i].offsetHeight;
4842             // Roo.log(child_nodes[i].offsetHeight);
4843         }
4844         
4845         return child_height;
4846     },
4847     toggleHeaderInput : function(is_edit)
4848     {
4849         if (!this.editableTitle) {
4850             return; // not editable.
4851         }
4852         if (is_edit && this.is_header_editing) {
4853             return; // already editing..
4854         }
4855         if (is_edit) {
4856     
4857             this.headerEditEl.dom.value = this.title;
4858             this.headerEditEl.removeClass('d-none');
4859             this.headerEditEl.dom.focus();
4860             this.titleEl.addClass('d-none');
4861             
4862             this.is_header_editing = true;
4863             return
4864         }
4865         // flip back to not editing.
4866         this.title = this.headerEditEl.dom.value;
4867         this.headerEditEl.addClass('d-none');
4868         this.titleEl.removeClass('d-none');
4869         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4870         this.is_header_editing = false;
4871         this.fireEvent('titlechanged', this, this.title);
4872     
4873             
4874         
4875     }
4876
4877 });
4878
4879
4880 Roo.apply(Roo.bootstrap.Modal,  {
4881     /**
4882          * Button config that displays a single OK button
4883          * @type Object
4884          */
4885         OK :  [{
4886             name : 'ok',
4887             weight : 'primary',
4888             html : 'OK'
4889         }],
4890         /**
4891          * Button config that displays Yes and No buttons
4892          * @type Object
4893          */
4894         YESNO : [
4895             {
4896                 name  : 'no',
4897                 html : 'No'
4898             },
4899             {
4900                 name  :'yes',
4901                 weight : 'primary',
4902                 html : 'Yes'
4903             }
4904         ],
4905
4906         /**
4907          * Button config that displays OK and Cancel buttons
4908          * @type Object
4909          */
4910         OKCANCEL : [
4911             {
4912                name : 'cancel',
4913                 html : 'Cancel'
4914             },
4915             {
4916                 name : 'ok',
4917                 weight : 'primary',
4918                 html : 'OK'
4919             }
4920         ],
4921         /**
4922          * Button config that displays Yes, No and Cancel buttons
4923          * @type Object
4924          */
4925         YESNOCANCEL : [
4926             {
4927                 name : 'yes',
4928                 weight : 'primary',
4929                 html : 'Yes'
4930             },
4931             {
4932                 name : 'no',
4933                 html : 'No'
4934             },
4935             {
4936                 name : 'cancel',
4937                 html : 'Cancel'
4938             }
4939         ],
4940         
4941         zIndex : 10001
4942 });
4943
4944 /*
4945  * - LGPL
4946  *
4947  * messagebox - can be used as a replace
4948  * 
4949  */
4950 /**
4951  * @class Roo.MessageBox
4952  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4953  * Example usage:
4954  *<pre><code>
4955 // Basic alert:
4956 Roo.Msg.alert('Status', 'Changes saved successfully.');
4957
4958 // Prompt for user data:
4959 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4960     if (btn == 'ok'){
4961         // process text value...
4962     }
4963 });
4964
4965 // Show a dialog using config options:
4966 Roo.Msg.show({
4967    title:'Save Changes?',
4968    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4969    buttons: Roo.Msg.YESNOCANCEL,
4970    fn: processResult,
4971    animEl: 'elId'
4972 });
4973 </code></pre>
4974  * @singleton
4975  */
4976 Roo.bootstrap.MessageBox = function(){
4977     var dlg, opt, mask, waitTimer;
4978     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4979     var buttons, activeTextEl, bwidth;
4980
4981     
4982     // private
4983     var handleButton = function(button){
4984         dlg.hide();
4985         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4986     };
4987
4988     // private
4989     var handleHide = function(){
4990         if(opt && opt.cls){
4991             dlg.el.removeClass(opt.cls);
4992         }
4993         //if(waitTimer){
4994         //    Roo.TaskMgr.stop(waitTimer);
4995         //    waitTimer = null;
4996         //}
4997     };
4998
4999     // private
5000     var updateButtons = function(b){
5001         var width = 0;
5002         if(!b){
5003             buttons["ok"].hide();
5004             buttons["cancel"].hide();
5005             buttons["yes"].hide();
5006             buttons["no"].hide();
5007             dlg.footerEl.hide();
5008             
5009             return width;
5010         }
5011         dlg.footerEl.show();
5012         for(var k in buttons){
5013             if(typeof buttons[k] != "function"){
5014                 if(b[k]){
5015                     buttons[k].show();
5016                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5017                     width += buttons[k].el.getWidth()+15;
5018                 }else{
5019                     buttons[k].hide();
5020                 }
5021             }
5022         }
5023         return width;
5024     };
5025
5026     // private
5027     var handleEsc = function(d, k, e){
5028         if(opt && opt.closable !== false){
5029             dlg.hide();
5030         }
5031         if(e){
5032             e.stopEvent();
5033         }
5034     };
5035
5036     return {
5037         /**
5038          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5039          * @return {Roo.BasicDialog} The BasicDialog element
5040          */
5041         getDialog : function(){
5042            if(!dlg){
5043                 dlg = new Roo.bootstrap.Modal( {
5044                     //draggable: true,
5045                     //resizable:false,
5046                     //constraintoviewport:false,
5047                     //fixedcenter:true,
5048                     //collapsible : false,
5049                     //shim:true,
5050                     //modal: true,
5051                 //    width: 'auto',
5052                   //  height:100,
5053                     //buttonAlign:"center",
5054                     closeClick : function(){
5055                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5056                             handleButton("no");
5057                         }else{
5058                             handleButton("cancel");
5059                         }
5060                     }
5061                 });
5062                 dlg.render();
5063                 dlg.on("hide", handleHide);
5064                 mask = dlg.mask;
5065                 //dlg.addKeyListener(27, handleEsc);
5066                 buttons = {};
5067                 this.buttons = buttons;
5068                 var bt = this.buttonText;
5069                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5070                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5071                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5072                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5073                 //Roo.log(buttons);
5074                 bodyEl = dlg.bodyEl.createChild({
5075
5076                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5077                         '<textarea class="roo-mb-textarea"></textarea>' +
5078                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5079                 });
5080                 msgEl = bodyEl.dom.firstChild;
5081                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5082                 textboxEl.enableDisplayMode();
5083                 textboxEl.addKeyListener([10,13], function(){
5084                     if(dlg.isVisible() && opt && opt.buttons){
5085                         if(opt.buttons.ok){
5086                             handleButton("ok");
5087                         }else if(opt.buttons.yes){
5088                             handleButton("yes");
5089                         }
5090                     }
5091                 });
5092                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5093                 textareaEl.enableDisplayMode();
5094                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5095                 progressEl.enableDisplayMode();
5096                 
5097                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5098                 var pf = progressEl.dom.firstChild;
5099                 if (pf) {
5100                     pp = Roo.get(pf.firstChild);
5101                     pp.setHeight(pf.offsetHeight);
5102                 }
5103                 
5104             }
5105             return dlg;
5106         },
5107
5108         /**
5109          * Updates the message box body text
5110          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5111          * the XHTML-compliant non-breaking space character '&amp;#160;')
5112          * @return {Roo.MessageBox} This message box
5113          */
5114         updateText : function(text)
5115         {
5116             if(!dlg.isVisible() && !opt.width){
5117                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5118                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5119             }
5120             msgEl.innerHTML = text || '&#160;';
5121       
5122             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5123             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5124             var w = Math.max(
5125                     Math.min(opt.width || cw , this.maxWidth), 
5126                     Math.max(opt.minWidth || this.minWidth, bwidth)
5127             );
5128             if(opt.prompt){
5129                 activeTextEl.setWidth(w);
5130             }
5131             if(dlg.isVisible()){
5132                 dlg.fixedcenter = false;
5133             }
5134             // to big, make it scroll. = But as usual stupid IE does not support
5135             // !important..
5136             
5137             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5138                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5139                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5140             } else {
5141                 bodyEl.dom.style.height = '';
5142                 bodyEl.dom.style.overflowY = '';
5143             }
5144             if (cw > w) {
5145                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5146             } else {
5147                 bodyEl.dom.style.overflowX = '';
5148             }
5149             
5150             dlg.setContentSize(w, bodyEl.getHeight());
5151             if(dlg.isVisible()){
5152                 dlg.fixedcenter = true;
5153             }
5154             return this;
5155         },
5156
5157         /**
5158          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5159          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5160          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5161          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5162          * @return {Roo.MessageBox} This message box
5163          */
5164         updateProgress : function(value, text){
5165             if(text){
5166                 this.updateText(text);
5167             }
5168             
5169             if (pp) { // weird bug on my firefox - for some reason this is not defined
5170                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5171                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5172             }
5173             return this;
5174         },        
5175
5176         /**
5177          * Returns true if the message box is currently displayed
5178          * @return {Boolean} True if the message box is visible, else false
5179          */
5180         isVisible : function(){
5181             return dlg && dlg.isVisible();  
5182         },
5183
5184         /**
5185          * Hides the message box if it is displayed
5186          */
5187         hide : function(){
5188             if(this.isVisible()){
5189                 dlg.hide();
5190             }  
5191         },
5192
5193         /**
5194          * Displays a new message box, or reinitializes an existing message box, based on the config options
5195          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5196          * The following config object properties are supported:
5197          * <pre>
5198 Property    Type             Description
5199 ----------  ---------------  ------------------------------------------------------------------------------------
5200 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5201                                    closes (defaults to undefined)
5202 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5203                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5204 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5205                                    progress and wait dialogs will ignore this property and always hide the
5206                                    close button as they can only be closed programmatically.
5207 cls               String           A custom CSS class to apply to the message box element
5208 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5209                                    displayed (defaults to 75)
5210 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5211                                    function will be btn (the name of the button that was clicked, if applicable,
5212                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5213                                    Progress and wait dialogs will ignore this option since they do not respond to
5214                                    user actions and can only be closed programmatically, so any required function
5215                                    should be called by the same code after it closes the dialog.
5216 icon              String           A CSS class that provides a background image to be used as an icon for
5217                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5218 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5219 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5220 modal             Boolean          False to allow user interaction with the page while the message box is
5221                                    displayed (defaults to true)
5222 msg               String           A string that will replace the existing message box body text (defaults
5223                                    to the XHTML-compliant non-breaking space character '&#160;')
5224 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5225 progress          Boolean          True to display a progress bar (defaults to false)
5226 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5227 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5228 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5229 title             String           The title text
5230 value             String           The string value to set into the active textbox element if displayed
5231 wait              Boolean          True to display a progress bar (defaults to false)
5232 width             Number           The width of the dialog in pixels
5233 </pre>
5234          *
5235          * Example usage:
5236          * <pre><code>
5237 Roo.Msg.show({
5238    title: 'Address',
5239    msg: 'Please enter your address:',
5240    width: 300,
5241    buttons: Roo.MessageBox.OKCANCEL,
5242    multiline: true,
5243    fn: saveAddress,
5244    animEl: 'addAddressBtn'
5245 });
5246 </code></pre>
5247          * @param {Object} config Configuration options
5248          * @return {Roo.MessageBox} This message box
5249          */
5250         show : function(options)
5251         {
5252             
5253             // this causes nightmares if you show one dialog after another
5254             // especially on callbacks..
5255              
5256             if(this.isVisible()){
5257                 
5258                 this.hide();
5259                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5260                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5261                 Roo.log("New Dialog Message:" +  options.msg )
5262                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5263                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5264                 
5265             }
5266             var d = this.getDialog();
5267             opt = options;
5268             d.setTitle(opt.title || "&#160;");
5269             d.closeEl.setDisplayed(opt.closable !== false);
5270             activeTextEl = textboxEl;
5271             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5272             if(opt.prompt){
5273                 if(opt.multiline){
5274                     textboxEl.hide();
5275                     textareaEl.show();
5276                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5277                         opt.multiline : this.defaultTextHeight);
5278                     activeTextEl = textareaEl;
5279                 }else{
5280                     textboxEl.show();
5281                     textareaEl.hide();
5282                 }
5283             }else{
5284                 textboxEl.hide();
5285                 textareaEl.hide();
5286             }
5287             progressEl.setDisplayed(opt.progress === true);
5288             if (opt.progress) {
5289                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5290             }
5291             this.updateProgress(0);
5292             activeTextEl.dom.value = opt.value || "";
5293             if(opt.prompt){
5294                 dlg.setDefaultButton(activeTextEl);
5295             }else{
5296                 var bs = opt.buttons;
5297                 var db = null;
5298                 if(bs && bs.ok){
5299                     db = buttons["ok"];
5300                 }else if(bs && bs.yes){
5301                     db = buttons["yes"];
5302                 }
5303                 dlg.setDefaultButton(db);
5304             }
5305             bwidth = updateButtons(opt.buttons);
5306             this.updateText(opt.msg);
5307             if(opt.cls){
5308                 d.el.addClass(opt.cls);
5309             }
5310             d.proxyDrag = opt.proxyDrag === true;
5311             d.modal = opt.modal !== false;
5312             d.mask = opt.modal !== false ? mask : false;
5313             if(!d.isVisible()){
5314                 // force it to the end of the z-index stack so it gets a cursor in FF
5315                 document.body.appendChild(dlg.el.dom);
5316                 d.animateTarget = null;
5317                 d.show(options.animEl);
5318             }
5319             return this;
5320         },
5321
5322         /**
5323          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5324          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5325          * and closing the message box when the process is complete.
5326          * @param {String} title The title bar text
5327          * @param {String} msg The message box body text
5328          * @return {Roo.MessageBox} This message box
5329          */
5330         progress : function(title, msg){
5331             this.show({
5332                 title : title,
5333                 msg : msg,
5334                 buttons: false,
5335                 progress:true,
5336                 closable:false,
5337                 minWidth: this.minProgressWidth,
5338                 modal : true
5339             });
5340             return this;
5341         },
5342
5343         /**
5344          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5345          * If a callback function is passed it will be called after the user clicks the button, and the
5346          * id of the button that was clicked will be passed as the only parameter to the callback
5347          * (could also be the top-right close button).
5348          * @param {String} title The title bar text
5349          * @param {String} msg The message box body text
5350          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5351          * @param {Object} scope (optional) The scope of the callback function
5352          * @return {Roo.MessageBox} This message box
5353          */
5354         alert : function(title, msg, fn, scope)
5355         {
5356             this.show({
5357                 title : title,
5358                 msg : msg,
5359                 buttons: this.OK,
5360                 fn: fn,
5361                 closable : false,
5362                 scope : scope,
5363                 modal : true
5364             });
5365             return this;
5366         },
5367
5368         /**
5369          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5370          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5371          * You are responsible for closing the message box when the process is complete.
5372          * @param {String} msg The message box body text
5373          * @param {String} title (optional) The title bar text
5374          * @return {Roo.MessageBox} This message box
5375          */
5376         wait : function(msg, title){
5377             this.show({
5378                 title : title,
5379                 msg : msg,
5380                 buttons: false,
5381                 closable:false,
5382                 progress:true,
5383                 modal:true,
5384                 width:300,
5385                 wait:true
5386             });
5387             waitTimer = Roo.TaskMgr.start({
5388                 run: function(i){
5389                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5390                 },
5391                 interval: 1000
5392             });
5393             return this;
5394         },
5395
5396         /**
5397          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5398          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5399          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5400          * @param {String} title The title bar text
5401          * @param {String} msg The message box body text
5402          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5403          * @param {Object} scope (optional) The scope of the callback function
5404          * @return {Roo.MessageBox} This message box
5405          */
5406         confirm : function(title, msg, fn, scope){
5407             this.show({
5408                 title : title,
5409                 msg : msg,
5410                 buttons: this.YESNO,
5411                 fn: fn,
5412                 scope : scope,
5413                 modal : true
5414             });
5415             return this;
5416         },
5417
5418         /**
5419          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5420          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5421          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5422          * (could also be the top-right close button) and the text that was entered will be passed as the two
5423          * parameters to the callback.
5424          * @param {String} title The title bar text
5425          * @param {String} msg The message box body text
5426          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5427          * @param {Object} scope (optional) The scope of the callback function
5428          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5429          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5430          * @return {Roo.MessageBox} This message box
5431          */
5432         prompt : function(title, msg, fn, scope, multiline){
5433             this.show({
5434                 title : title,
5435                 msg : msg,
5436                 buttons: this.OKCANCEL,
5437                 fn: fn,
5438                 minWidth:250,
5439                 scope : scope,
5440                 prompt:true,
5441                 multiline: multiline,
5442                 modal : true
5443             });
5444             return this;
5445         },
5446
5447         /**
5448          * Button config that displays a single OK button
5449          * @type Object
5450          */
5451         OK : {ok:true},
5452         /**
5453          * Button config that displays Yes and No buttons
5454          * @type Object
5455          */
5456         YESNO : {yes:true, no:true},
5457         /**
5458          * Button config that displays OK and Cancel buttons
5459          * @type Object
5460          */
5461         OKCANCEL : {ok:true, cancel:true},
5462         /**
5463          * Button config that displays Yes, No and Cancel buttons
5464          * @type Object
5465          */
5466         YESNOCANCEL : {yes:true, no:true, cancel:true},
5467
5468         /**
5469          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5470          * @type Number
5471          */
5472         defaultTextHeight : 75,
5473         /**
5474          * The maximum width in pixels of the message box (defaults to 600)
5475          * @type Number
5476          */
5477         maxWidth : 600,
5478         /**
5479          * The minimum width in pixels of the message box (defaults to 100)
5480          * @type Number
5481          */
5482         minWidth : 100,
5483         /**
5484          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5485          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5486          * @type Number
5487          */
5488         minProgressWidth : 250,
5489         /**
5490          * An object containing the default button text strings that can be overriden for localized language support.
5491          * Supported properties are: ok, cancel, yes and no.
5492          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5493          * @type Object
5494          */
5495         buttonText : {
5496             ok : "OK",
5497             cancel : "Cancel",
5498             yes : "Yes",
5499             no : "No"
5500         }
5501     };
5502 }();
5503
5504 /**
5505  * Shorthand for {@link Roo.MessageBox}
5506  */
5507 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5508 Roo.Msg = Roo.Msg || Roo.MessageBox;
5509 /*
5510  * - LGPL
5511  *
5512  * navbar
5513  * 
5514  */
5515
5516 /**
5517  * @class Roo.bootstrap.Navbar
5518  * @extends Roo.bootstrap.Component
5519  * Bootstrap Navbar class
5520
5521  * @constructor
5522  * Create a new Navbar
5523  * @param {Object} config The config object
5524  */
5525
5526
5527 Roo.bootstrap.Navbar = function(config){
5528     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5529     this.addEvents({
5530         // raw events
5531         /**
5532          * @event beforetoggle
5533          * Fire before toggle the menu
5534          * @param {Roo.EventObject} e
5535          */
5536         "beforetoggle" : true
5537     });
5538 };
5539
5540 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5541     
5542     
5543    
5544     // private
5545     navItems : false,
5546     loadMask : false,
5547     
5548     
5549     getAutoCreate : function(){
5550         
5551         
5552         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5553         
5554     },
5555     
5556     initEvents :function ()
5557     {
5558         //Roo.log(this.el.select('.navbar-toggle',true));
5559         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5560         
5561         var mark = {
5562             tag: "div",
5563             cls:"x-dlg-mask"
5564         };
5565         
5566         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5567         
5568         var size = this.el.getSize();
5569         this.maskEl.setSize(size.width, size.height);
5570         this.maskEl.enableDisplayMode("block");
5571         this.maskEl.hide();
5572         
5573         if(this.loadMask){
5574             this.maskEl.show();
5575         }
5576     },
5577     
5578     
5579     getChildContainer : function()
5580     {
5581         if (this.el && this.el.select('.collapse').getCount()) {
5582             return this.el.select('.collapse',true).first();
5583         }
5584         
5585         return this.el;
5586     },
5587     
5588     mask : function()
5589     {
5590         this.maskEl.show();
5591     },
5592     
5593     unmask : function()
5594     {
5595         this.maskEl.hide();
5596     },
5597     onToggle : function()
5598     {
5599         
5600         if(this.fireEvent('beforetoggle', this) === false){
5601             return;
5602         }
5603         var ce = this.el.select('.navbar-collapse',true).first();
5604       
5605         if (!ce.hasClass('show')) {
5606            this.expand();
5607         } else {
5608             this.collapse();
5609         }
5610         
5611         
5612     
5613     },
5614     /**
5615      * Expand the navbar pulldown 
5616      */
5617     expand : function ()
5618     {
5619        
5620         var ce = this.el.select('.navbar-collapse',true).first();
5621         if (ce.hasClass('collapsing')) {
5622             return;
5623         }
5624         ce.dom.style.height = '';
5625                // show it...
5626         ce.addClass('in'); // old...
5627         ce.removeClass('collapse');
5628         ce.addClass('show');
5629         var h = ce.getHeight();
5630         Roo.log(h);
5631         ce.removeClass('show');
5632         // at this point we should be able to see it..
5633         ce.addClass('collapsing');
5634         
5635         ce.setHeight(0); // resize it ...
5636         ce.on('transitionend', function() {
5637             //Roo.log('done transition');
5638             ce.removeClass('collapsing');
5639             ce.addClass('show');
5640             ce.removeClass('collapse');
5641
5642             ce.dom.style.height = '';
5643         }, this, { single: true} );
5644         ce.setHeight(h);
5645         ce.dom.scrollTop = 0;
5646     },
5647     /**
5648      * Collapse the navbar pulldown 
5649      */
5650     collapse : function()
5651     {
5652          var ce = this.el.select('.navbar-collapse',true).first();
5653        
5654         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5655             // it's collapsed or collapsing..
5656             return;
5657         }
5658         ce.removeClass('in'); // old...
5659         ce.setHeight(ce.getHeight());
5660         ce.removeClass('show');
5661         ce.addClass('collapsing');
5662         
5663         ce.on('transitionend', function() {
5664             ce.dom.style.height = '';
5665             ce.removeClass('collapsing');
5666             ce.addClass('collapse');
5667         }, this, { single: true} );
5668         ce.setHeight(0);
5669     }
5670     
5671     
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSimplebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  *
5691  * @cfg {Boolean} inverse is inverted color
5692  * 
5693  * @cfg {String} type (nav | pills | tabs)
5694  * @cfg {Boolean} arrangement stacked | justified
5695  * @cfg {String} align (left | right) alignment
5696  * 
5697  * @cfg {Boolean} main (true|false) main nav bar? default false
5698  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5699  * 
5700  * @cfg {String} tag (header|footer|nav|div) default is nav 
5701
5702  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5703  * 
5704  * 
5705  * @constructor
5706  * Create a new Sidebar
5707  * @param {Object} config The config object
5708  */
5709
5710
5711 Roo.bootstrap.NavSimplebar = function(config){
5712     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5713 };
5714
5715 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5716     
5717     inverse: false,
5718     
5719     type: false,
5720     arrangement: '',
5721     align : false,
5722     
5723     weight : 'light',
5724     
5725     main : false,
5726     
5727     
5728     tag : false,
5729     
5730     
5731     getAutoCreate : function(){
5732         
5733         
5734         var cfg = {
5735             tag : this.tag || 'div',
5736             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5737         };
5738         if (['light','white'].indexOf(this.weight) > -1) {
5739             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5740         }
5741         cfg.cls += ' bg-' + this.weight;
5742         
5743         if (this.inverse) {
5744             cfg.cls += ' navbar-inverse';
5745             
5746         }
5747         
5748         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5749         
5750         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5751             return cfg;
5752         }
5753         
5754         
5755     
5756         
5757         cfg.cn = [
5758             {
5759                 cls: 'nav nav-' + this.xtype,
5760                 tag : 'ul'
5761             }
5762         ];
5763         
5764          
5765         this.type = this.type || 'nav';
5766         if (['tabs','pills'].indexOf(this.type) != -1) {
5767             cfg.cn[0].cls += ' nav-' + this.type
5768         
5769         
5770         } else {
5771             if (this.type!=='nav') {
5772                 Roo.log('nav type must be nav/tabs/pills')
5773             }
5774             cfg.cn[0].cls += ' navbar-nav'
5775         }
5776         
5777         
5778         
5779         
5780         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5781             cfg.cn[0].cls += ' nav-' + this.arrangement;
5782         }
5783         
5784         
5785         if (this.align === 'right') {
5786             cfg.cn[0].cls += ' navbar-right';
5787         }
5788         
5789         
5790         
5791         
5792         return cfg;
5793     
5794         
5795     }
5796     
5797     
5798     
5799 });
5800
5801
5802
5803  
5804
5805  
5806        /*
5807  * - LGPL
5808  *
5809  * navbar
5810  * navbar-fixed-top
5811  * navbar-expand-md  fixed-top 
5812  */
5813
5814 /**
5815  * @class Roo.bootstrap.NavHeaderbar
5816  * @extends Roo.bootstrap.NavSimplebar
5817  * Bootstrap Sidebar class
5818  *
5819  * @cfg {String} brand what is brand
5820  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5821  * @cfg {String} brand_href href of the brand
5822  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5823  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5824  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5825  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5826  * 
5827  * @constructor
5828  * Create a new Sidebar
5829  * @param {Object} config The config object
5830  */
5831
5832
5833 Roo.bootstrap.NavHeaderbar = function(config){
5834     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5835       
5836 };
5837
5838 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5839     
5840     position: '',
5841     brand: '',
5842     brand_href: false,
5843     srButton : true,
5844     autohide : false,
5845     desktopCenter : false,
5846    
5847     
5848     getAutoCreate : function(){
5849         
5850         var   cfg = {
5851             tag: this.nav || 'nav',
5852             cls: 'navbar navbar-expand-md',
5853             role: 'navigation',
5854             cn: []
5855         };
5856         
5857         var cn = cfg.cn;
5858         if (this.desktopCenter) {
5859             cn.push({cls : 'container', cn : []});
5860             cn = cn[0].cn;
5861         }
5862         
5863         if(this.srButton){
5864             var btn = {
5865                 tag: 'button',
5866                 type: 'button',
5867                 cls: 'navbar-toggle navbar-toggler',
5868                 'data-toggle': 'collapse',
5869                 cn: [
5870                     {
5871                         tag: 'span',
5872                         cls: 'sr-only',
5873                         html: 'Toggle navigation'
5874                     },
5875                     {
5876                         tag: 'span',
5877                         cls: 'icon-bar navbar-toggler-icon'
5878                     },
5879                     {
5880                         tag: 'span',
5881                         cls: 'icon-bar'
5882                     },
5883                     {
5884                         tag: 'span',
5885                         cls: 'icon-bar'
5886                     }
5887                 ]
5888             };
5889             
5890             cn.push( Roo.bootstrap.version == 4 ? btn : {
5891                 tag: 'div',
5892                 cls: 'navbar-header',
5893                 cn: [
5894                     btn
5895                 ]
5896             });
5897         }
5898         
5899         cn.push({
5900             tag: 'div',
5901             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5902             cn : []
5903         });
5904         
5905         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5906         
5907         if (['light','white'].indexOf(this.weight) > -1) {
5908             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5909         }
5910         cfg.cls += ' bg-' + this.weight;
5911         
5912         
5913         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5914             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5915             
5916             // tag can override this..
5917             
5918             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5919         }
5920         
5921         if (this.brand !== '') {
5922             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5923             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5924                 tag: 'a',
5925                 href: this.brand_href ? this.brand_href : '#',
5926                 cls: 'navbar-brand',
5927                 cn: [
5928                 this.brand
5929                 ]
5930             });
5931         }
5932         
5933         if(this.main){
5934             cfg.cls += ' main-nav';
5935         }
5936         
5937         
5938         return cfg;
5939
5940         
5941     },
5942     getHeaderChildContainer : function()
5943     {
5944         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5945             return this.el.select('.navbar-header',true).first();
5946         }
5947         
5948         return this.getChildContainer();
5949     },
5950     
5951     getChildContainer : function()
5952     {
5953          
5954         return this.el.select('.roo-navbar-collapse',true).first();
5955          
5956         
5957     },
5958     
5959     initEvents : function()
5960     {
5961         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5962         
5963         if (this.autohide) {
5964             
5965             var prevScroll = 0;
5966             var ft = this.el;
5967             
5968             Roo.get(document).on('scroll',function(e) {
5969                 var ns = Roo.get(document).getScroll().top;
5970                 var os = prevScroll;
5971                 prevScroll = ns;
5972                 
5973                 if(ns > os){
5974                     ft.removeClass('slideDown');
5975                     ft.addClass('slideUp');
5976                     return;
5977                 }
5978                 ft.removeClass('slideUp');
5979                 ft.addClass('slideDown');
5980                  
5981               
5982           },this);
5983         }
5984     }    
5985     
5986 });
5987
5988
5989
5990  
5991
5992  /*
5993  * - LGPL
5994  *
5995  * navbar
5996  * 
5997  */
5998
5999 /**
6000  * @class Roo.bootstrap.NavSidebar
6001  * @extends Roo.bootstrap.Navbar
6002  * Bootstrap Sidebar class
6003  * 
6004  * @constructor
6005  * Create a new Sidebar
6006  * @param {Object} config The config object
6007  */
6008
6009
6010 Roo.bootstrap.NavSidebar = function(config){
6011     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6012 };
6013
6014 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6015     
6016     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6017     
6018     getAutoCreate : function(){
6019         
6020         
6021         return  {
6022             tag: 'div',
6023             cls: 'sidebar sidebar-nav'
6024         };
6025     
6026         
6027     }
6028     
6029     
6030     
6031 });
6032
6033
6034
6035  
6036
6037  /*
6038  * - LGPL
6039  *
6040  * nav group
6041  * 
6042  */
6043
6044 /**
6045  * @class Roo.bootstrap.NavGroup
6046  * @extends Roo.bootstrap.Component
6047  * Bootstrap NavGroup class
6048  * @cfg {String} align (left|right)
6049  * @cfg {Boolean} inverse
6050  * @cfg {String} type (nav|pills|tab) default nav
6051  * @cfg {String} navId - reference Id for navbar.
6052  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6053  * 
6054  * @constructor
6055  * Create a new nav group
6056  * @param {Object} config The config object
6057  */
6058
6059 Roo.bootstrap.NavGroup = function(config){
6060     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6061     this.navItems = [];
6062    
6063     Roo.bootstrap.NavGroup.register(this);
6064      this.addEvents({
6065         /**
6066              * @event changed
6067              * Fires when the active item changes
6068              * @param {Roo.bootstrap.NavGroup} this
6069              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6070              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6071          */
6072         'changed': true
6073      });
6074     
6075 };
6076
6077 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6078     
6079     align: '',
6080     inverse: false,
6081     form: false,
6082     type: 'nav',
6083     navId : '',
6084     // private
6085     pilltype : true,
6086     
6087     navItems : false, 
6088     
6089     getAutoCreate : function()
6090     {
6091         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6092         
6093         cfg = {
6094             tag : 'ul',
6095             cls: 'nav' 
6096         };
6097         if (Roo.bootstrap.version == 4) {
6098             if (['tabs','pills'].indexOf(this.type) != -1) {
6099                 cfg.cls += ' nav-' + this.type; 
6100             } else {
6101                 // trying to remove so header bar can right align top?
6102                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6103                     // do not use on header bar... 
6104                     cfg.cls += ' navbar-nav';
6105                 }
6106             }
6107             
6108         } else {
6109             if (['tabs','pills'].indexOf(this.type) != -1) {
6110                 cfg.cls += ' nav-' + this.type
6111             } else {
6112                 if (this.type !== 'nav') {
6113                     Roo.log('nav type must be nav/tabs/pills')
6114                 }
6115                 cfg.cls += ' navbar-nav'
6116             }
6117         }
6118         
6119         if (this.parent() && this.parent().sidebar) {
6120             cfg = {
6121                 tag: 'ul',
6122                 cls: 'dashboard-menu sidebar-menu'
6123             };
6124             
6125             return cfg;
6126         }
6127         
6128         if (this.form === true) {
6129             cfg = {
6130                 tag: 'form',
6131                 cls: 'navbar-form form-inline'
6132             };
6133             //nav navbar-right ml-md-auto
6134             if (this.align === 'right') {
6135                 cfg.cls += ' navbar-right ml-md-auto';
6136             } else {
6137                 cfg.cls += ' navbar-left';
6138             }
6139         }
6140         
6141         if (this.align === 'right') {
6142             cfg.cls += ' navbar-right ml-md-auto';
6143         } else {
6144             cfg.cls += ' mr-auto';
6145         }
6146         
6147         if (this.inverse) {
6148             cfg.cls += ' navbar-inverse';
6149             
6150         }
6151         
6152         
6153         return cfg;
6154     },
6155     /**
6156     * sets the active Navigation item
6157     * @param {Roo.bootstrap.NavItem} the new current navitem
6158     */
6159     setActiveItem : function(item)
6160     {
6161         var prev = false;
6162         Roo.each(this.navItems, function(v){
6163             if (v == item) {
6164                 return ;
6165             }
6166             if (v.isActive()) {
6167                 v.setActive(false, true);
6168                 prev = v;
6169                 
6170             }
6171             
6172         });
6173
6174         item.setActive(true, true);
6175         this.fireEvent('changed', this, item, prev);
6176         
6177         
6178     },
6179     /**
6180     * gets the active Navigation item
6181     * @return {Roo.bootstrap.NavItem} the current navitem
6182     */
6183     getActive : function()
6184     {
6185         
6186         var prev = false;
6187         Roo.each(this.navItems, function(v){
6188             
6189             if (v.isActive()) {
6190                 prev = v;
6191                 
6192             }
6193             
6194         });
6195         return prev;
6196     },
6197     
6198     indexOfNav : function()
6199     {
6200         
6201         var prev = false;
6202         Roo.each(this.navItems, function(v,i){
6203             
6204             if (v.isActive()) {
6205                 prev = i;
6206                 
6207             }
6208             
6209         });
6210         return prev;
6211     },
6212     /**
6213     * adds a Navigation item
6214     * @param {Roo.bootstrap.NavItem} the navitem to add
6215     */
6216     addItem : function(cfg)
6217     {
6218         if (this.form && Roo.bootstrap.version == 4) {
6219             cfg.tag = 'div';
6220         }
6221         var cn = new Roo.bootstrap.NavItem(cfg);
6222         this.register(cn);
6223         cn.parentId = this.id;
6224         cn.onRender(this.el, null);
6225         return cn;
6226     },
6227     /**
6228     * register a Navigation item
6229     * @param {Roo.bootstrap.NavItem} the navitem to add
6230     */
6231     register : function(item)
6232     {
6233         this.navItems.push( item);
6234         item.navId = this.navId;
6235     
6236     },
6237     
6238     /**
6239     * clear all the Navigation item
6240     */
6241    
6242     clearAll : function()
6243     {
6244         this.navItems = [];
6245         this.el.dom.innerHTML = '';
6246     },
6247     
6248     getNavItem: function(tabId)
6249     {
6250         var ret = false;
6251         Roo.each(this.navItems, function(e) {
6252             if (e.tabId == tabId) {
6253                ret =  e;
6254                return false;
6255             }
6256             return true;
6257             
6258         });
6259         return ret;
6260     },
6261     
6262     setActiveNext : function()
6263     {
6264         var i = this.indexOfNav(this.getActive());
6265         if (i > this.navItems.length) {
6266             return;
6267         }
6268         this.setActiveItem(this.navItems[i+1]);
6269     },
6270     setActivePrev : function()
6271     {
6272         var i = this.indexOfNav(this.getActive());
6273         if (i  < 1) {
6274             return;
6275         }
6276         this.setActiveItem(this.navItems[i-1]);
6277     },
6278     clearWasActive : function(except) {
6279         Roo.each(this.navItems, function(e) {
6280             if (e.tabId != except.tabId && e.was_active) {
6281                e.was_active = false;
6282                return false;
6283             }
6284             return true;
6285             
6286         });
6287     },
6288     getWasActive : function ()
6289     {
6290         var r = false;
6291         Roo.each(this.navItems, function(e) {
6292             if (e.was_active) {
6293                r = e;
6294                return false;
6295             }
6296             return true;
6297             
6298         });
6299         return r;
6300     }
6301     
6302     
6303 });
6304
6305  
6306 Roo.apply(Roo.bootstrap.NavGroup, {
6307     
6308     groups: {},
6309      /**
6310     * register a Navigation Group
6311     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6312     */
6313     register : function(navgrp)
6314     {
6315         this.groups[navgrp.navId] = navgrp;
6316         
6317     },
6318     /**
6319     * fetch a Navigation Group based on the navigation ID
6320     * @param {string} the navgroup to add
6321     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6322     */
6323     get: function(navId) {
6324         if (typeof(this.groups[navId]) == 'undefined') {
6325             return false;
6326             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6327         }
6328         return this.groups[navId] ;
6329     }
6330     
6331     
6332     
6333 });
6334
6335  /*
6336  * - LGPL
6337  *
6338  * row
6339  * 
6340  */
6341
6342 /**
6343  * @class Roo.bootstrap.NavItem
6344  * @extends Roo.bootstrap.Component
6345  * Bootstrap Navbar.NavItem class
6346  * @cfg {String} href  link to
6347  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6348  * @cfg {Boolean} button_outline show and outlined button
6349  * @cfg {String} html content of button
6350  * @cfg {String} badge text inside badge
6351  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6352  * @cfg {String} glyphicon DEPRICATED - use fa
6353  * @cfg {String} icon DEPRICATED - use fa
6354  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6355  * @cfg {Boolean} active Is item active
6356  * @cfg {Boolean} disabled Is item disabled
6357  * @cfg {String} linkcls  Link Class
6358  * @cfg {Boolean} preventDefault (true | false) default false
6359  * @cfg {String} tabId the tab that this item activates.
6360  * @cfg {String} tagtype (a|span) render as a href or span?
6361  * @cfg {Boolean} animateRef (true|false) link to element default false  
6362   
6363  * @constructor
6364  * Create a new Navbar Item
6365  * @param {Object} config The config object
6366  */
6367 Roo.bootstrap.NavItem = function(config){
6368     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6369     this.addEvents({
6370         // raw events
6371         /**
6372          * @event click
6373          * The raw click event for the entire grid.
6374          * @param {Roo.EventObject} e
6375          */
6376         "click" : true,
6377          /**
6378             * @event changed
6379             * Fires when the active item active state changes
6380             * @param {Roo.bootstrap.NavItem} this
6381             * @param {boolean} state the new state
6382              
6383          */
6384         'changed': true,
6385         /**
6386             * @event scrollto
6387             * Fires when scroll to element
6388             * @param {Roo.bootstrap.NavItem} this
6389             * @param {Object} options
6390             * @param {Roo.EventObject} e
6391              
6392          */
6393         'scrollto': true
6394     });
6395    
6396 };
6397
6398 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6399     
6400     href: false,
6401     html: '',
6402     badge: '',
6403     icon: false,
6404     fa : false,
6405     glyphicon: false,
6406     active: false,
6407     preventDefault : false,
6408     tabId : false,
6409     tagtype : 'a',
6410     tag: 'li',
6411     disabled : false,
6412     animateRef : false,
6413     was_active : false,
6414     button_weight : '',
6415     button_outline : false,
6416     linkcls : '',
6417     navLink: false,
6418     
6419     getAutoCreate : function(){
6420          
6421         var cfg = {
6422             tag: this.tag,
6423             cls: 'nav-item'
6424         };
6425         
6426         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6427         
6428         if (this.active) {
6429             cfg.cls +=  ' active' ;
6430         }
6431         if (this.disabled) {
6432             cfg.cls += ' disabled';
6433         }
6434         
6435         // BS4 only?
6436         if (this.button_weight.length) {
6437             cfg.tag = this.href ? 'a' : 'button';
6438             cfg.html = this.html || '';
6439             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6440             if (this.href) {
6441                 cfg.href = this.href;
6442             }
6443             if (this.fa) {
6444                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6445             } else {
6446                 cfg.cls += " nav-html";
6447             }
6448             
6449             // menu .. should add dropdown-menu class - so no need for carat..
6450             
6451             if (this.badge !== '') {
6452                  
6453                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6454             }
6455             return cfg;
6456         }
6457         
6458         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6459             cfg.cn = [
6460                 {
6461                     tag: this.tagtype,
6462                     href : this.href || "#",
6463                     html: this.html || '',
6464                     cls : ''
6465                 }
6466             ];
6467             if (this.tagtype == 'a') {
6468                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6469         
6470             }
6471             if (this.icon) {
6472                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6473             } else  if (this.fa) {
6474                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6475             } else if(this.glyphicon) {
6476                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6477             } else {
6478                 cfg.cn[0].cls += " nav-html";
6479             }
6480             
6481             if (this.menu) {
6482                 cfg.cn[0].html += " <span class='caret'></span>";
6483              
6484             }
6485             
6486             if (this.badge !== '') {
6487                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6488             }
6489         }
6490         
6491         
6492         
6493         return cfg;
6494     },
6495     onRender : function(ct, position)
6496     {
6497        // Roo.log("Call onRender: " + this.xtype);
6498         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6499             this.tag = 'div';
6500         }
6501         
6502         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6503         this.navLink = this.el.select('.nav-link',true).first();
6504         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6505         return ret;
6506     },
6507       
6508     
6509     initEvents: function() 
6510     {
6511         if (typeof (this.menu) != 'undefined') {
6512             this.menu.parentType = this.xtype;
6513             this.menu.triggerEl = this.el;
6514             this.menu = this.addxtype(Roo.apply({}, this.menu));
6515         }
6516         
6517         this.el.on('click', this.onClick, this);
6518         
6519         //if(this.tagtype == 'span'){
6520         //    this.el.select('span',true).on('click', this.onClick, this);
6521         //}
6522        
6523         // at this point parent should be available..
6524         this.parent().register(this);
6525     },
6526     
6527     onClick : function(e)
6528     {
6529         if (e.getTarget('.dropdown-menu-item')) {
6530             // did you click on a menu itemm.... - then don't trigger onclick..
6531             return;
6532         }
6533         
6534         if(
6535                 this.preventDefault || 
6536                 this.href == '#' 
6537         ){
6538             Roo.log("NavItem - prevent Default?");
6539             e.preventDefault();
6540         }
6541         
6542         if (this.disabled) {
6543             return;
6544         }
6545         
6546         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6547         if (tg && tg.transition) {
6548             Roo.log("waiting for the transitionend");
6549             return;
6550         }
6551         
6552         
6553         
6554         //Roo.log("fire event clicked");
6555         if(this.fireEvent('click', this, e) === false){
6556             return;
6557         };
6558         
6559         if(this.tagtype == 'span'){
6560             return;
6561         }
6562         
6563         //Roo.log(this.href);
6564         var ael = this.el.select('a',true).first();
6565         //Roo.log(ael);
6566         
6567         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6568             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6569             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6570                 return; // ignore... - it's a 'hash' to another page.
6571             }
6572             Roo.log("NavItem - prevent Default?");
6573             e.preventDefault();
6574             this.scrollToElement(e);
6575         }
6576         
6577         
6578         var p =  this.parent();
6579    
6580         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6581             if (typeof(p.setActiveItem) !== 'undefined') {
6582                 p.setActiveItem(this);
6583             }
6584         }
6585         
6586         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6587         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6588             // remove the collapsed menu expand...
6589             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6590         }
6591     },
6592     
6593     isActive: function () {
6594         return this.active
6595     },
6596     setActive : function(state, fire, is_was_active)
6597     {
6598         if (this.active && !state && this.navId) {
6599             this.was_active = true;
6600             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6601             if (nv) {
6602                 nv.clearWasActive(this);
6603             }
6604             
6605         }
6606         this.active = state;
6607         
6608         if (!state ) {
6609             this.el.removeClass('active');
6610             this.navLink ? this.navLink.removeClass('active') : false;
6611         } else if (!this.el.hasClass('active')) {
6612             
6613             this.el.addClass('active');
6614             if (Roo.bootstrap.version == 4 && this.navLink ) {
6615                 this.navLink.addClass('active');
6616             }
6617             
6618         }
6619         if (fire) {
6620             this.fireEvent('changed', this, state);
6621         }
6622         
6623         // show a panel if it's registered and related..
6624         
6625         if (!this.navId || !this.tabId || !state || is_was_active) {
6626             return;
6627         }
6628         
6629         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6630         if (!tg) {
6631             return;
6632         }
6633         var pan = tg.getPanelByName(this.tabId);
6634         if (!pan) {
6635             return;
6636         }
6637         // if we can not flip to new panel - go back to old nav highlight..
6638         if (false == tg.showPanel(pan)) {
6639             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6640             if (nv) {
6641                 var onav = nv.getWasActive();
6642                 if (onav) {
6643                     onav.setActive(true, false, true);
6644                 }
6645             }
6646             
6647         }
6648         
6649         
6650         
6651     },
6652      // this should not be here...
6653     setDisabled : function(state)
6654     {
6655         this.disabled = state;
6656         if (!state ) {
6657             this.el.removeClass('disabled');
6658         } else if (!this.el.hasClass('disabled')) {
6659             this.el.addClass('disabled');
6660         }
6661         
6662     },
6663     
6664     /**
6665      * Fetch the element to display the tooltip on.
6666      * @return {Roo.Element} defaults to this.el
6667      */
6668     tooltipEl : function()
6669     {
6670         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6671     },
6672     
6673     scrollToElement : function(e)
6674     {
6675         var c = document.body;
6676         
6677         /*
6678          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6679          */
6680         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6681             c = document.documentElement;
6682         }
6683         
6684         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6685         
6686         if(!target){
6687             return;
6688         }
6689
6690         var o = target.calcOffsetsTo(c);
6691         
6692         var options = {
6693             target : target,
6694             value : o[1]
6695         };
6696         
6697         this.fireEvent('scrollto', this, options, e);
6698         
6699         Roo.get(c).scrollTo('top', options.value, true);
6700         
6701         return;
6702     },
6703     /**
6704      * Set the HTML (text content) of the item
6705      * @param {string} html  content for the nav item
6706      */
6707     setHtml : function(html)
6708     {
6709         this.html = html;
6710         this.htmlEl.dom.innerHTML = html;
6711         
6712     } 
6713 });
6714  
6715
6716  /*
6717  * - LGPL
6718  *
6719  * sidebar item
6720  *
6721  *  li
6722  *    <span> icon </span>
6723  *    <span> text </span>
6724  *    <span>badge </span>
6725  */
6726
6727 /**
6728  * @class Roo.bootstrap.NavSidebarItem
6729  * @extends Roo.bootstrap.NavItem
6730  * Bootstrap Navbar.NavSidebarItem class
6731  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6732  * {Boolean} open is the menu open
6733  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6734  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6735  * {String} buttonSize (sm|md|lg)the extra classes for the button
6736  * {Boolean} showArrow show arrow next to the text (default true)
6737  * @constructor
6738  * Create a new Navbar Button
6739  * @param {Object} config The config object
6740  */
6741 Roo.bootstrap.NavSidebarItem = function(config){
6742     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6743     this.addEvents({
6744         // raw events
6745         /**
6746          * @event click
6747          * The raw click event for the entire grid.
6748          * @param {Roo.EventObject} e
6749          */
6750         "click" : true,
6751          /**
6752             * @event changed
6753             * Fires when the active item active state changes
6754             * @param {Roo.bootstrap.NavSidebarItem} this
6755             * @param {boolean} state the new state
6756              
6757          */
6758         'changed': true
6759     });
6760    
6761 };
6762
6763 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6764     
6765     badgeWeight : 'default',
6766     
6767     open: false,
6768     
6769     buttonView : false,
6770     
6771     buttonWeight : 'default',
6772     
6773     buttonSize : 'md',
6774     
6775     showArrow : true,
6776     
6777     getAutoCreate : function(){
6778         
6779         
6780         var a = {
6781                 tag: 'a',
6782                 href : this.href || '#',
6783                 cls: '',
6784                 html : '',
6785                 cn : []
6786         };
6787         
6788         if(this.buttonView){
6789             a = {
6790                 tag: 'button',
6791                 href : this.href || '#',
6792                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6793                 html : this.html,
6794                 cn : []
6795             };
6796         }
6797         
6798         var cfg = {
6799             tag: 'li',
6800             cls: '',
6801             cn: [ a ]
6802         };
6803         
6804         if (this.active) {
6805             cfg.cls += ' active';
6806         }
6807         
6808         if (this.disabled) {
6809             cfg.cls += ' disabled';
6810         }
6811         if (this.open) {
6812             cfg.cls += ' open x-open';
6813         }
6814         // left icon..
6815         if (this.glyphicon || this.icon) {
6816             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6817             a.cn.push({ tag : 'i', cls : c }) ;
6818         }
6819         
6820         if(!this.buttonView){
6821             var span = {
6822                 tag: 'span',
6823                 html : this.html || ''
6824             };
6825
6826             a.cn.push(span);
6827             
6828         }
6829         
6830         if (this.badge !== '') {
6831             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6832         }
6833         
6834         if (this.menu) {
6835             
6836             if(this.showArrow){
6837                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6838             }
6839             
6840             a.cls += ' dropdown-toggle treeview' ;
6841         }
6842         
6843         return cfg;
6844     },
6845     
6846     initEvents : function()
6847     { 
6848         if (typeof (this.menu) != 'undefined') {
6849             this.menu.parentType = this.xtype;
6850             this.menu.triggerEl = this.el;
6851             this.menu = this.addxtype(Roo.apply({}, this.menu));
6852         }
6853         
6854         this.el.on('click', this.onClick, this);
6855         
6856         if(this.badge !== ''){
6857             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6858         }
6859         
6860     },
6861     
6862     onClick : function(e)
6863     {
6864         if(this.disabled){
6865             e.preventDefault();
6866             return;
6867         }
6868         
6869         if(this.preventDefault){
6870             e.preventDefault();
6871         }
6872         
6873         this.fireEvent('click', this, e);
6874     },
6875     
6876     disable : function()
6877     {
6878         this.setDisabled(true);
6879     },
6880     
6881     enable : function()
6882     {
6883         this.setDisabled(false);
6884     },
6885     
6886     setDisabled : function(state)
6887     {
6888         if(this.disabled == state){
6889             return;
6890         }
6891         
6892         this.disabled = state;
6893         
6894         if (state) {
6895             this.el.addClass('disabled');
6896             return;
6897         }
6898         
6899         this.el.removeClass('disabled');
6900         
6901         return;
6902     },
6903     
6904     setActive : function(state)
6905     {
6906         if(this.active == state){
6907             return;
6908         }
6909         
6910         this.active = state;
6911         
6912         if (state) {
6913             this.el.addClass('active');
6914             return;
6915         }
6916         
6917         this.el.removeClass('active');
6918         
6919         return;
6920     },
6921     
6922     isActive: function () 
6923     {
6924         return this.active;
6925     },
6926     
6927     setBadge : function(str)
6928     {
6929         if(!this.badgeEl){
6930             return;
6931         }
6932         
6933         this.badgeEl.dom.innerHTML = str;
6934     }
6935     
6936    
6937      
6938  
6939 });
6940  
6941
6942  /*
6943  * - LGPL
6944  *
6945  *  Breadcrumb Nav
6946  * 
6947  */
6948 Roo.namespace('Roo.bootstrap.breadcrumb');
6949
6950
6951 /**
6952  * @class Roo.bootstrap.breadcrumb.Nav
6953  * @extends Roo.bootstrap.Component
6954  * Bootstrap Breadcrumb Nav Class
6955  *  
6956  * @children Roo.bootstrap.breadcrumb.Item
6957  * 
6958  * @constructor
6959  * Create a new breadcrumb.Nav
6960  * @param {Object} config The config object
6961  */
6962
6963
6964 Roo.bootstrap.breadcrumb.Nav = function(config){
6965     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6966     
6967     
6968 };
6969
6970 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6971     
6972     getAutoCreate : function()
6973     {
6974
6975         var cfg = {
6976             tag: 'nav',
6977             cn : [
6978                 {
6979                     tag : 'ol',
6980                     cls : 'breadcrumb'
6981                 }
6982             ]
6983             
6984         };
6985           
6986         return cfg;
6987     },
6988     
6989     initEvents: function()
6990     {
6991         this.olEl = this.el.select('ol',true).first();    
6992     },
6993     getChildContainer : function()
6994     {
6995         return this.olEl;  
6996     }
6997     
6998 });
6999
7000  /*
7001  * - LGPL
7002  *
7003  *  Breadcrumb Item
7004  * 
7005  */
7006
7007
7008 /**
7009  * @class Roo.bootstrap.breadcrumb.Nav
7010  * @extends Roo.bootstrap.Component
7011  * Bootstrap Breadcrumb Nav Class
7012  *  
7013  * @children Roo.bootstrap.breadcrumb.Component
7014  * @cfg {String} html the content of the link.
7015  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7016  * @cfg {Boolean} active is it active
7017
7018  * 
7019  * @constructor
7020  * Create a new breadcrumb.Nav
7021  * @param {Object} config The config object
7022  */
7023
7024 Roo.bootstrap.breadcrumb.Item = function(config){
7025     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7026     this.addEvents({
7027         // img events
7028         /**
7029          * @event click
7030          * The img click event for the img.
7031          * @param {Roo.EventObject} e
7032          */
7033         "click" : true
7034     });
7035     
7036 };
7037
7038 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7039     
7040     href: false,
7041     html : '',
7042     
7043     getAutoCreate : function()
7044     {
7045
7046         var cfg = {
7047             tag: 'li',
7048             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7049         };
7050         if (this.href !== false) {
7051             cfg.cn = [{
7052                 tag : 'a',
7053                 href : this.href,
7054                 html : this.html
7055             }];
7056         } else {
7057             cfg.html = this.html;
7058         }
7059         
7060         return cfg;
7061     },
7062     
7063     initEvents: function()
7064     {
7065         if (this.href) {
7066             this.el.select('a', true).first().on('click',this.onClick, this)
7067         }
7068         
7069     },
7070     onClick : function(e)
7071     {
7072         e.preventDefault();
7073         this.fireEvent('click',this,  e);
7074     }
7075     
7076 });
7077
7078  /*
7079  * - LGPL
7080  *
7081  * row
7082  * 
7083  */
7084
7085 /**
7086  * @class Roo.bootstrap.Row
7087  * @extends Roo.bootstrap.Component
7088  * Bootstrap Row class (contains columns...)
7089  * 
7090  * @constructor
7091  * Create a new Row
7092  * @param {Object} config The config object
7093  */
7094
7095 Roo.bootstrap.Row = function(config){
7096     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7097 };
7098
7099 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7100     
7101     getAutoCreate : function(){
7102        return {
7103             cls: 'row clearfix'
7104        };
7105     }
7106     
7107     
7108 });
7109
7110  
7111
7112  /*
7113  * - LGPL
7114  *
7115  * pagination
7116  * 
7117  */
7118
7119 /**
7120  * @class Roo.bootstrap.Pagination
7121  * @extends Roo.bootstrap.Component
7122  * Bootstrap Pagination class
7123  * @cfg {String} size xs | sm | md | lg
7124  * @cfg {Boolean} inverse false | true
7125  * 
7126  * @constructor
7127  * Create a new Pagination
7128  * @param {Object} config The config object
7129  */
7130
7131 Roo.bootstrap.Pagination = function(config){
7132     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7133 };
7134
7135 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7136     
7137     cls: false,
7138     size: false,
7139     inverse: false,
7140     
7141     getAutoCreate : function(){
7142         var cfg = {
7143             tag: 'ul',
7144                 cls: 'pagination'
7145         };
7146         if (this.inverse) {
7147             cfg.cls += ' inverse';
7148         }
7149         if (this.html) {
7150             cfg.html=this.html;
7151         }
7152         if (this.cls) {
7153             cfg.cls += " " + this.cls;
7154         }
7155         return cfg;
7156     }
7157    
7158 });
7159
7160  
7161
7162  /*
7163  * - LGPL
7164  *
7165  * Pagination item
7166  * 
7167  */
7168
7169
7170 /**
7171  * @class Roo.bootstrap.PaginationItem
7172  * @extends Roo.bootstrap.Component
7173  * Bootstrap PaginationItem class
7174  * @cfg {String} html text
7175  * @cfg {String} href the link
7176  * @cfg {Boolean} preventDefault (true | false) default true
7177  * @cfg {Boolean} active (true | false) default false
7178  * @cfg {Boolean} disabled default false
7179  * 
7180  * 
7181  * @constructor
7182  * Create a new PaginationItem
7183  * @param {Object} config The config object
7184  */
7185
7186
7187 Roo.bootstrap.PaginationItem = function(config){
7188     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7189     this.addEvents({
7190         // raw events
7191         /**
7192          * @event click
7193          * The raw click event for the entire grid.
7194          * @param {Roo.EventObject} e
7195          */
7196         "click" : true
7197     });
7198 };
7199
7200 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7201     
7202     href : false,
7203     html : false,
7204     preventDefault: true,
7205     active : false,
7206     cls : false,
7207     disabled: false,
7208     
7209     getAutoCreate : function(){
7210         var cfg= {
7211             tag: 'li',
7212             cn: [
7213                 {
7214                     tag : 'a',
7215                     href : this.href ? this.href : '#',
7216                     html : this.html ? this.html : ''
7217                 }
7218             ]
7219         };
7220         
7221         if(this.cls){
7222             cfg.cls = this.cls;
7223         }
7224         
7225         if(this.disabled){
7226             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7227         }
7228         
7229         if(this.active){
7230             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7231         }
7232         
7233         return cfg;
7234     },
7235     
7236     initEvents: function() {
7237         
7238         this.el.on('click', this.onClick, this);
7239         
7240     },
7241     onClick : function(e)
7242     {
7243         Roo.log('PaginationItem on click ');
7244         if(this.preventDefault){
7245             e.preventDefault();
7246         }
7247         
7248         if(this.disabled){
7249             return;
7250         }
7251         
7252         this.fireEvent('click', this, e);
7253     }
7254    
7255 });
7256
7257  
7258
7259  /*
7260  * - LGPL
7261  *
7262  * slider
7263  * 
7264  */
7265
7266
7267 /**
7268  * @class Roo.bootstrap.Slider
7269  * @extends Roo.bootstrap.Component
7270  * Bootstrap Slider class
7271  *    
7272  * @constructor
7273  * Create a new Slider
7274  * @param {Object} config The config object
7275  */
7276
7277 Roo.bootstrap.Slider = function(config){
7278     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7279 };
7280
7281 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7282     
7283     getAutoCreate : function(){
7284         
7285         var cfg = {
7286             tag: 'div',
7287             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7288             cn: [
7289                 {
7290                     tag: 'a',
7291                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7292                 }
7293             ]
7294         };
7295         
7296         return cfg;
7297     }
7298    
7299 });
7300
7301  /*
7302  * Based on:
7303  * Ext JS Library 1.1.1
7304  * Copyright(c) 2006-2007, Ext JS, LLC.
7305  *
7306  * Originally Released Under LGPL - original licence link has changed is not relivant.
7307  *
7308  * Fork - LGPL
7309  * <script type="text/javascript">
7310  */
7311  /**
7312  * @extends Roo.dd.DDProxy
7313  * @class Roo.grid.SplitDragZone
7314  * Support for Column Header resizing
7315  * @constructor
7316  * @param {Object} config
7317  */
7318 // private
7319 // This is a support class used internally by the Grid components
7320 Roo.grid.SplitDragZone = function(grid, hd, hd2){
7321     this.grid = grid;
7322     this.view = grid.getView();
7323     this.proxy = this.view.resizeProxy;
7324     Roo.grid.SplitDragZone.superclass.constructor.call(
7325         this,
7326         hd, // ID
7327         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
7328         {  // CONFIG
7329             dragElId : Roo.id(this.proxy.dom),
7330             resizeFrame:false
7331         }
7332     );
7333     
7334     this.setHandleElId(Roo.id(hd));
7335     if (hd2 !== false) {
7336         this.setOuterHandleElId(Roo.id(hd2));
7337     }
7338     
7339     this.scroll = false;
7340 };
7341 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
7342     fly: Roo.Element.fly,
7343
7344     b4StartDrag : function(x, y){
7345         this.view.headersDisabled = true;
7346         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
7347                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
7348         );
7349         this.proxy.setHeight(h);
7350         
7351         // for old system colWidth really stored the actual width?
7352         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
7353         // which in reality did not work.. - it worked only for fixed sizes
7354         // for resizable we need to use actual sizes.
7355         var w = this.cm.getColumnWidth(this.cellIndex);
7356         if (!this.view.mainWrap) {
7357             // bootstrap.
7358             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
7359         }
7360         
7361         
7362         
7363         // this was w-this.grid.minColumnWidth;
7364         // doesnt really make sense? - w = thie curren width or the rendered one?
7365         var minw = Math.max(w-this.grid.minColumnWidth, 0);
7366         this.resetConstraints();
7367         this.setXConstraint(minw, 1000);
7368         this.setYConstraint(0, 0);
7369         this.minX = x - minw;
7370         this.maxX = x + 1000;
7371         this.startPos = x;
7372         if (!this.view.mainWrap) { // this is Bootstrap code..
7373             this.getDragEl().style.display='block';
7374         }
7375         
7376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
7377     },
7378
7379
7380     handleMouseDown : function(e){
7381         ev = Roo.EventObject.setEvent(e);
7382         var t = this.fly(ev.getTarget());
7383         if(t.hasClass("x-grid-split")){
7384             this.cellIndex = this.view.getCellIndex(t.dom);
7385             this.split = t.dom;
7386             this.cm = this.grid.colModel;
7387             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
7388                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
7389             }
7390         }
7391     },
7392
7393     endDrag : function(e){
7394         this.view.headersDisabled = false;
7395         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
7396         var diff = endX - this.startPos;
7397         // 
7398         var w = this.cm.getColumnWidth(this.cellIndex);
7399         if (!this.view.mainWrap) {
7400             w = 0;
7401         }
7402         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
7403     },
7404
7405     autoOffset : function(){
7406         this.setDelta(0,0);
7407     }
7408 });/*
7409  * Based on:
7410  * Ext JS Library 1.1.1
7411  * Copyright(c) 2006-2007, Ext JS, LLC.
7412  *
7413  * Originally Released Under LGPL - original licence link has changed is not relivant.
7414  *
7415  * Fork - LGPL
7416  * <script type="text/javascript">
7417  */
7418
7419 /**
7420  * @class Roo.grid.AbstractSelectionModel
7421  * @extends Roo.util.Observable
7422  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7423  * implemented by descendant classes.  This class should not be directly instantiated.
7424  * @constructor
7425  */
7426 Roo.grid.AbstractSelectionModel = function(){
7427     this.locked = false;
7428     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7429 };
7430
7431 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7432     /** @ignore Called by the grid automatically. Do not call directly. */
7433     init : function(grid){
7434         this.grid = grid;
7435         this.initEvents();
7436     },
7437
7438     /**
7439      * Locks the selections.
7440      */
7441     lock : function(){
7442         this.locked = true;
7443     },
7444
7445     /**
7446      * Unlocks the selections.
7447      */
7448     unlock : function(){
7449         this.locked = false;
7450     },
7451
7452     /**
7453      * Returns true if the selections are locked.
7454      * @return {Boolean}
7455      */
7456     isLocked : function(){
7457         return this.locked;
7458     }
7459 });/*
7460  * Based on:
7461  * Ext JS Library 1.1.1
7462  * Copyright(c) 2006-2007, Ext JS, LLC.
7463  *
7464  * Originally Released Under LGPL - original licence link has changed is not relivant.
7465  *
7466  * Fork - LGPL
7467  * <script type="text/javascript">
7468  */
7469 /**
7470  * @extends Roo.grid.AbstractSelectionModel
7471  * @class Roo.grid.RowSelectionModel
7472  * The default SelectionModel used by {@link Roo.grid.Grid}.
7473  * It supports multiple selections and keyboard selection/navigation. 
7474  * @constructor
7475  * @param {Object} config
7476  */
7477 Roo.grid.RowSelectionModel = function(config){
7478     Roo.apply(this, config);
7479     this.selections = new Roo.util.MixedCollection(false, function(o){
7480         return o.id;
7481     });
7482
7483     this.last = false;
7484     this.lastActive = false;
7485
7486     this.addEvents({
7487         /**
7488         * @event selectionchange
7489         * Fires when the selection changes
7490         * @param {SelectionModel} this
7491         */
7492        "selectionchange" : true,
7493        /**
7494         * @event afterselectionchange
7495         * Fires after the selection changes (eg. by key press or clicking)
7496         * @param {SelectionModel} this
7497         */
7498        "afterselectionchange" : true,
7499        /**
7500         * @event beforerowselect
7501         * Fires when a row is selected being selected, return false to cancel.
7502         * @param {SelectionModel} this
7503         * @param {Number} rowIndex The selected index
7504         * @param {Boolean} keepExisting False if other selections will be cleared
7505         */
7506        "beforerowselect" : true,
7507        /**
7508         * @event rowselect
7509         * Fires when a row is selected.
7510         * @param {SelectionModel} this
7511         * @param {Number} rowIndex The selected index
7512         * @param {Roo.data.Record} r The record
7513         */
7514        "rowselect" : true,
7515        /**
7516         * @event rowdeselect
7517         * Fires when a row is deselected.
7518         * @param {SelectionModel} this
7519         * @param {Number} rowIndex The selected index
7520         */
7521         "rowdeselect" : true
7522     });
7523     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7524     this.locked = false;
7525 };
7526
7527 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7528     /**
7529      * @cfg {Boolean} singleSelect
7530      * True to allow selection of only one row at a time (defaults to false)
7531      */
7532     singleSelect : false,
7533
7534     // private
7535     initEvents : function(){
7536
7537         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7538             this.grid.on("mousedown", this.handleMouseDown, this);
7539         }else{ // allow click to work like normal
7540             this.grid.on("rowclick", this.handleDragableRowClick, this);
7541         }
7542         // bootstrap does not have a view..
7543         var view = this.grid.view ? this.grid.view : this.grid;
7544         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7545             "up" : function(e){
7546                 if(!e.shiftKey){
7547                     this.selectPrevious(e.shiftKey);
7548                 }else if(this.last !== false && this.lastActive !== false){
7549                     var last = this.last;
7550                     this.selectRange(this.last,  this.lastActive-1);
7551                     view.focusRow(this.lastActive);
7552                     if(last !== false){
7553                         this.last = last;
7554                     }
7555                 }else{
7556                     this.selectFirstRow();
7557                 }
7558                 this.fireEvent("afterselectionchange", this);
7559             },
7560             "down" : function(e){
7561                 if(!e.shiftKey){
7562                     this.selectNext(e.shiftKey);
7563                 }else if(this.last !== false && this.lastActive !== false){
7564                     var last = this.last;
7565                     this.selectRange(this.last,  this.lastActive+1);
7566                     view.focusRow(this.lastActive);
7567                     if(last !== false){
7568                         this.last = last;
7569                     }
7570                 }else{
7571                     this.selectFirstRow();
7572                 }
7573                 this.fireEvent("afterselectionchange", this);
7574             },
7575             scope: this
7576         });
7577
7578          
7579         view.on("refresh", this.onRefresh, this);
7580         view.on("rowupdated", this.onRowUpdated, this);
7581         view.on("rowremoved", this.onRemove, this);
7582     },
7583
7584     // private
7585     onRefresh : function(){
7586         var ds = this.grid.ds, i, v = this.grid.view;
7587         var s = this.selections;
7588         s.each(function(r){
7589             if((i = ds.indexOfId(r.id)) != -1){
7590                 v.onRowSelect(i);
7591                 s.add(ds.getAt(i)); // updating the selection relate data
7592             }else{
7593                 s.remove(r);
7594             }
7595         });
7596     },
7597
7598     // private
7599     onRemove : function(v, index, r){
7600         this.selections.remove(r);
7601     },
7602
7603     // private
7604     onRowUpdated : function(v, index, r){
7605         if(this.isSelected(r)){
7606             v.onRowSelect(index);
7607         }
7608     },
7609
7610     /**
7611      * Select records.
7612      * @param {Array} records The records to select
7613      * @param {Boolean} keepExisting (optional) True to keep existing selections
7614      */
7615     selectRecords : function(records, keepExisting){
7616         if(!keepExisting){
7617             this.clearSelections();
7618         }
7619         var ds = this.grid.ds;
7620         for(var i = 0, len = records.length; i < len; i++){
7621             this.selectRow(ds.indexOf(records[i]), true);
7622         }
7623     },
7624
7625     /**
7626      * Gets the number of selected rows.
7627      * @return {Number}
7628      */
7629     getCount : function(){
7630         return this.selections.length;
7631     },
7632
7633     /**
7634      * Selects the first row in the grid.
7635      */
7636     selectFirstRow : function(){
7637         this.selectRow(0);
7638     },
7639
7640     /**
7641      * Select the last row.
7642      * @param {Boolean} keepExisting (optional) True to keep existing selections
7643      */
7644     selectLastRow : function(keepExisting){
7645         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7646     },
7647
7648     /**
7649      * Selects the row immediately following the last selected row.
7650      * @param {Boolean} keepExisting (optional) True to keep existing selections
7651      */
7652     selectNext : function(keepExisting){
7653         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7654             this.selectRow(this.last+1, keepExisting);
7655             var view = this.grid.view ? this.grid.view : this.grid;
7656             view.focusRow(this.last);
7657         }
7658     },
7659
7660     /**
7661      * Selects the row that precedes the last selected row.
7662      * @param {Boolean} keepExisting (optional) True to keep existing selections
7663      */
7664     selectPrevious : function(keepExisting){
7665         if(this.last){
7666             this.selectRow(this.last-1, keepExisting);
7667             var view = this.grid.view ? this.grid.view : this.grid;
7668             view.focusRow(this.last);
7669         }
7670     },
7671
7672     /**
7673      * Returns the selected records
7674      * @return {Array} Array of selected records
7675      */
7676     getSelections : function(){
7677         return [].concat(this.selections.items);
7678     },
7679
7680     /**
7681      * Returns the first selected record.
7682      * @return {Record}
7683      */
7684     getSelected : function(){
7685         return this.selections.itemAt(0);
7686     },
7687
7688
7689     /**
7690      * Clears all selections.
7691      */
7692     clearSelections : function(fast){
7693         if(this.locked) {
7694             return;
7695         }
7696         if(fast !== true){
7697             var ds = this.grid.ds;
7698             var s = this.selections;
7699             s.each(function(r){
7700                 this.deselectRow(ds.indexOfId(r.id));
7701             }, this);
7702             s.clear();
7703         }else{
7704             this.selections.clear();
7705         }
7706         this.last = false;
7707     },
7708
7709
7710     /**
7711      * Selects all rows.
7712      */
7713     selectAll : function(){
7714         if(this.locked) {
7715             return;
7716         }
7717         this.selections.clear();
7718         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7719             this.selectRow(i, true);
7720         }
7721     },
7722
7723     /**
7724      * Returns True if there is a selection.
7725      * @return {Boolean}
7726      */
7727     hasSelection : function(){
7728         return this.selections.length > 0;
7729     },
7730
7731     /**
7732      * Returns True if the specified row is selected.
7733      * @param {Number/Record} record The record or index of the record to check
7734      * @return {Boolean}
7735      */
7736     isSelected : function(index){
7737         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7738         return (r && this.selections.key(r.id) ? true : false);
7739     },
7740
7741     /**
7742      * Returns True if the specified record id is selected.
7743      * @param {String} id The id of record to check
7744      * @return {Boolean}
7745      */
7746     isIdSelected : function(id){
7747         return (this.selections.key(id) ? true : false);
7748     },
7749
7750     // private
7751     handleMouseDown : function(e, t)
7752     {
7753         var view = this.grid.view ? this.grid.view : this.grid;
7754         var rowIndex;
7755         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7756             return;
7757         };
7758         if(e.shiftKey && this.last !== false){
7759             var last = this.last;
7760             this.selectRange(last, rowIndex, e.ctrlKey);
7761             this.last = last; // reset the last
7762             view.focusRow(rowIndex);
7763         }else{
7764             var isSelected = this.isSelected(rowIndex);
7765             if(e.button !== 0 && isSelected){
7766                 view.focusRow(rowIndex);
7767             }else if(e.ctrlKey && isSelected){
7768                 this.deselectRow(rowIndex);
7769             }else if(!isSelected){
7770                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7771                 view.focusRow(rowIndex);
7772             }
7773         }
7774         this.fireEvent("afterselectionchange", this);
7775     },
7776     // private
7777     handleDragableRowClick :  function(grid, rowIndex, e) 
7778     {
7779         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7780             this.selectRow(rowIndex, false);
7781             var view = this.grid.view ? this.grid.view : this.grid;
7782             view.focusRow(rowIndex);
7783              this.fireEvent("afterselectionchange", this);
7784         }
7785     },
7786     
7787     /**
7788      * Selects multiple rows.
7789      * @param {Array} rows Array of the indexes of the row to select
7790      * @param {Boolean} keepExisting (optional) True to keep existing selections
7791      */
7792     selectRows : function(rows, keepExisting){
7793         if(!keepExisting){
7794             this.clearSelections();
7795         }
7796         for(var i = 0, len = rows.length; i < len; i++){
7797             this.selectRow(rows[i], true);
7798         }
7799     },
7800
7801     /**
7802      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7803      * @param {Number} startRow The index of the first row in the range
7804      * @param {Number} endRow The index of the last row in the range
7805      * @param {Boolean} keepExisting (optional) True to retain existing selections
7806      */
7807     selectRange : function(startRow, endRow, keepExisting){
7808         if(this.locked) {
7809             return;
7810         }
7811         if(!keepExisting){
7812             this.clearSelections();
7813         }
7814         if(startRow <= endRow){
7815             for(var i = startRow; i <= endRow; i++){
7816                 this.selectRow(i, true);
7817             }
7818         }else{
7819             for(var i = startRow; i >= endRow; i--){
7820                 this.selectRow(i, true);
7821             }
7822         }
7823     },
7824
7825     /**
7826      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7827      * @param {Number} startRow The index of the first row in the range
7828      * @param {Number} endRow The index of the last row in the range
7829      */
7830     deselectRange : function(startRow, endRow, preventViewNotify){
7831         if(this.locked) {
7832             return;
7833         }
7834         for(var i = startRow; i <= endRow; i++){
7835             this.deselectRow(i, preventViewNotify);
7836         }
7837     },
7838
7839     /**
7840      * Selects a row.
7841      * @param {Number} row The index of the row to select
7842      * @param {Boolean} keepExisting (optional) True to keep existing selections
7843      */
7844     selectRow : function(index, keepExisting, preventViewNotify){
7845         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7846             return;
7847         }
7848         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7849             if(!keepExisting || this.singleSelect){
7850                 this.clearSelections();
7851             }
7852             var r = this.grid.ds.getAt(index);
7853             this.selections.add(r);
7854             this.last = this.lastActive = index;
7855             if(!preventViewNotify){
7856                 var view = this.grid.view ? this.grid.view : this.grid;
7857                 view.onRowSelect(index);
7858             }
7859             this.fireEvent("rowselect", this, index, r);
7860             this.fireEvent("selectionchange", this);
7861         }
7862     },
7863
7864     /**
7865      * Deselects a row.
7866      * @param {Number} row The index of the row to deselect
7867      */
7868     deselectRow : function(index, preventViewNotify){
7869         if(this.locked) {
7870             return;
7871         }
7872         if(this.last == index){
7873             this.last = false;
7874         }
7875         if(this.lastActive == index){
7876             this.lastActive = false;
7877         }
7878         var r = this.grid.ds.getAt(index);
7879         this.selections.remove(r);
7880         if(!preventViewNotify){
7881             var view = this.grid.view ? this.grid.view : this.grid;
7882             view.onRowDeselect(index);
7883         }
7884         this.fireEvent("rowdeselect", this, index);
7885         this.fireEvent("selectionchange", this);
7886     },
7887
7888     // private
7889     restoreLast : function(){
7890         if(this._last){
7891             this.last = this._last;
7892         }
7893     },
7894
7895     // private
7896     acceptsNav : function(row, col, cm){
7897         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7898     },
7899
7900     // private
7901     onEditorKey : function(field, e){
7902         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7903         if(k == e.TAB){
7904             e.stopEvent();
7905             ed.completeEdit();
7906             if(e.shiftKey){
7907                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7908             }else{
7909                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7910             }
7911         }else if(k == e.ENTER && !e.ctrlKey){
7912             e.stopEvent();
7913             ed.completeEdit();
7914             if(e.shiftKey){
7915                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7916             }else{
7917                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7918             }
7919         }else if(k == e.ESC){
7920             ed.cancelEdit();
7921         }
7922         if(newCell){
7923             g.startEditing(newCell[0], newCell[1]);
7924         }
7925     }
7926 });/*
7927  * Based on:
7928  * Ext JS Library 1.1.1
7929  * Copyright(c) 2006-2007, Ext JS, LLC.
7930  *
7931  * Originally Released Under LGPL - original licence link has changed is not relivant.
7932  *
7933  * Fork - LGPL
7934  * <script type="text/javascript">
7935  */
7936  
7937
7938 /**
7939  * @class Roo.grid.ColumnModel
7940  * @extends Roo.util.Observable
7941  * This is the default implementation of a ColumnModel used by the Grid. It defines
7942  * the columns in the grid.
7943  * <br>Usage:<br>
7944  <pre><code>
7945  var colModel = new Roo.grid.ColumnModel([
7946         {header: "Ticker", width: 60, sortable: true, locked: true},
7947         {header: "Company Name", width: 150, sortable: true},
7948         {header: "Market Cap.", width: 100, sortable: true},
7949         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7950         {header: "Employees", width: 100, sortable: true, resizable: false}
7951  ]);
7952  </code></pre>
7953  * <p>
7954  
7955  * The config options listed for this class are options which may appear in each
7956  * individual column definition.
7957  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7958  * @constructor
7959  * @param {Object} config An Array of column config objects. See this class's
7960  * config objects for details.
7961 */
7962 Roo.grid.ColumnModel = function(config){
7963         /**
7964      * The config passed into the constructor
7965      */
7966     this.config = []; //config;
7967     this.lookup = {};
7968
7969     // if no id, create one
7970     // if the column does not have a dataIndex mapping,
7971     // map it to the order it is in the config
7972     for(var i = 0, len = config.length; i < len; i++){
7973         this.addColumn(config[i]);
7974         
7975     }
7976
7977     /**
7978      * The width of columns which have no width specified (defaults to 100)
7979      * @type Number
7980      */
7981     this.defaultWidth = 100;
7982
7983     /**
7984      * Default sortable of columns which have no sortable specified (defaults to false)
7985      * @type Boolean
7986      */
7987     this.defaultSortable = false;
7988
7989     this.addEvents({
7990         /**
7991              * @event widthchange
7992              * Fires when the width of a column changes.
7993              * @param {ColumnModel} this
7994              * @param {Number} columnIndex The column index
7995              * @param {Number} newWidth The new width
7996              */
7997             "widthchange": true,
7998         /**
7999              * @event headerchange
8000              * Fires when the text of a header changes.
8001              * @param {ColumnModel} this
8002              * @param {Number} columnIndex The column index
8003              * @param {Number} newText The new header text
8004              */
8005             "headerchange": true,
8006         /**
8007              * @event hiddenchange
8008              * Fires when a column is hidden or "unhidden".
8009              * @param {ColumnModel} this
8010              * @param {Number} columnIndex The column index
8011              * @param {Boolean} hidden true if hidden, false otherwise
8012              */
8013             "hiddenchange": true,
8014             /**
8015          * @event columnmoved
8016          * Fires when a column is moved.
8017          * @param {ColumnModel} this
8018          * @param {Number} oldIndex
8019          * @param {Number} newIndex
8020          */
8021         "columnmoved" : true,
8022         /**
8023          * @event columlockchange
8024          * Fires when a column's locked state is changed
8025          * @param {ColumnModel} this
8026          * @param {Number} colIndex
8027          * @param {Boolean} locked true if locked
8028          */
8029         "columnlockchange" : true
8030     });
8031     Roo.grid.ColumnModel.superclass.constructor.call(this);
8032 };
8033 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
8034     /**
8035      * @cfg {String} header The header text to display in the Grid view.
8036      */
8037         /**
8038      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
8039      */
8040         /**
8041      * @cfg {String} smHeader Header at Bootsrap Small width
8042      */
8043         /**
8044      * @cfg {String} mdHeader Header at Bootsrap Medium width
8045      */
8046         /**
8047      * @cfg {String} lgHeader Header at Bootsrap Large width
8048      */
8049         /**
8050      * @cfg {String} xlHeader Header at Bootsrap extra Large width
8051      */
8052     /**
8053      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
8054      * {@link Roo.data.Record} definition from which to draw the column's value. If not
8055      * specified, the column's index is used as an index into the Record's data Array.
8056      */
8057     /**
8058      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
8059      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
8060      */
8061     /**
8062      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
8063      * Defaults to the value of the {@link #defaultSortable} property.
8064      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
8065      */
8066     /**
8067      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
8068      */
8069     /**
8070      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
8071      */
8072     /**
8073      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
8074      */
8075     /**
8076      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
8077      */
8078     /**
8079      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
8080      * given the cell's data value. See {@link #setRenderer}. If not specified, the
8081      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
8082      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
8083      */
8084        /**
8085      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
8086      */
8087     /**
8088      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
8089      */
8090     /**
8091      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
8092      */
8093     /**
8094      * @cfg {String} cursor (Optional)
8095      */
8096     /**
8097      * @cfg {String} tooltip (Optional)
8098      */
8099     /**
8100      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
8101      */
8102     /**
8103      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
8104      */
8105     /**
8106      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
8107      */
8108     /**
8109      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
8110      */
8111         /**
8112      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
8113      */
8114     /**
8115      * Returns the id of the column at the specified index.
8116      * @param {Number} index The column index
8117      * @return {String} the id
8118      */
8119     getColumnId : function(index){
8120         return this.config[index].id;
8121     },
8122
8123     /**
8124      * Returns the column for a specified id.
8125      * @param {String} id The column id
8126      * @return {Object} the column
8127      */
8128     getColumnById : function(id){
8129         return this.lookup[id];
8130     },
8131
8132     
8133     /**
8134      * Returns the column Object for a specified dataIndex.
8135      * @param {String} dataIndex The column dataIndex
8136      * @return {Object|Boolean} the column or false if not found
8137      */
8138     getColumnByDataIndex: function(dataIndex){
8139         var index = this.findColumnIndex(dataIndex);
8140         return index > -1 ? this.config[index] : false;
8141     },
8142     
8143     /**
8144      * Returns the index for a specified column id.
8145      * @param {String} id The column id
8146      * @return {Number} the index, or -1 if not found
8147      */
8148     getIndexById : function(id){
8149         for(var i = 0, len = this.config.length; i < len; i++){
8150             if(this.config[i].id == id){
8151                 return i;
8152             }
8153         }
8154         return -1;
8155     },
8156     
8157     /**
8158      * Returns the index for a specified column dataIndex.
8159      * @param {String} dataIndex The column dataIndex
8160      * @return {Number} the index, or -1 if not found
8161      */
8162     
8163     findColumnIndex : function(dataIndex){
8164         for(var i = 0, len = this.config.length; i < len; i++){
8165             if(this.config[i].dataIndex == dataIndex){
8166                 return i;
8167             }
8168         }
8169         return -1;
8170     },
8171     
8172     
8173     moveColumn : function(oldIndex, newIndex){
8174         var c = this.config[oldIndex];
8175         this.config.splice(oldIndex, 1);
8176         this.config.splice(newIndex, 0, c);
8177         this.dataMap = null;
8178         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8179     },
8180
8181     isLocked : function(colIndex){
8182         return this.config[colIndex].locked === true;
8183     },
8184
8185     setLocked : function(colIndex, value, suppressEvent){
8186         if(this.isLocked(colIndex) == value){
8187             return;
8188         }
8189         this.config[colIndex].locked = value;
8190         if(!suppressEvent){
8191             this.fireEvent("columnlockchange", this, colIndex, value);
8192         }
8193     },
8194
8195     getTotalLockedWidth : function(){
8196         var totalWidth = 0;
8197         for(var i = 0; i < this.config.length; i++){
8198             if(this.isLocked(i) && !this.isHidden(i)){
8199                 this.totalWidth += this.getColumnWidth(i);
8200             }
8201         }
8202         return totalWidth;
8203     },
8204
8205     getLockedCount : function(){
8206         for(var i = 0, len = this.config.length; i < len; i++){
8207             if(!this.isLocked(i)){
8208                 return i;
8209             }
8210         }
8211         
8212         return this.config.length;
8213     },
8214
8215     /**
8216      * Returns the number of columns.
8217      * @return {Number}
8218      */
8219     getColumnCount : function(visibleOnly){
8220         if(visibleOnly === true){
8221             var c = 0;
8222             for(var i = 0, len = this.config.length; i < len; i++){
8223                 if(!this.isHidden(i)){
8224                     c++;
8225                 }
8226             }
8227             return c;
8228         }
8229         return this.config.length;
8230     },
8231
8232     /**
8233      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8234      * @param {Function} fn
8235      * @param {Object} scope (optional)
8236      * @return {Array} result
8237      */
8238     getColumnsBy : function(fn, scope){
8239         var r = [];
8240         for(var i = 0, len = this.config.length; i < len; i++){
8241             var c = this.config[i];
8242             if(fn.call(scope||this, c, i) === true){
8243                 r[r.length] = c;
8244             }
8245         }
8246         return r;
8247     },
8248
8249     /**
8250      * Returns true if the specified column is sortable.
8251      * @param {Number} col The column index
8252      * @return {Boolean}
8253      */
8254     isSortable : function(col){
8255         if(typeof this.config[col].sortable == "undefined"){
8256             return this.defaultSortable;
8257         }
8258         return this.config[col].sortable;
8259     },
8260
8261     /**
8262      * Returns the rendering (formatting) function defined for the column.
8263      * @param {Number} col The column index.
8264      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8265      */
8266     getRenderer : function(col){
8267         if(!this.config[col].renderer){
8268             return Roo.grid.ColumnModel.defaultRenderer;
8269         }
8270         return this.config[col].renderer;
8271     },
8272
8273     /**
8274      * Sets the rendering (formatting) function for a column.
8275      * @param {Number} col The column index
8276      * @param {Function} fn The function to use to process the cell's raw data
8277      * to return HTML markup for the grid view. The render function is called with
8278      * the following parameters:<ul>
8279      * <li>Data value.</li>
8280      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8281      * <li>css A CSS style string to apply to the table cell.</li>
8282      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8283      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8284      * <li>Row index</li>
8285      * <li>Column index</li>
8286      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8287      */
8288     setRenderer : function(col, fn){
8289         this.config[col].renderer = fn;
8290     },
8291
8292     /**
8293      * Returns the width for the specified column.
8294      * @param {Number} col The column index
8295      * @param (optional) {String} gridSize bootstrap width size.
8296      * @return {Number}
8297      */
8298     getColumnWidth : function(col, gridSize)
8299         {
8300                 var cfg = this.config[col];
8301                 
8302                 if (typeof(gridSize) == 'undefined') {
8303                         return cfg.width * 1 || this.defaultWidth;
8304                 }
8305                 if (gridSize === false) { // if we set it..
8306                         return cfg.width || false;
8307                 }
8308                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
8309                 
8310                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
8311                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
8312                                 continue;
8313                         }
8314                         return cfg[ sizes[i] ];
8315                 }
8316                 return 1;
8317                 
8318     },
8319
8320     /**
8321      * Sets the width for a column.
8322      * @param {Number} col The column index
8323      * @param {Number} width The new width
8324      */
8325     setColumnWidth : function(col, width, suppressEvent){
8326         this.config[col].width = width;
8327         this.totalWidth = null;
8328         if(!suppressEvent){
8329              this.fireEvent("widthchange", this, col, width);
8330         }
8331     },
8332
8333     /**
8334      * Returns the total width of all columns.
8335      * @param {Boolean} includeHidden True to include hidden column widths
8336      * @return {Number}
8337      */
8338     getTotalWidth : function(includeHidden){
8339         if(!this.totalWidth){
8340             this.totalWidth = 0;
8341             for(var i = 0, len = this.config.length; i < len; i++){
8342                 if(includeHidden || !this.isHidden(i)){
8343                     this.totalWidth += this.getColumnWidth(i);
8344                 }
8345             }
8346         }
8347         return this.totalWidth;
8348     },
8349
8350     /**
8351      * Returns the header for the specified column.
8352      * @param {Number} col The column index
8353      * @return {String}
8354      */
8355     getColumnHeader : function(col){
8356         return this.config[col].header;
8357     },
8358
8359     /**
8360      * Sets the header for a column.
8361      * @param {Number} col The column index
8362      * @param {String} header The new header
8363      */
8364     setColumnHeader : function(col, header){
8365         this.config[col].header = header;
8366         this.fireEvent("headerchange", this, col, header);
8367     },
8368
8369     /**
8370      * Returns the tooltip for the specified column.
8371      * @param {Number} col The column index
8372      * @return {String}
8373      */
8374     getColumnTooltip : function(col){
8375             return this.config[col].tooltip;
8376     },
8377     /**
8378      * Sets the tooltip for a column.
8379      * @param {Number} col The column index
8380      * @param {String} tooltip The new tooltip
8381      */
8382     setColumnTooltip : function(col, tooltip){
8383             this.config[col].tooltip = tooltip;
8384     },
8385
8386     /**
8387      * Returns the dataIndex for the specified column.
8388      * @param {Number} col The column index
8389      * @return {Number}
8390      */
8391     getDataIndex : function(col){
8392         return this.config[col].dataIndex;
8393     },
8394
8395     /**
8396      * Sets the dataIndex for a column.
8397      * @param {Number} col The column index
8398      * @param {Number} dataIndex The new dataIndex
8399      */
8400     setDataIndex : function(col, dataIndex){
8401         this.config[col].dataIndex = dataIndex;
8402     },
8403
8404     
8405     
8406     /**
8407      * Returns true if the cell is editable.
8408      * @param {Number} colIndex The column index
8409      * @param {Number} rowIndex The row index - this is nto actually used..?
8410      * @return {Boolean}
8411      */
8412     isCellEditable : function(colIndex, rowIndex){
8413         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8414     },
8415
8416     /**
8417      * Returns the editor defined for the cell/column.
8418      * return false or null to disable editing.
8419      * @param {Number} colIndex The column index
8420      * @param {Number} rowIndex The row index
8421      * @return {Object}
8422      */
8423     getCellEditor : function(colIndex, rowIndex){
8424         return this.config[colIndex].editor;
8425     },
8426
8427     /**
8428      * Sets if a column is editable.
8429      * @param {Number} col The column index
8430      * @param {Boolean} editable True if the column is editable
8431      */
8432     setEditable : function(col, editable){
8433         this.config[col].editable = editable;
8434     },
8435
8436
8437     /**
8438      * Returns true if the column is hidden.
8439      * @param {Number} colIndex The column index
8440      * @return {Boolean}
8441      */
8442     isHidden : function(colIndex){
8443         return this.config[colIndex].hidden;
8444     },
8445
8446
8447     /**
8448      * Returns true if the column width cannot be changed
8449      */
8450     isFixed : function(colIndex){
8451         return this.config[colIndex].fixed;
8452     },
8453
8454     /**
8455      * Returns true if the column can be resized
8456      * @return {Boolean}
8457      */
8458     isResizable : function(colIndex){
8459         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8460     },
8461     /**
8462      * Sets if a column is hidden.
8463      * @param {Number} colIndex The column index
8464      * @param {Boolean} hidden True if the column is hidden
8465      */
8466     setHidden : function(colIndex, hidden){
8467         this.config[colIndex].hidden = hidden;
8468         this.totalWidth = null;
8469         this.fireEvent("hiddenchange", this, colIndex, hidden);
8470     },
8471
8472     /**
8473      * Sets the editor for a column.
8474      * @param {Number} col The column index
8475      * @param {Object} editor The editor object
8476      */
8477     setEditor : function(col, editor){
8478         this.config[col].editor = editor;
8479     },
8480     /**
8481      * Add a column (experimental...) - defaults to adding to the end..
8482      * @param {Object} config 
8483     */
8484     addColumn : function(c)
8485     {
8486     
8487         var i = this.config.length;
8488         this.config[i] = c;
8489         
8490         if(typeof c.dataIndex == "undefined"){
8491             c.dataIndex = i;
8492         }
8493         if(typeof c.renderer == "string"){
8494             c.renderer = Roo.util.Format[c.renderer];
8495         }
8496         if(typeof c.id == "undefined"){
8497             c.id = Roo.id();
8498         }
8499         if(c.editor && c.editor.xtype){
8500             c.editor  = Roo.factory(c.editor, Roo.grid);
8501         }
8502         if(c.editor && c.editor.isFormField){
8503             c.editor = new Roo.grid.GridEditor(c.editor);
8504         }
8505         this.lookup[c.id] = c;
8506     }
8507     
8508 });
8509
8510 Roo.grid.ColumnModel.defaultRenderer = function(value)
8511 {
8512     if(typeof value == "object") {
8513         return value;
8514     }
8515         if(typeof value == "string" && value.length < 1){
8516             return "&#160;";
8517         }
8518     
8519         return String.format("{0}", value);
8520 };
8521
8522 // Alias for backwards compatibility
8523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8524 /*
8525  * Based on:
8526  * Ext JS Library 1.1.1
8527  * Copyright(c) 2006-2007, Ext JS, LLC.
8528  *
8529  * Originally Released Under LGPL - original licence link has changed is not relivant.
8530  *
8531  * Fork - LGPL
8532  * <script type="text/javascript">
8533  */
8534  
8535 /**
8536  * @class Roo.LoadMask
8537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8540  * element's UpdateManager load indicator and will be destroyed after the initial load.
8541  * @constructor
8542  * Create a new LoadMask
8543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8544  * @param {Object} config The config object
8545  */
8546 Roo.LoadMask = function(el, config){
8547     this.el = Roo.get(el);
8548     Roo.apply(this, config);
8549     if(this.store){
8550         this.store.on('beforeload', this.onBeforeLoad, this);
8551         this.store.on('load', this.onLoad, this);
8552         this.store.on('loadexception', this.onLoadException, this);
8553         this.removeMask = false;
8554     }else{
8555         var um = this.el.getUpdateManager();
8556         um.showLoadIndicator = false; // disable the default indicator
8557         um.on('beforeupdate', this.onBeforeLoad, this);
8558         um.on('update', this.onLoad, this);
8559         um.on('failure', this.onLoad, this);
8560         this.removeMask = true;
8561     }
8562 };
8563
8564 Roo.LoadMask.prototype = {
8565     /**
8566      * @cfg {Boolean} removeMask
8567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8569      */
8570     removeMask : false,
8571     /**
8572      * @cfg {String} msg
8573      * The text to display in a centered loading message box (defaults to 'Loading...')
8574      */
8575     msg : 'Loading...',
8576     /**
8577      * @cfg {String} msgCls
8578      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8579      */
8580     msgCls : 'x-mask-loading',
8581
8582     /**
8583      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8584      * @type Boolean
8585      */
8586     disabled: false,
8587
8588     /**
8589      * Disables the mask to prevent it from being displayed
8590      */
8591     disable : function(){
8592        this.disabled = true;
8593     },
8594
8595     /**
8596      * Enables the mask so that it can be displayed
8597      */
8598     enable : function(){
8599         this.disabled = false;
8600     },
8601     
8602     onLoadException : function()
8603     {
8604         Roo.log(arguments);
8605         
8606         if (typeof(arguments[3]) != 'undefined') {
8607             Roo.MessageBox.alert("Error loading",arguments[3]);
8608         } 
8609         /*
8610         try {
8611             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8612                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8613             }   
8614         } catch(e) {
8615             
8616         }
8617         */
8618     
8619         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8620     },
8621     // private
8622     onLoad : function()
8623     {
8624         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8625     },
8626
8627     // private
8628     onBeforeLoad : function(){
8629         if(!this.disabled){
8630             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8631         }
8632     },
8633
8634     // private
8635     destroy : function(){
8636         if(this.store){
8637             this.store.un('beforeload', this.onBeforeLoad, this);
8638             this.store.un('load', this.onLoad, this);
8639             this.store.un('loadexception', this.onLoadException, this);
8640         }else{
8641             var um = this.el.getUpdateManager();
8642             um.un('beforeupdate', this.onBeforeLoad, this);
8643             um.un('update', this.onLoad, this);
8644             um.un('failure', this.onLoad, this);
8645         }
8646     }
8647 };/**
8648  * @class Roo.bootstrap.Table
8649  * @licence LGBL
8650  * @extends Roo.bootstrap.Component
8651  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8652  * Similar to Roo.grid.Grid
8653  * <pre><code>
8654  var table = Roo.factory({
8655     xtype : 'Table',
8656     xns : Roo.bootstrap,
8657     autoSizeColumns: true,
8658     
8659     
8660     store : {
8661         xtype : 'Store',
8662         xns : Roo.data,
8663         remoteSort : true,
8664         sortInfo : { direction : 'ASC', field: 'name' },
8665         proxy : {
8666            xtype : 'HttpProxy',
8667            xns : Roo.data,
8668            method : 'GET',
8669            url : 'https://example.com/some.data.url.json'
8670         },
8671         reader : {
8672            xtype : 'JsonReader',
8673            xns : Roo.data,
8674            fields : [ 'id', 'name', whatever' ],
8675            id : 'id',
8676            root : 'data'
8677         }
8678     },
8679     cm : [
8680         {
8681             xtype : 'ColumnModel',
8682             xns : Roo.grid,
8683             align : 'center',
8684             cursor : 'pointer',
8685             dataIndex : 'is_in_group',
8686             header : "Name",
8687             sortable : true,
8688             renderer : function(v, x , r) {  
8689             
8690                 return String.format("{0}", v)
8691             }
8692             width : 3
8693         } // more columns..
8694     ],
8695     selModel : {
8696         xtype : 'RowSelectionModel',
8697         xns : Roo.bootstrap.Table
8698         // you can add listeners to catch selection change here....
8699     }
8700      
8701
8702  });
8703  // set any options
8704  grid.render(Roo.get("some-div"));
8705 </code></pre>
8706
8707 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8708
8709
8710
8711  *
8712  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8713  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8714  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8715  * 
8716  * @cfg {String} cls table class
8717  *
8718  * 
8719  * @cfg {boolean} striped Should the rows be alternative striped
8720  * @cfg {boolean} bordered Add borders to the table
8721  * @cfg {boolean} hover Add hover highlighting
8722  * @cfg {boolean} condensed Format condensed
8723  * @cfg {boolean} responsive default false - if this is on, columns are rendered with col-xs-4 etc. classes, otherwise columns will be sized by CSS,
8724  *                also adds table-responsive (see bootstrap docs for details)
8725  * @cfg {Boolean} loadMask (true|false) default false
8726  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8727  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8728  * @cfg {Boolean} rowSelection (true|false) default false
8729  * @cfg {Boolean} cellSelection (true|false) default false
8730  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8731  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8732  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8733  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8734  * @cfg {Boolean} enableColumnResize default true if columns can be resized (drag/drop)
8735  * @cfg {Number} minColumnWidth default 50 pixels minimum column width 
8736  * 
8737  * @constructor
8738  * Create a new Table
8739  * @param {Object} config The config object
8740  */
8741
8742 Roo.bootstrap.Table = function(config)
8743 {
8744     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8745      
8746     // BC...
8747     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8748     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8749     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8750     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8751     
8752     this.view = this; // compat with grid.
8753     
8754     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8755     if (this.sm) {
8756         this.sm.grid = this;
8757         this.selModel = Roo.factory(this.sm, Roo.grid);
8758         this.sm = this.selModel;
8759         this.sm.xmodule = this.xmodule || false;
8760     }
8761     
8762     if (this.cm && typeof(this.cm.config) == 'undefined') {
8763         this.colModel = new Roo.grid.ColumnModel(this.cm);
8764         this.cm = this.colModel;
8765         this.cm.xmodule = this.xmodule || false;
8766     }
8767     if (this.store) {
8768         this.store= Roo.factory(this.store, Roo.data);
8769         this.ds = this.store;
8770         this.ds.xmodule = this.xmodule || false;
8771          
8772     }
8773     if (this.footer && this.store) {
8774         this.footer.dataSource = this.ds;
8775         this.footer = Roo.factory(this.footer);
8776     }
8777     
8778     /** @private */
8779     this.addEvents({
8780         /**
8781          * @event cellclick
8782          * Fires when a cell is clicked
8783          * @param {Roo.bootstrap.Table} this
8784          * @param {Roo.Element} el
8785          * @param {Number} rowIndex
8786          * @param {Number} columnIndex
8787          * @param {Roo.EventObject} e
8788          */
8789         "cellclick" : true,
8790         /**
8791          * @event celldblclick
8792          * Fires when a cell is double clicked
8793          * @param {Roo.bootstrap.Table} this
8794          * @param {Roo.Element} el
8795          * @param {Number} rowIndex
8796          * @param {Number} columnIndex
8797          * @param {Roo.EventObject} e
8798          */
8799         "celldblclick" : true,
8800         /**
8801          * @event rowclick
8802          * Fires when a row is clicked
8803          * @param {Roo.bootstrap.Table} this
8804          * @param {Roo.Element} el
8805          * @param {Number} rowIndex
8806          * @param {Roo.EventObject} e
8807          */
8808         "rowclick" : true,
8809         /**
8810          * @event rowdblclick
8811          * Fires when a row is double clicked
8812          * @param {Roo.bootstrap.Table} this
8813          * @param {Roo.Element} el
8814          * @param {Number} rowIndex
8815          * @param {Roo.EventObject} e
8816          */
8817         "rowdblclick" : true,
8818         /**
8819          * @event mouseover
8820          * Fires when a mouseover occur
8821          * @param {Roo.bootstrap.Table} this
8822          * @param {Roo.Element} el
8823          * @param {Number} rowIndex
8824          * @param {Number} columnIndex
8825          * @param {Roo.EventObject} e
8826          */
8827         "mouseover" : true,
8828         /**
8829          * @event mouseout
8830          * Fires when a mouseout occur
8831          * @param {Roo.bootstrap.Table} this
8832          * @param {Roo.Element} el
8833          * @param {Number} rowIndex
8834          * @param {Number} columnIndex
8835          * @param {Roo.EventObject} e
8836          */
8837         "mouseout" : true,
8838         /**
8839          * @event rowclass
8840          * Fires when a row is rendered, so you can change add a style to it.
8841          * @param {Roo.bootstrap.Table} this
8842          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8843          */
8844         'rowclass' : true,
8845           /**
8846          * @event rowsrendered
8847          * Fires when all the  rows have been rendered
8848          * @param {Roo.bootstrap.Table} this
8849          */
8850         'rowsrendered' : true,
8851         /**
8852          * @event contextmenu
8853          * The raw contextmenu event for the entire grid.
8854          * @param {Roo.EventObject} e
8855          */
8856         "contextmenu" : true,
8857         /**
8858          * @event rowcontextmenu
8859          * Fires when a row is right clicked
8860          * @param {Roo.bootstrap.Table} this
8861          * @param {Number} rowIndex
8862          * @param {Roo.EventObject} e
8863          */
8864         "rowcontextmenu" : true,
8865         /**
8866          * @event cellcontextmenu
8867          * Fires when a cell is right clicked
8868          * @param {Roo.bootstrap.Table} this
8869          * @param {Number} rowIndex
8870          * @param {Number} cellIndex
8871          * @param {Roo.EventObject} e
8872          */
8873          "cellcontextmenu" : true,
8874          /**
8875          * @event headercontextmenu
8876          * Fires when a header is right clicked
8877          * @param {Roo.bootstrap.Table} this
8878          * @param {Number} columnIndex
8879          * @param {Roo.EventObject} e
8880          */
8881         "headercontextmenu" : true,
8882         /**
8883          * @event mousedown
8884          * The raw mousedown event for the entire grid.
8885          * @param {Roo.EventObject} e
8886          */
8887         "mousedown" : true
8888         
8889     });
8890 };
8891
8892 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8893     
8894     cls: false,
8895     
8896     striped : false,
8897     scrollBody : false,
8898     bordered: false,
8899     hover:  false,
8900     condensed : false,
8901     responsive : false,
8902     sm : false,
8903     cm : false,
8904     store : false,
8905     loadMask : false,
8906     footerShow : true,
8907     headerShow : true,
8908     enableColumnResize: true,
8909   
8910     rowSelection : false,
8911     cellSelection : false,
8912     layout : false,
8913
8914     minColumnWidth : 50,
8915     
8916     // Roo.Element - the tbody
8917     bodyEl: false,  // <tbody> Roo.Element - thead element    
8918     headEl: false,  // <thead> Roo.Element - thead element
8919     resizeProxy : false, // proxy element for dragging?
8920
8921
8922     
8923     container: false, // used by gridpanel...
8924     
8925     lazyLoad : false,
8926     
8927     CSS : Roo.util.CSS,
8928     
8929     auto_hide_footer : false,
8930     
8931     view: false, // actually points to this..
8932     
8933     getAutoCreate : function()
8934     {
8935         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8936         
8937         cfg = {
8938             tag: 'table',
8939             cls : 'table', 
8940             cn : []
8941         };
8942         // this get's auto added by panel.Grid
8943         if (this.scrollBody) {
8944             cfg.cls += ' table-body-fixed';
8945         }    
8946         if (this.striped) {
8947             cfg.cls += ' table-striped';
8948         }
8949         
8950         if (this.hover) {
8951             cfg.cls += ' table-hover';
8952         }
8953         if (this.bordered) {
8954             cfg.cls += ' table-bordered';
8955         }
8956         if (this.condensed) {
8957             cfg.cls += ' table-condensed';
8958         }
8959         
8960         if (this.responsive) {
8961             cfg.cls += ' table-responsive';
8962         }
8963         
8964         if (this.cls) {
8965             cfg.cls+=  ' ' +this.cls;
8966         }
8967         
8968         
8969         
8970         if (this.layout) {
8971             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8972         }
8973         
8974         if(this.store || this.cm){
8975             if(this.headerShow){
8976                 cfg.cn.push(this.renderHeader());
8977             }
8978             
8979             cfg.cn.push(this.renderBody());
8980             
8981             if(this.footerShow){
8982                 cfg.cn.push(this.renderFooter());
8983             }
8984             // where does this come from?
8985             //cfg.cls+=  ' TableGrid';
8986         }
8987         
8988         return { cn : [ cfg ] };
8989     },
8990     
8991     initEvents : function()
8992     {   
8993         if(!this.store || !this.cm){
8994             return;
8995         }
8996         if (this.selModel) {
8997             this.selModel.initEvents();
8998         }
8999         
9000         
9001         //Roo.log('initEvents with ds!!!!');
9002         
9003         this.bodyEl = this.el.select('tbody', true).first();
9004         this.headEl = this.el.select('thead', true).first();
9005         this.mainFoot = this.el.select('tfoot', true).first();
9006         
9007         
9008         
9009         
9010         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9011             e.on('click', this.sort, this);
9012         }, this);
9013         
9014         
9015         // why is this done????? = it breaks dialogs??
9016         //this.parent().el.setStyle('position', 'relative');
9017         
9018         
9019         if (this.footer) {
9020             this.footer.parentId = this.id;
9021             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
9022             
9023             if(this.lazyLoad){
9024                 this.el.select('tfoot tr td').first().addClass('hide');
9025             }
9026         } 
9027         
9028         if(this.loadMask) {
9029             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
9030         }
9031         
9032         this.store.on('load', this.onLoad, this);
9033         this.store.on('beforeload', this.onBeforeLoad, this);
9034         this.store.on('update', this.onUpdate, this);
9035         this.store.on('add', this.onAdd, this);
9036         this.store.on("clear", this.clear, this);
9037         
9038         this.el.on("contextmenu", this.onContextMenu, this);
9039         
9040         
9041         this.cm.on("headerchange", this.onHeaderChange, this);
9042         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
9043
9044  //?? does bodyEl get replaced on render?
9045         this.bodyEl.on("click", this.onClick, this);
9046         this.bodyEl.on("dblclick", this.onDblClick, this);        
9047         this.bodyEl.on('scroll', this.onBodyScroll, this);
9048
9049         // guessing mainbody will work - this relays usually caught by selmodel at present.
9050         this.relayEvents(this.bodyEl, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
9051   
9052   
9053         this.resizeProxy = Roo.get(document.body).createChild({ cls:"x-grid-resize-proxy", html: '&#160;' });
9054         
9055   
9056         if(this.headEl && this.enableColumnResize !== false && Roo.grid.SplitDragZone){
9057             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
9058         }
9059         
9060         this.initCSS();
9061     },
9062     // Compatibility with grid - we implement all the view features at present.
9063     getView : function()
9064     {
9065         return this;
9066     },
9067     
9068     initCSS : function()
9069     {
9070         
9071         
9072         var cm = this.cm, styles = [];
9073         this.CSS.removeStyleSheet(this.id + '-cssrules');
9074         var headHeight = this.headEl ? this.headEl.dom.clientHeight : 0;
9075         // we can honour xs/sm/md/xl  as widths...
9076         // we first have to decide what widht we are currently at...
9077         var sz = Roo.getGridSize();
9078         
9079         var total = 0;
9080         var last = -1;
9081         var cols = []; // visable cols.
9082         var total_abs = 0;
9083         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9084             var w = cm.getColumnWidth(i, false);
9085             if(cm.isHidden(i)){
9086                 cols.push( { rel : false, abs : 0 });
9087                 continue;
9088             }
9089             if (w !== false) {
9090                 cols.push( { rel : false, abs : w });
9091                 total_abs += w;
9092                 last = i; // not really..
9093                 continue;
9094             }
9095             var w = cm.getColumnWidth(i, sz);
9096             if (w > 0) {
9097                 last = i
9098             }
9099             total += w;
9100             cols.push( { rel : w, abs : false });
9101         }
9102         
9103         var avail = this.bodyEl.dom.clientWidth - total_abs;
9104         
9105         var unitWidth = Math.floor(avail / total);
9106         var rem = avail - (unitWidth * total);
9107         
9108         var hidden, width, pos = 0 , splithide , left;
9109         for(var i = 0, len = cm.getColumnCount(); i < len; i++) {
9110             
9111             hidden = 'display:none;';
9112             left = '';
9113             width  = 'width:0px;';
9114             splithide = '';
9115             if(!cm.isHidden(i)){
9116                 hidden = '';
9117                 
9118                 
9119                 // we can honour xs/sm/md/xl ?
9120                 var w = cols[i].rel == false ? cols[i].abs : (cols[i].rel * unitWidth);
9121                 if (w===0) {
9122                     hidden = 'display:none;';
9123                 }
9124                 // width should return a small number...
9125                 if (i == last) {
9126                     w+=rem; // add the remaining with..
9127                 }
9128                 pos += w;
9129                 left = "left:" + (pos -4) + "px;";
9130                 width = "width:" + w+ "px;";
9131                 
9132             }
9133             if (this.responsive) {
9134                 width = '';
9135                 left = '';
9136                 hidden = cm.isHidden(i) ? 'display:none;' : '';
9137                 splithide = 'display: none;';
9138             }
9139             
9140             styles.push( '#' , this.id , ' .x-col-' , i, " {", cm.config[i].css, width, hidden, "}\n" );
9141             if (this.headEl) {
9142                 if (i == last) {
9143                     splithide = 'display:none;';
9144                 }
9145                 
9146                 styles.push('#' , this.id , ' .x-hcol-' , i, " { ", width, hidden," }\n",
9147                             '#' , this.id , ' .x-grid-split-' , i, " { ", left, splithide,'height:', (headHeight - 4), "px;}\n"
9148                 );
9149             }
9150             
9151         }
9152         //Roo.log(styles.join(''));
9153         this.CSS.createStyleSheet( styles.join(''), this.id + '-cssrules');
9154         
9155     },
9156     
9157     
9158     
9159     onContextMenu : function(e, t)
9160     {
9161         this.processEvent("contextmenu", e);
9162     },
9163     
9164     processEvent : function(name, e)
9165     {
9166         if (name != 'touchstart' ) {
9167             this.fireEvent(name, e);    
9168         }
9169         
9170         var t = e.getTarget();
9171         
9172         var cell = Roo.get(t);
9173         
9174         if(!cell){
9175             return;
9176         }
9177         
9178         if(cell.findParent('tfoot', false, true)){
9179             return;
9180         }
9181         
9182         if(cell.findParent('thead', false, true)){
9183             
9184             if(e.getTarget().nodeName.toLowerCase() != 'th'){
9185                 cell = Roo.get(t).findParent('th', false, true);
9186                 if (!cell) {
9187                     Roo.log("failed to find th in thead?");
9188                     Roo.log(e.getTarget());
9189                     return;
9190                 }
9191             }
9192             
9193             var cellIndex = cell.dom.cellIndex;
9194             
9195             var ename = name == 'touchstart' ? 'click' : name;
9196             this.fireEvent("header" + ename, this, cellIndex, e);
9197             
9198             return;
9199         }
9200         
9201         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9202             cell = Roo.get(t).findParent('td', false, true);
9203             if (!cell) {
9204                 Roo.log("failed to find th in tbody?");
9205                 Roo.log(e.getTarget());
9206                 return;
9207             }
9208         }
9209         
9210         var row = cell.findParent('tr', false, true);
9211         var cellIndex = cell.dom.cellIndex;
9212         var rowIndex = row.dom.rowIndex - 1;
9213         
9214         if(row !== false){
9215             
9216             this.fireEvent("row" + name, this, rowIndex, e);
9217             
9218             if(cell !== false){
9219             
9220                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
9221             }
9222         }
9223         
9224     },
9225     
9226     onMouseover : function(e, el)
9227     {
9228         var cell = Roo.get(el);
9229         
9230         if(!cell){
9231             return;
9232         }
9233         
9234         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9235             cell = cell.findParent('td', false, true);
9236         }
9237         
9238         var row = cell.findParent('tr', false, true);
9239         var cellIndex = cell.dom.cellIndex;
9240         var rowIndex = row.dom.rowIndex - 1; // start from 0
9241         
9242         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
9243         
9244     },
9245     
9246     onMouseout : function(e, el)
9247     {
9248         var cell = Roo.get(el);
9249         
9250         if(!cell){
9251             return;
9252         }
9253         
9254         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9255             cell = cell.findParent('td', false, true);
9256         }
9257         
9258         var row = cell.findParent('tr', false, true);
9259         var cellIndex = cell.dom.cellIndex;
9260         var rowIndex = row.dom.rowIndex - 1; // start from 0
9261         
9262         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9263         
9264     },
9265     
9266     onClick : function(e, el)
9267     {
9268         var cell = Roo.get(el);
9269         
9270         if(!cell || (!this.cellSelection && !this.rowSelection)){
9271             return;
9272         }
9273         
9274         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9275             cell = cell.findParent('td', false, true);
9276         }
9277         
9278         if(!cell || typeof(cell) == 'undefined'){
9279             return;
9280         }
9281         
9282         var row = cell.findParent('tr', false, true);
9283         
9284         if(!row || typeof(row) == 'undefined'){
9285             return;
9286         }
9287         
9288         var cellIndex = cell.dom.cellIndex;
9289         var rowIndex = this.getRowIndex(row);
9290         
9291         // why??? - should these not be based on SelectionModel?
9292         //if(this.cellSelection){
9293             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9294         //}
9295         
9296         //if(this.rowSelection){
9297             this.fireEvent('rowclick', this, row, rowIndex, e);
9298         //}
9299          
9300     },
9301         
9302     onDblClick : function(e,el)
9303     {
9304         var cell = Roo.get(el);
9305         
9306         if(!cell || (!this.cellSelection && !this.rowSelection)){
9307             return;
9308         }
9309         
9310         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9311             cell = cell.findParent('td', false, true);
9312         }
9313         
9314         if(!cell || typeof(cell) == 'undefined'){
9315             return;
9316         }
9317         
9318         var row = cell.findParent('tr', false, true);
9319         
9320         if(!row || typeof(row) == 'undefined'){
9321             return;
9322         }
9323         
9324         var cellIndex = cell.dom.cellIndex;
9325         var rowIndex = this.getRowIndex(row);
9326         
9327         if(this.cellSelection){
9328             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9329         }
9330         
9331         if(this.rowSelection){
9332             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9333         }
9334     },
9335     findRowIndex : function(el)
9336     {
9337         var cell = Roo.get(el);
9338         if(!cell) {
9339             return false;
9340         }
9341         var row = cell.findParent('tr', false, true);
9342         
9343         if(!row || typeof(row) == 'undefined'){
9344             return false;
9345         }
9346         return this.getRowIndex(row);
9347     },
9348     sort : function(e,el)
9349     {
9350         var col = Roo.get(el);
9351         
9352         if(!col.hasClass('sortable')){
9353             return;
9354         }
9355         
9356         var sort = col.attr('sort');
9357         var dir = 'ASC';
9358         
9359         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9360             dir = 'DESC';
9361         }
9362         
9363         this.store.sortInfo = {field : sort, direction : dir};
9364         
9365         if (this.footer) {
9366             Roo.log("calling footer first");
9367             this.footer.onClick('first');
9368         } else {
9369         
9370             this.store.load({ params : { start : 0 } });
9371         }
9372     },
9373     
9374     renderHeader : function()
9375     {
9376         var header = {
9377             tag: 'thead',
9378             cn : []
9379         };
9380         
9381         var cm = this.cm;
9382         this.totalWidth = 0;
9383         
9384         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9385             
9386             var config = cm.config[i];
9387             
9388             var c = {
9389                 tag: 'th',
9390                 cls : 'x-hcol-' + i,
9391                 style : '',
9392                 
9393                 html: cm.getColumnHeader(i)
9394             };
9395             
9396             var tooltip = cm.getColumnTooltip(i);
9397             if (tooltip) {
9398                 c.tooltip = tooltip;
9399             }
9400             
9401             
9402             var hh = '';
9403             
9404             if(typeof(config.sortable) != 'undefined' && config.sortable){
9405                 c.cls += ' sortable';
9406                 c.html = '<i class="fa"></i>' + c.html;
9407             }
9408             
9409             // could use BS4 hidden-..-down 
9410             
9411             if(typeof(config.lgHeader) != 'undefined'){
9412                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9413             }
9414             
9415             if(typeof(config.mdHeader) != 'undefined'){
9416                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9417             }
9418             
9419             if(typeof(config.smHeader) != 'undefined'){
9420                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9421             }
9422             
9423             if(typeof(config.xsHeader) != 'undefined'){
9424                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9425             }
9426             
9427             if(hh.length){
9428                 c.html = hh;
9429             }
9430             
9431             if(typeof(config.tooltip) != 'undefined'){
9432                 c.tooltip = config.tooltip;
9433             }
9434             
9435             if(typeof(config.colspan) != 'undefined'){
9436                 c.colspan = config.colspan;
9437             }
9438             
9439             // hidden is handled by CSS now
9440             
9441             if(typeof(config.dataIndex) != 'undefined'){
9442                 c.sort = config.dataIndex;
9443             }
9444             
9445            
9446             
9447             if(typeof(config.align) != 'undefined' && config.align.length){
9448                 c.style += ' text-align:' + config.align + ';';
9449             }
9450             
9451             /* width is done in CSS
9452              *if(typeof(config.width) != 'undefined'){
9453                 c.style += ' width:' + config.width + 'px;';
9454                 this.totalWidth += config.width;
9455             } else {
9456                 this.totalWidth += 100; // assume minimum of 100 per column?
9457             }
9458             */
9459             
9460             if(typeof(config.cls) != 'undefined'){
9461                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9462             }
9463             // this is the bit that doesnt reall work at all...
9464             
9465             if (this.responsive) {
9466                  
9467             
9468                 ['xs','sm','md','lg'].map(function(size){
9469                     
9470                     if(typeof(config[size]) == 'undefined'){
9471                         return;
9472                     }
9473                      
9474                     if (!config[size]) { // 0 = hidden
9475                         // BS 4 '0' is treated as hide that column and below.
9476                         c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9477                         return;
9478                     }
9479                     
9480                     c.cls += ' col-' + size + '-' + config[size] + (
9481                         size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9482                     );
9483                     
9484                     
9485                 });
9486             }
9487             // at the end?
9488             
9489             c.html +=' <span class="x-grid-split x-grid-split-' + i + '"></span>';
9490             
9491             
9492             
9493             
9494             header.cn.push(c)
9495         }
9496         
9497         return header;
9498     },
9499     
9500     renderBody : function()
9501     {
9502         var body = {
9503             tag: 'tbody',
9504             cn : [
9505                 {
9506                     tag: 'tr',
9507                     cn : [
9508                         {
9509                             tag : 'td',
9510                             colspan :  this.cm.getColumnCount()
9511                         }
9512                     ]
9513                 }
9514             ]
9515         };
9516         
9517         return body;
9518     },
9519     
9520     renderFooter : function()
9521     {
9522         var footer = {
9523             tag: 'tfoot',
9524             cn : [
9525                 {
9526                     tag: 'tr',
9527                     cn : [
9528                         {
9529                             tag : 'td',
9530                             colspan :  this.cm.getColumnCount()
9531                         }
9532                     ]
9533                 }
9534             ]
9535         };
9536         
9537         return footer;
9538     },
9539     
9540     
9541     
9542     onLoad : function()
9543     {
9544 //        Roo.log('ds onload');
9545         this.clear();
9546         
9547         var _this = this;
9548         var cm = this.cm;
9549         var ds = this.store;
9550         
9551         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9552             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9553             if (_this.store.sortInfo) {
9554                     
9555                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9556                     e.select('i', true).addClass(['fa-arrow-up']);
9557                 }
9558                 
9559                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9560                     e.select('i', true).addClass(['fa-arrow-down']);
9561                 }
9562             }
9563         });
9564         
9565         var tbody =  this.bodyEl;
9566               
9567         if(ds.getCount() > 0){
9568             ds.data.each(function(d,rowIndex){
9569                 var row =  this.renderRow(cm, ds, rowIndex);
9570                 
9571                 tbody.createChild(row);
9572                 
9573                 var _this = this;
9574                 
9575                 if(row.cellObjects.length){
9576                     Roo.each(row.cellObjects, function(r){
9577                         _this.renderCellObject(r);
9578                     })
9579                 }
9580                 
9581             }, this);
9582         }
9583         
9584         var tfoot = this.el.select('tfoot', true).first();
9585         
9586         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9587             
9588             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9589             
9590             var total = this.ds.getTotalCount();
9591             
9592             if(this.footer.pageSize < total){
9593                 this.mainFoot.show();
9594             }
9595         }
9596         
9597         Roo.each(this.el.select('tbody td', true).elements, function(e){
9598             e.on('mouseover', _this.onMouseover, _this);
9599         });
9600         
9601         Roo.each(this.el.select('tbody td', true).elements, function(e){
9602             e.on('mouseout', _this.onMouseout, _this);
9603         });
9604         this.fireEvent('rowsrendered', this);
9605         
9606         this.autoSize();
9607         
9608         this.initCSS(); /// resize cols
9609
9610         
9611     },
9612     
9613     
9614     onUpdate : function(ds,record)
9615     {
9616         this.refreshRow(record);
9617         this.autoSize();
9618     },
9619     
9620     onRemove : function(ds, record, index, isUpdate){
9621         if(isUpdate !== true){
9622             this.fireEvent("beforerowremoved", this, index, record);
9623         }
9624         var bt = this.bodyEl.dom;
9625         
9626         var rows = this.el.select('tbody > tr', true).elements;
9627         
9628         if(typeof(rows[index]) != 'undefined'){
9629             bt.removeChild(rows[index].dom);
9630         }
9631         
9632 //        if(bt.rows[index]){
9633 //            bt.removeChild(bt.rows[index]);
9634 //        }
9635         
9636         if(isUpdate !== true){
9637             //this.stripeRows(index);
9638             //this.syncRowHeights(index, index);
9639             //this.layout();
9640             this.fireEvent("rowremoved", this, index, record);
9641         }
9642     },
9643     
9644     onAdd : function(ds, records, rowIndex)
9645     {
9646         //Roo.log('on Add called');
9647         // - note this does not handle multiple adding very well..
9648         var bt = this.bodyEl.dom;
9649         for (var i =0 ; i < records.length;i++) {
9650             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9651             //Roo.log(records[i]);
9652             //Roo.log(this.store.getAt(rowIndex+i));
9653             this.insertRow(this.store, rowIndex + i, false);
9654             return;
9655         }
9656         
9657     },
9658     
9659     
9660     refreshRow : function(record){
9661         var ds = this.store, index;
9662         if(typeof record == 'number'){
9663             index = record;
9664             record = ds.getAt(index);
9665         }else{
9666             index = ds.indexOf(record);
9667             if (index < 0) {
9668                 return; // should not happen - but seems to 
9669             }
9670         }
9671         this.insertRow(ds, index, true);
9672         this.autoSize();
9673         this.onRemove(ds, record, index+1, true);
9674         this.autoSize();
9675         //this.syncRowHeights(index, index);
9676         //this.layout();
9677         this.fireEvent("rowupdated", this, index, record);
9678     },
9679     // private - called by RowSelection
9680     onRowSelect : function(rowIndex){
9681         var row = this.getRowDom(rowIndex);
9682         row.addClass(['bg-info','info']);
9683     },
9684     // private - called by RowSelection
9685     onRowDeselect : function(rowIndex)
9686     {
9687         if (rowIndex < 0) {
9688             return;
9689         }
9690         var row = this.getRowDom(rowIndex);
9691         row.removeClass(['bg-info','info']);
9692     },
9693       /**
9694      * Focuses the specified row.
9695      * @param {Number} row The row index
9696      */
9697     focusRow : function(row)
9698     {
9699         //Roo.log('GridView.focusRow');
9700         var x = this.bodyEl.dom.scrollLeft;
9701         this.focusCell(row, 0, false);
9702         this.bodyEl.dom.scrollLeft = x;
9703
9704     },
9705      /**
9706      * Focuses the specified cell.
9707      * @param {Number} row The row index
9708      * @param {Number} col The column index
9709      * @param {Boolean} hscroll false to disable horizontal scrolling
9710      */
9711     focusCell : function(row, col, hscroll)
9712     {
9713         //Roo.log('GridView.focusCell');
9714         var el = this.ensureVisible(row, col, hscroll);
9715         // not sure what focusEL achives = it's a <a> pos relative 
9716         //this.focusEl.alignTo(el, "tl-tl");
9717         //if(Roo.isGecko){
9718         //    this.focusEl.focus();
9719         //}else{
9720         //    this.focusEl.focus.defer(1, this.focusEl);
9721         //}
9722     },
9723     
9724      /**
9725      * Scrolls the specified cell into view
9726      * @param {Number} row The row index
9727      * @param {Number} col The column index
9728      * @param {Boolean} hscroll false to disable horizontal scrolling
9729      */
9730     ensureVisible : function(row, col, hscroll)
9731     {
9732         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
9733         //return null; //disable for testing.
9734         if(typeof row != "number"){
9735             row = row.rowIndex;
9736         }
9737         if(row < 0 && row >= this.ds.getCount()){
9738             return  null;
9739         }
9740         col = (col !== undefined ? col : 0);
9741         var cm = this.cm;
9742         while(cm.isHidden(col)){
9743             col++;
9744         }
9745
9746         var el = this.getCellDom(row, col);
9747         if(!el){
9748             return null;
9749         }
9750         var c = this.bodyEl.dom;
9751
9752         var ctop = parseInt(el.offsetTop, 10);
9753         var cleft = parseInt(el.offsetLeft, 10);
9754         var cbot = ctop + el.offsetHeight;
9755         var cright = cleft + el.offsetWidth;
9756
9757         //var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
9758         var ch = 0; //?? header is not withing the area?
9759         var stop = parseInt(c.scrollTop, 10);
9760         var sleft = parseInt(c.scrollLeft, 10);
9761         var sbot = stop + ch;
9762         var sright = sleft + c.clientWidth;
9763         /*
9764         Roo.log('GridView.ensureVisible:' +
9765                 ' ctop:' + ctop +
9766                 ' c.clientHeight:' + c.clientHeight +
9767                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
9768                 ' stop:' + stop +
9769                 ' cbot:' + cbot +
9770                 ' sbot:' + sbot +
9771                 ' ch:' + ch  
9772                 );
9773         */
9774         if(ctop < stop){
9775             c.scrollTop = ctop;
9776             //Roo.log("set scrolltop to ctop DISABLE?");
9777         }else if(cbot > sbot){
9778             //Roo.log("set scrolltop to cbot-ch");
9779             c.scrollTop = cbot-ch;
9780         }
9781
9782         if(hscroll !== false){
9783             if(cleft < sleft){
9784                 c.scrollLeft = cleft;
9785             }else if(cright > sright){
9786                 c.scrollLeft = cright-c.clientWidth;
9787             }
9788         }
9789
9790         return el;
9791     },
9792     
9793     
9794     insertRow : function(dm, rowIndex, isUpdate){
9795         
9796         if(!isUpdate){
9797             this.fireEvent("beforerowsinserted", this, rowIndex);
9798         }
9799             //var s = this.getScrollState();
9800         var row = this.renderRow(this.cm, this.store, rowIndex);
9801         // insert before rowIndex..
9802         var e = this.bodyEl.createChild(row,this.getRowDom(rowIndex));
9803         
9804         var _this = this;
9805                 
9806         if(row.cellObjects.length){
9807             Roo.each(row.cellObjects, function(r){
9808                 _this.renderCellObject(r);
9809             })
9810         }
9811             
9812         if(!isUpdate){
9813             this.fireEvent("rowsinserted", this, rowIndex);
9814             //this.syncRowHeights(firstRow, lastRow);
9815             //this.stripeRows(firstRow);
9816             //this.layout();
9817         }
9818         
9819     },
9820     
9821     
9822     getRowDom : function(rowIndex)
9823     {
9824         var rows = this.el.select('tbody > tr', true).elements;
9825         
9826         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9827         
9828     },
9829     getCellDom : function(rowIndex, colIndex)
9830     {
9831         var row = this.getRowDom(rowIndex);
9832         if (row === false) {
9833             return false;
9834         }
9835         var cols = row.select('td', true).elements;
9836         return (typeof(cols[colIndex]) == 'undefined') ? false : cols[colIndex];
9837         
9838     },
9839     
9840     // returns the object tree for a tr..
9841   
9842     
9843     renderRow : function(cm, ds, rowIndex) 
9844     {
9845         var d = ds.getAt(rowIndex);
9846         
9847         var row = {
9848             tag : 'tr',
9849             cls : 'x-row-' + rowIndex,
9850             cn : []
9851         };
9852             
9853         var cellObjects = [];
9854         
9855         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9856             var config = cm.config[i];
9857             
9858             var renderer = cm.getRenderer(i);
9859             var value = '';
9860             var id = false;
9861             
9862             if(typeof(renderer) !== 'undefined'){
9863                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9864             }
9865             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9866             // and are rendered into the cells after the row is rendered - using the id for the element.
9867             
9868             if(typeof(value) === 'object'){
9869                 id = Roo.id();
9870                 cellObjects.push({
9871                     container : id,
9872                     cfg : value 
9873                 })
9874             }
9875             
9876             var rowcfg = {
9877                 record: d,
9878                 rowIndex : rowIndex,
9879                 colIndex : i,
9880                 rowClass : ''
9881             };
9882
9883             this.fireEvent('rowclass', this, rowcfg);
9884             
9885             var td = {
9886                 tag: 'td',
9887                 // this might end up displaying HTML?
9888                 // this is too messy... - better to only do it on columsn you know are going to be too long
9889                 //tooltip : (typeof(value) === 'object') ? '' : value,
9890                 cls : rowcfg.rowClass + ' x-col-' + i,
9891                 style: '',
9892                 html: (typeof(value) === 'object') ? '' : value
9893             };
9894             
9895             if (id) {
9896                 td.id = id;
9897             }
9898             
9899             if(typeof(config.colspan) != 'undefined'){
9900                 td.colspan = config.colspan;
9901             }
9902             
9903             
9904             
9905             if(typeof(config.align) != 'undefined' && config.align.length){
9906                 td.style += ' text-align:' + config.align + ';';
9907             }
9908             if(typeof(config.valign) != 'undefined' && config.valign.length){
9909                 td.style += ' vertical-align:' + config.valign + ';';
9910             }
9911             /*
9912             if(typeof(config.width) != 'undefined'){
9913                 td.style += ' width:' +  config.width + 'px;';
9914             }
9915             */
9916             
9917             if(typeof(config.cursor) != 'undefined'){
9918                 td.style += ' cursor:' +  config.cursor + ';';
9919             }
9920             
9921             if(typeof(config.cls) != 'undefined'){
9922                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9923             }
9924             if (this.responsive) {
9925                 ['xs','sm','md','lg'].map(function(size){
9926                     
9927                     if(typeof(config[size]) == 'undefined'){
9928                         return;
9929                     }
9930                     
9931                     
9932                       
9933                     if (!config[size]) { // 0 = hidden
9934                         // BS 4 '0' is treated as hide that column and below.
9935                         td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9936                         return;
9937                     }
9938                     
9939                     td.cls += ' col-' + size + '-' + config[size] + (
9940                         size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9941                     );
9942                      
9943     
9944                 });
9945             }
9946             row.cn.push(td);
9947            
9948         }
9949         
9950         row.cellObjects = cellObjects;
9951         
9952         return row;
9953           
9954     },
9955     
9956     
9957     
9958     onBeforeLoad : function()
9959     {
9960         
9961     },
9962      /**
9963      * Remove all rows
9964      */
9965     clear : function()
9966     {
9967         this.el.select('tbody', true).first().dom.innerHTML = '';
9968     },
9969     /**
9970      * Show or hide a row.
9971      * @param {Number} rowIndex to show or hide
9972      * @param {Boolean} state hide
9973      */
9974     setRowVisibility : function(rowIndex, state)
9975     {
9976         var bt = this.bodyEl.dom;
9977         
9978         var rows = this.el.select('tbody > tr', true).elements;
9979         
9980         if(typeof(rows[rowIndex]) == 'undefined'){
9981             return;
9982         }
9983         rows[rowIndex][ state ? 'removeClass' : 'addClass']('d-none');
9984         
9985     },
9986     
9987     
9988     getSelectionModel : function(){
9989         if(!this.selModel){
9990             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9991         }
9992         return this.selModel;
9993     },
9994     /*
9995      * Render the Roo.bootstrap object from renderder
9996      */
9997     renderCellObject : function(r)
9998     {
9999         var _this = this;
10000         
10001         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
10002         
10003         var t = r.cfg.render(r.container);
10004         
10005         if(r.cfg.cn){
10006             Roo.each(r.cfg.cn, function(c){
10007                 var child = {
10008                     container: t.getChildContainer(),
10009                     cfg: c
10010                 };
10011                 _this.renderCellObject(child);
10012             })
10013         }
10014     },
10015     /**
10016      * get the Row Index from a dom element.
10017      * @param {Roo.Element} row The row to look for
10018      * @returns {Number} the row
10019      */
10020     getRowIndex : function(row)
10021     {
10022         var rowIndex = -1;
10023         
10024         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
10025             if(el != row){
10026                 return;
10027             }
10028             
10029             rowIndex = index;
10030         });
10031         
10032         return rowIndex;
10033     },
10034     /**
10035      * get the header TH element for columnIndex
10036      * @param {Number} columnIndex
10037      * @returns {Roo.Element}
10038      */
10039     getHeaderIndex: function(colIndex)
10040     {
10041         var cols = this.headEl.select('th', true).elements;
10042         return cols[colIndex]; 
10043     },
10044     /**
10045      * get the Column Index from a dom element. (using regex on x-hcol-{colid})
10046      * @param {domElement} cell to look for
10047      * @returns {Number} the column
10048      */
10049     getCellIndex : function(cell)
10050     {
10051         var id = String(cell.className).match(Roo.bootstrap.Table.cellRE);
10052         if(id){
10053             return parseInt(id[1], 10);
10054         }
10055         return 0;
10056     },
10057      /**
10058      * Returns the grid's underlying element = used by panel.Grid
10059      * @return {Element} The element
10060      */
10061     getGridEl : function(){
10062         return this.el;
10063     },
10064      /**
10065      * Forces a resize - used by panel.Grid
10066      * @return {Element} The element
10067      */
10068     autoSize : function()
10069     {
10070         //var ctr = Roo.get(this.container.dom.parentElement);
10071         var ctr = Roo.get(this.el.dom);
10072         
10073         var thd = this.getGridEl().select('thead',true).first();
10074         var tbd = this.getGridEl().select('tbody', true).first();
10075         var tfd = this.getGridEl().select('tfoot', true).first();
10076         
10077         var cw = ctr.getWidth();
10078         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
10079         
10080         if (tbd) {
10081             
10082             tbd.setWidth(ctr.getWidth());
10083             // if the body has a max height - and then scrolls - we should perhaps set up the height here
10084             // this needs fixing for various usage - currently only hydra job advers I think..
10085             //tdb.setHeight(
10086             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
10087             //); 
10088             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
10089             cw -= barsize;
10090         }
10091         cw = Math.max(cw, this.totalWidth);
10092         this.getGridEl().select('tbody tr',true).setWidth(cw);
10093         this.initCSS();
10094         
10095         // resize 'expandable coloumn?
10096         
10097         return; // we doe not have a view in this design..
10098         
10099     },
10100     onBodyScroll: function()
10101     {
10102         //Roo.log("body scrolled');" + this.bodyEl.dom.scrollLeft);
10103         if(this.headEl){
10104             this.headEl.setStyle({
10105                 'position' : 'relative',
10106                 'left': (-1* this.bodyEl.dom.scrollLeft) + 'px'
10107             });
10108         }
10109         
10110         if(this.lazyLoad){
10111             
10112             var scrollHeight = this.bodyEl.dom.scrollHeight;
10113             
10114             var scrollTop = Math.ceil(this.bodyEl.getScroll().top);
10115             
10116             var height = this.bodyEl.getHeight();
10117             
10118             if(scrollHeight - height == scrollTop) {
10119                 
10120                 var total = this.ds.getTotalCount();
10121                 
10122                 if(this.footer.cursor + this.footer.pageSize < total){
10123                     
10124                     this.footer.ds.load({
10125                         params : {
10126                             start : this.footer.cursor + this.footer.pageSize,
10127                             limit : this.footer.pageSize
10128                         },
10129                         add : true
10130                     });
10131                 }
10132             }
10133             
10134         }
10135     },
10136     onColumnSplitterMoved : function(i, diff)
10137     {
10138         this.userResized = true;
10139         
10140         var cm = this.colModel;
10141         
10142         var w = this.getHeaderIndex(i).getWidth() + diff;
10143         
10144         
10145         cm.setColumnWidth(i, w, true);
10146         this.initCSS();
10147         //var cid = cm.getColumnId(i); << not used in this version?
10148        /* Roo.log(['#' + this.id + ' .x-col-' + i, "width", w + "px"]);
10149         
10150         this.CSS.updateRule( '#' + this.id + ' .x-col-' + i, "width", w + "px");
10151         this.CSS.updateRule('#' + this.id + ' .x-hcol-' + i, "width", w + "px");
10152         this.CSS.updateRule('#' + this.id + ' .x-grid-split-' + i, "left", w + "px");
10153 */
10154         //this.updateSplitters();
10155         //this.layout(); << ??
10156         this.fireEvent("columnresize", i, w);
10157     },
10158     onHeaderChange : function()
10159     {
10160         var header = this.renderHeader();
10161         var table = this.el.select('table', true).first();
10162         
10163         this.headEl.remove();
10164         this.headEl = table.createChild(header, this.bodyEl, false);
10165         
10166         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
10167             e.on('click', this.sort, this);
10168         }, this);
10169         
10170         if(this.enableColumnResize !== false && Roo.grid.SplitDragZone){
10171             new Roo.grid.SplitDragZone(this, this.headEl.dom, false); // not sure what 'lockedHd is for this implementation..)
10172         }
10173         
10174     },
10175     
10176     onHiddenChange : function(colModel, colIndex, hidden)
10177     {
10178         /*
10179         this.cm.setHidden()
10180         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
10181         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
10182         
10183         this.CSS.updateRule(thSelector, "display", "");
10184         this.CSS.updateRule(tdSelector, "display", "");
10185         
10186         if(hidden){
10187             this.CSS.updateRule(thSelector, "display", "none");
10188             this.CSS.updateRule(tdSelector, "display", "none");
10189         }
10190         */
10191         // onload calls initCSS()
10192         this.onHeaderChange();
10193         this.onLoad();
10194     },
10195     
10196     setColumnWidth: function(col_index, width)
10197     {
10198         // width = "md-2 xs-2..."
10199         if(!this.colModel.config[col_index]) {
10200             return;
10201         }
10202         
10203         var w = width.split(" ");
10204         
10205         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
10206         
10207         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
10208         
10209         
10210         for(var j = 0; j < w.length; j++) {
10211             
10212             if(!w[j]) {
10213                 continue;
10214             }
10215             
10216             var size_cls = w[j].split("-");
10217             
10218             if(!Number.isInteger(size_cls[1] * 1)) {
10219                 continue;
10220             }
10221             
10222             if(!this.colModel.config[col_index][size_cls[0]]) {
10223                 continue;
10224             }
10225             
10226             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10227                 continue;
10228             }
10229             
10230             h_row[0].classList.replace(
10231                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10232                 "col-"+size_cls[0]+"-"+size_cls[1]
10233             );
10234             
10235             for(var i = 0; i < rows.length; i++) {
10236                 
10237                 var size_cls = w[j].split("-");
10238                 
10239                 if(!Number.isInteger(size_cls[1] * 1)) {
10240                     continue;
10241                 }
10242                 
10243                 if(!this.colModel.config[col_index][size_cls[0]]) {
10244                     continue;
10245                 }
10246                 
10247                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
10248                     continue;
10249                 }
10250                 
10251                 rows[i].classList.replace(
10252                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
10253                     "col-"+size_cls[0]+"-"+size_cls[1]
10254                 );
10255             }
10256             
10257             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
10258         }
10259     }
10260 });
10261
10262 // currently only used to find the split on drag.. 
10263 Roo.bootstrap.Table.cellRE = /(?:.*?)x-grid-(?:hd|cell|split)-([\d]+)(?:.*?)/;
10264
10265 /**
10266  * @depricated
10267 */
10268 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
10269 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;
10270 /*
10271  * - LGPL
10272  *
10273  * table cell
10274  * 
10275  */
10276
10277 /**
10278  * @class Roo.bootstrap.TableCell
10279  * @extends Roo.bootstrap.Component
10280  * Bootstrap TableCell class
10281  * @cfg {String} html cell contain text
10282  * @cfg {String} cls cell class
10283  * @cfg {String} tag cell tag (td|th) default td
10284  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
10285  * @cfg {String} align Aligns the content in a cell
10286  * @cfg {String} axis Categorizes cells
10287  * @cfg {String} bgcolor Specifies the background color of a cell
10288  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10289  * @cfg {Number} colspan Specifies the number of columns a cell should span
10290  * @cfg {String} headers Specifies one or more header cells a cell is related to
10291  * @cfg {Number} height Sets the height of a cell
10292  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
10293  * @cfg {Number} rowspan Sets the number of rows a cell should span
10294  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
10295  * @cfg {String} valign Vertical aligns the content in a cell
10296  * @cfg {Number} width Specifies the width of a cell
10297  * 
10298  * @constructor
10299  * Create a new TableCell
10300  * @param {Object} config The config object
10301  */
10302
10303 Roo.bootstrap.TableCell = function(config){
10304     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
10305 };
10306
10307 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
10308     
10309     html: false,
10310     cls: false,
10311     tag: false,
10312     abbr: false,
10313     align: false,
10314     axis: false,
10315     bgcolor: false,
10316     charoff: false,
10317     colspan: false,
10318     headers: false,
10319     height: false,
10320     nowrap: false,
10321     rowspan: false,
10322     scope: false,
10323     valign: false,
10324     width: false,
10325     
10326     
10327     getAutoCreate : function(){
10328         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
10329         
10330         cfg = {
10331             tag: 'td'
10332         };
10333         
10334         if(this.tag){
10335             cfg.tag = this.tag;
10336         }
10337         
10338         if (this.html) {
10339             cfg.html=this.html
10340         }
10341         if (this.cls) {
10342             cfg.cls=this.cls
10343         }
10344         if (this.abbr) {
10345             cfg.abbr=this.abbr
10346         }
10347         if (this.align) {
10348             cfg.align=this.align
10349         }
10350         if (this.axis) {
10351             cfg.axis=this.axis
10352         }
10353         if (this.bgcolor) {
10354             cfg.bgcolor=this.bgcolor
10355         }
10356         if (this.charoff) {
10357             cfg.charoff=this.charoff
10358         }
10359         if (this.colspan) {
10360             cfg.colspan=this.colspan
10361         }
10362         if (this.headers) {
10363             cfg.headers=this.headers
10364         }
10365         if (this.height) {
10366             cfg.height=this.height
10367         }
10368         if (this.nowrap) {
10369             cfg.nowrap=this.nowrap
10370         }
10371         if (this.rowspan) {
10372             cfg.rowspan=this.rowspan
10373         }
10374         if (this.scope) {
10375             cfg.scope=this.scope
10376         }
10377         if (this.valign) {
10378             cfg.valign=this.valign
10379         }
10380         if (this.width) {
10381             cfg.width=this.width
10382         }
10383         
10384         
10385         return cfg;
10386     }
10387    
10388 });
10389
10390  
10391
10392  /*
10393  * - LGPL
10394  *
10395  * table row
10396  * 
10397  */
10398
10399 /**
10400  * @class Roo.bootstrap.TableRow
10401  * @extends Roo.bootstrap.Component
10402  * Bootstrap TableRow class
10403  * @cfg {String} cls row class
10404  * @cfg {String} align Aligns the content in a table row
10405  * @cfg {String} bgcolor Specifies a background color for a table row
10406  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
10407  * @cfg {String} valign Vertical aligns the content in a table row
10408  * 
10409  * @constructor
10410  * Create a new TableRow
10411  * @param {Object} config The config object
10412  */
10413
10414 Roo.bootstrap.TableRow = function(config){
10415     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
10416 };
10417
10418 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
10419     
10420     cls: false,
10421     align: false,
10422     bgcolor: false,
10423     charoff: false,
10424     valign: false,
10425     
10426     getAutoCreate : function(){
10427         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
10428         
10429         cfg = {
10430             tag: 'tr'
10431         };
10432             
10433         if(this.cls){
10434             cfg.cls = this.cls;
10435         }
10436         if(this.align){
10437             cfg.align = this.align;
10438         }
10439         if(this.bgcolor){
10440             cfg.bgcolor = this.bgcolor;
10441         }
10442         if(this.charoff){
10443             cfg.charoff = this.charoff;
10444         }
10445         if(this.valign){
10446             cfg.valign = this.valign;
10447         }
10448         
10449         return cfg;
10450     }
10451    
10452 });
10453
10454  
10455
10456  /*
10457  * - LGPL
10458  *
10459  * table body
10460  * 
10461  */
10462
10463 /**
10464  * @class Roo.bootstrap.TableBody
10465  * @extends Roo.bootstrap.Component
10466  * Bootstrap TableBody class
10467  * @cfg {String} cls element class
10468  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10469  * @cfg {String} align Aligns the content inside the element
10470  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10471  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10472  * 
10473  * @constructor
10474  * Create a new TableBody
10475  * @param {Object} config The config object
10476  */
10477
10478 Roo.bootstrap.TableBody = function(config){
10479     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10480 };
10481
10482 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10483     
10484     cls: false,
10485     tag: false,
10486     align: false,
10487     charoff: false,
10488     valign: false,
10489     
10490     getAutoCreate : function(){
10491         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10492         
10493         cfg = {
10494             tag: 'tbody'
10495         };
10496             
10497         if (this.cls) {
10498             cfg.cls=this.cls
10499         }
10500         if(this.tag){
10501             cfg.tag = this.tag;
10502         }
10503         
10504         if(this.align){
10505             cfg.align = this.align;
10506         }
10507         if(this.charoff){
10508             cfg.charoff = this.charoff;
10509         }
10510         if(this.valign){
10511             cfg.valign = this.valign;
10512         }
10513         
10514         return cfg;
10515     }
10516     
10517     
10518 //    initEvents : function()
10519 //    {
10520 //        
10521 //        if(!this.store){
10522 //            return;
10523 //        }
10524 //        
10525 //        this.store = Roo.factory(this.store, Roo.data);
10526 //        this.store.on('load', this.onLoad, this);
10527 //        
10528 //        this.store.load();
10529 //        
10530 //    },
10531 //    
10532 //    onLoad: function () 
10533 //    {   
10534 //        this.fireEvent('load', this);
10535 //    }
10536 //    
10537 //   
10538 });
10539
10540  
10541
10542  /*
10543  * Based on:
10544  * Ext JS Library 1.1.1
10545  * Copyright(c) 2006-2007, Ext JS, LLC.
10546  *
10547  * Originally Released Under LGPL - original licence link has changed is not relivant.
10548  *
10549  * Fork - LGPL
10550  * <script type="text/javascript">
10551  */
10552
10553 // as we use this in bootstrap.
10554 Roo.namespace('Roo.form');
10555  /**
10556  * @class Roo.form.Action
10557  * Internal Class used to handle form actions
10558  * @constructor
10559  * @param {Roo.form.BasicForm} el The form element or its id
10560  * @param {Object} config Configuration options
10561  */
10562
10563  
10564  
10565 // define the action interface
10566 Roo.form.Action = function(form, options){
10567     this.form = form;
10568     this.options = options || {};
10569 };
10570 /**
10571  * Client Validation Failed
10572  * @const 
10573  */
10574 Roo.form.Action.CLIENT_INVALID = 'client';
10575 /**
10576  * Server Validation Failed
10577  * @const 
10578  */
10579 Roo.form.Action.SERVER_INVALID = 'server';
10580  /**
10581  * Connect to Server Failed
10582  * @const 
10583  */
10584 Roo.form.Action.CONNECT_FAILURE = 'connect';
10585 /**
10586  * Reading Data from Server Failed
10587  * @const 
10588  */
10589 Roo.form.Action.LOAD_FAILURE = 'load';
10590
10591 Roo.form.Action.prototype = {
10592     type : 'default',
10593     failureType : undefined,
10594     response : undefined,
10595     result : undefined,
10596
10597     // interface method
10598     run : function(options){
10599
10600     },
10601
10602     // interface method
10603     success : function(response){
10604
10605     },
10606
10607     // interface method
10608     handleResponse : function(response){
10609
10610     },
10611
10612     // default connection failure
10613     failure : function(response){
10614         
10615         this.response = response;
10616         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10617         this.form.afterAction(this, false);
10618     },
10619
10620     processResponse : function(response){
10621         this.response = response;
10622         if(!response.responseText){
10623             return true;
10624         }
10625         this.result = this.handleResponse(response);
10626         return this.result;
10627     },
10628
10629     // utility functions used internally
10630     getUrl : function(appendParams){
10631         var url = this.options.url || this.form.url || this.form.el.dom.action;
10632         if(appendParams){
10633             var p = this.getParams();
10634             if(p){
10635                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10636             }
10637         }
10638         return url;
10639     },
10640
10641     getMethod : function(){
10642         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10643     },
10644
10645     getParams : function(){
10646         var bp = this.form.baseParams;
10647         var p = this.options.params;
10648         if(p){
10649             if(typeof p == "object"){
10650                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10651             }else if(typeof p == 'string' && bp){
10652                 p += '&' + Roo.urlEncode(bp);
10653             }
10654         }else if(bp){
10655             p = Roo.urlEncode(bp);
10656         }
10657         return p;
10658     },
10659
10660     createCallback : function(){
10661         return {
10662             success: this.success,
10663             failure: this.failure,
10664             scope: this,
10665             timeout: (this.form.timeout*1000),
10666             upload: this.form.fileUpload ? this.success : undefined
10667         };
10668     }
10669 };
10670
10671 Roo.form.Action.Submit = function(form, options){
10672     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10673 };
10674
10675 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10676     type : 'submit',
10677
10678     haveProgress : false,
10679     uploadComplete : false,
10680     
10681     // uploadProgress indicator.
10682     uploadProgress : function()
10683     {
10684         if (!this.form.progressUrl) {
10685             return;
10686         }
10687         
10688         if (!this.haveProgress) {
10689             Roo.MessageBox.progress("Uploading", "Uploading");
10690         }
10691         if (this.uploadComplete) {
10692            Roo.MessageBox.hide();
10693            return;
10694         }
10695         
10696         this.haveProgress = true;
10697    
10698         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10699         
10700         var c = new Roo.data.Connection();
10701         c.request({
10702             url : this.form.progressUrl,
10703             params: {
10704                 id : uid
10705             },
10706             method: 'GET',
10707             success : function(req){
10708                //console.log(data);
10709                 var rdata = false;
10710                 var edata;
10711                 try  {
10712                    rdata = Roo.decode(req.responseText)
10713                 } catch (e) {
10714                     Roo.log("Invalid data from server..");
10715                     Roo.log(edata);
10716                     return;
10717                 }
10718                 if (!rdata || !rdata.success) {
10719                     Roo.log(rdata);
10720                     Roo.MessageBox.alert(Roo.encode(rdata));
10721                     return;
10722                 }
10723                 var data = rdata.data;
10724                 
10725                 if (this.uploadComplete) {
10726                    Roo.MessageBox.hide();
10727                    return;
10728                 }
10729                    
10730                 if (data){
10731                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10732                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10733                     );
10734                 }
10735                 this.uploadProgress.defer(2000,this);
10736             },
10737        
10738             failure: function(data) {
10739                 Roo.log('progress url failed ');
10740                 Roo.log(data);
10741             },
10742             scope : this
10743         });
10744            
10745     },
10746     
10747     
10748     run : function()
10749     {
10750         // run get Values on the form, so it syncs any secondary forms.
10751         this.form.getValues();
10752         
10753         var o = this.options;
10754         var method = this.getMethod();
10755         var isPost = method == 'POST';
10756         if(o.clientValidation === false || this.form.isValid()){
10757             
10758             if (this.form.progressUrl) {
10759                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10760                     (new Date() * 1) + '' + Math.random());
10761                     
10762             } 
10763             
10764             
10765             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10766                 form:this.form.el.dom,
10767                 url:this.getUrl(!isPost),
10768                 method: method,
10769                 params:isPost ? this.getParams() : null,
10770                 isUpload: this.form.fileUpload,
10771                 formData : this.form.formData
10772             }));
10773             
10774             this.uploadProgress();
10775
10776         }else if (o.clientValidation !== false){ // client validation failed
10777             this.failureType = Roo.form.Action.CLIENT_INVALID;
10778             this.form.afterAction(this, false);
10779         }
10780     },
10781
10782     success : function(response)
10783     {
10784         this.uploadComplete= true;
10785         if (this.haveProgress) {
10786             Roo.MessageBox.hide();
10787         }
10788         
10789         
10790         var result = this.processResponse(response);
10791         if(result === true || result.success){
10792             this.form.afterAction(this, true);
10793             return;
10794         }
10795         if(result.errors){
10796             this.form.markInvalid(result.errors);
10797             this.failureType = Roo.form.Action.SERVER_INVALID;
10798         }
10799         this.form.afterAction(this, false);
10800     },
10801     failure : function(response)
10802     {
10803         this.uploadComplete= true;
10804         if (this.haveProgress) {
10805             Roo.MessageBox.hide();
10806         }
10807         
10808         this.response = response;
10809         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10810         this.form.afterAction(this, false);
10811     },
10812     
10813     handleResponse : function(response){
10814         if(this.form.errorReader){
10815             var rs = this.form.errorReader.read(response);
10816             var errors = [];
10817             if(rs.records){
10818                 for(var i = 0, len = rs.records.length; i < len; i++) {
10819                     var r = rs.records[i];
10820                     errors[i] = r.data;
10821                 }
10822             }
10823             if(errors.length < 1){
10824                 errors = null;
10825             }
10826             return {
10827                 success : rs.success,
10828                 errors : errors
10829             };
10830         }
10831         var ret = false;
10832         try {
10833             ret = Roo.decode(response.responseText);
10834         } catch (e) {
10835             ret = {
10836                 success: false,
10837                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10838                 errors : []
10839             };
10840         }
10841         return ret;
10842         
10843     }
10844 });
10845
10846
10847 Roo.form.Action.Load = function(form, options){
10848     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10849     this.reader = this.form.reader;
10850 };
10851
10852 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10853     type : 'load',
10854
10855     run : function(){
10856         
10857         Roo.Ajax.request(Roo.apply(
10858                 this.createCallback(), {
10859                     method:this.getMethod(),
10860                     url:this.getUrl(false),
10861                     params:this.getParams()
10862         }));
10863     },
10864
10865     success : function(response){
10866         
10867         var result = this.processResponse(response);
10868         if(result === true || !result.success || !result.data){
10869             this.failureType = Roo.form.Action.LOAD_FAILURE;
10870             this.form.afterAction(this, false);
10871             return;
10872         }
10873         this.form.clearInvalid();
10874         this.form.setValues(result.data);
10875         this.form.afterAction(this, true);
10876     },
10877
10878     handleResponse : function(response){
10879         if(this.form.reader){
10880             var rs = this.form.reader.read(response);
10881             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10882             return {
10883                 success : rs.success,
10884                 data : data
10885             };
10886         }
10887         return Roo.decode(response.responseText);
10888     }
10889 });
10890
10891 Roo.form.Action.ACTION_TYPES = {
10892     'load' : Roo.form.Action.Load,
10893     'submit' : Roo.form.Action.Submit
10894 };/*
10895  * - LGPL
10896  *
10897  * form
10898  *
10899  */
10900
10901 /**
10902  * @class Roo.bootstrap.Form
10903  * @extends Roo.bootstrap.Component
10904  * Bootstrap Form class
10905  * @cfg {String} method  GET | POST (default POST)
10906  * @cfg {String} labelAlign top | left (default top)
10907  * @cfg {String} align left  | right - for navbars
10908  * @cfg {Boolean} loadMask load mask when submit (default true)
10909
10910  *
10911  * @constructor
10912  * Create a new Form
10913  * @param {Object} config The config object
10914  */
10915
10916
10917 Roo.bootstrap.Form = function(config){
10918     
10919     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10920     
10921     Roo.bootstrap.Form.popover.apply();
10922     
10923     this.addEvents({
10924         /**
10925          * @event clientvalidation
10926          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10927          * @param {Form} this
10928          * @param {Boolean} valid true if the form has passed client-side validation
10929          */
10930         clientvalidation: true,
10931         /**
10932          * @event beforeaction
10933          * Fires before any action is performed. Return false to cancel the action.
10934          * @param {Form} this
10935          * @param {Action} action The action to be performed
10936          */
10937         beforeaction: true,
10938         /**
10939          * @event actionfailed
10940          * Fires when an action fails.
10941          * @param {Form} this
10942          * @param {Action} action The action that failed
10943          */
10944         actionfailed : true,
10945         /**
10946          * @event actioncomplete
10947          * Fires when an action is completed.
10948          * @param {Form} this
10949          * @param {Action} action The action that completed
10950          */
10951         actioncomplete : true
10952     });
10953 };
10954
10955 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10956
10957      /**
10958      * @cfg {String} method
10959      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10960      */
10961     method : 'POST',
10962     /**
10963      * @cfg {String} url
10964      * The URL to use for form actions if one isn't supplied in the action options.
10965      */
10966     /**
10967      * @cfg {Boolean} fileUpload
10968      * Set to true if this form is a file upload.
10969      */
10970
10971     /**
10972      * @cfg {Object} baseParams
10973      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10974      */
10975
10976     /**
10977      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10978      */
10979     timeout: 30,
10980     /**
10981      * @cfg {Sting} align (left|right) for navbar forms
10982      */
10983     align : 'left',
10984
10985     // private
10986     activeAction : null,
10987
10988     /**
10989      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10990      * element by passing it or its id or mask the form itself by passing in true.
10991      * @type Mixed
10992      */
10993     waitMsgTarget : false,
10994
10995     loadMask : true,
10996     
10997     /**
10998      * @cfg {Boolean} errorMask (true|false) default false
10999      */
11000     errorMask : false,
11001     
11002     /**
11003      * @cfg {Number} maskOffset Default 100
11004      */
11005     maskOffset : 100,
11006     
11007     /**
11008      * @cfg {Boolean} maskBody
11009      */
11010     maskBody : false,
11011
11012     getAutoCreate : function(){
11013
11014         var cfg = {
11015             tag: 'form',
11016             method : this.method || 'POST',
11017             id : this.id || Roo.id(),
11018             cls : ''
11019         };
11020         if (this.parent().xtype.match(/^Nav/)) {
11021             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
11022
11023         }
11024
11025         if (this.labelAlign == 'left' ) {
11026             cfg.cls += ' form-horizontal';
11027         }
11028
11029
11030         return cfg;
11031     },
11032     initEvents : function()
11033     {
11034         this.el.on('submit', this.onSubmit, this);
11035         // this was added as random key presses on the form where triggering form submit.
11036         this.el.on('keypress', function(e) {
11037             if (e.getCharCode() != 13) {
11038                 return true;
11039             }
11040             // we might need to allow it for textareas.. and some other items.
11041             // check e.getTarget().
11042
11043             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
11044                 return true;
11045             }
11046
11047             Roo.log("keypress blocked");
11048
11049             e.preventDefault();
11050             return false;
11051         });
11052         
11053     },
11054     // private
11055     onSubmit : function(e){
11056         e.stopEvent();
11057     },
11058
11059      /**
11060      * Returns true if client-side validation on the form is successful.
11061      * @return Boolean
11062      */
11063     isValid : function(){
11064         var items = this.getItems();
11065         var valid = true;
11066         var target = false;
11067         
11068         items.each(function(f){
11069             
11070             if(f.validate()){
11071                 return;
11072             }
11073             
11074             Roo.log('invalid field: ' + f.name);
11075             
11076             valid = false;
11077
11078             if(!target && f.el.isVisible(true)){
11079                 target = f;
11080             }
11081            
11082         });
11083         
11084         if(this.errorMask && !valid){
11085             Roo.bootstrap.Form.popover.mask(this, target);
11086         }
11087         
11088         return valid;
11089     },
11090     
11091     /**
11092      * Returns true if any fields in this form have changed since their original load.
11093      * @return Boolean
11094      */
11095     isDirty : function(){
11096         var dirty = false;
11097         var items = this.getItems();
11098         items.each(function(f){
11099            if(f.isDirty()){
11100                dirty = true;
11101                return false;
11102            }
11103            return true;
11104         });
11105         return dirty;
11106     },
11107      /**
11108      * Performs a predefined action (submit or load) or custom actions you define on this form.
11109      * @param {String} actionName The name of the action type
11110      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
11111      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
11112      * accept other config options):
11113      * <pre>
11114 Property          Type             Description
11115 ----------------  ---------------  ----------------------------------------------------------------------------------
11116 url               String           The url for the action (defaults to the form's url)
11117 method            String           The form method to use (defaults to the form's method, or POST if not defined)
11118 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
11119 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
11120                                    validate the form on the client (defaults to false)
11121      * </pre>
11122      * @return {BasicForm} this
11123      */
11124     doAction : function(action, options){
11125         if(typeof action == 'string'){
11126             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
11127         }
11128         if(this.fireEvent('beforeaction', this, action) !== false){
11129             this.beforeAction(action);
11130             action.run.defer(100, action);
11131         }
11132         return this;
11133     },
11134
11135     // private
11136     beforeAction : function(action){
11137         var o = action.options;
11138         
11139         if(this.loadMask){
11140             
11141             if(this.maskBody){
11142                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
11143             } else {
11144                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11145             }
11146         }
11147         // not really supported yet.. ??
11148
11149         //if(this.waitMsgTarget === true){
11150         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
11151         //}else if(this.waitMsgTarget){
11152         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
11153         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
11154         //}else {
11155         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
11156        // }
11157
11158     },
11159
11160     // private
11161     afterAction : function(action, success){
11162         this.activeAction = null;
11163         var o = action.options;
11164
11165         if(this.loadMask){
11166             
11167             if(this.maskBody){
11168                 Roo.get(document.body).unmask();
11169             } else {
11170                 this.el.unmask();
11171             }
11172         }
11173         
11174         //if(this.waitMsgTarget === true){
11175 //            this.el.unmask();
11176         //}else if(this.waitMsgTarget){
11177         //    this.waitMsgTarget.unmask();
11178         //}else{
11179         //    Roo.MessageBox.updateProgress(1);
11180         //    Roo.MessageBox.hide();
11181        // }
11182         //
11183         if(success){
11184             if(o.reset){
11185                 this.reset();
11186             }
11187             Roo.callback(o.success, o.scope, [this, action]);
11188             this.fireEvent('actioncomplete', this, action);
11189
11190         }else{
11191
11192             // failure condition..
11193             // we have a scenario where updates need confirming.
11194             // eg. if a locking scenario exists..
11195             // we look for { errors : { needs_confirm : true }} in the response.
11196             if (
11197                 (typeof(action.result) != 'undefined')  &&
11198                 (typeof(action.result.errors) != 'undefined')  &&
11199                 (typeof(action.result.errors.needs_confirm) != 'undefined')
11200            ){
11201                 var _t = this;
11202                 Roo.log("not supported yet");
11203                  /*
11204
11205                 Roo.MessageBox.confirm(
11206                     "Change requires confirmation",
11207                     action.result.errorMsg,
11208                     function(r) {
11209                         if (r != 'yes') {
11210                             return;
11211                         }
11212                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
11213                     }
11214
11215                 );
11216                 */
11217
11218
11219                 return;
11220             }
11221
11222             Roo.callback(o.failure, o.scope, [this, action]);
11223             // show an error message if no failed handler is set..
11224             if (!this.hasListener('actionfailed')) {
11225                 Roo.log("need to add dialog support");
11226                 /*
11227                 Roo.MessageBox.alert("Error",
11228                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
11229                         action.result.errorMsg :
11230                         "Saving Failed, please check your entries or try again"
11231                 );
11232                 */
11233             }
11234
11235             this.fireEvent('actionfailed', this, action);
11236         }
11237
11238     },
11239     /**
11240      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
11241      * @param {String} id The value to search for
11242      * @return Field
11243      */
11244     findField : function(id){
11245         var items = this.getItems();
11246         var field = items.get(id);
11247         if(!field){
11248              items.each(function(f){
11249                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
11250                     field = f;
11251                     return false;
11252                 }
11253                 return true;
11254             });
11255         }
11256         return field || null;
11257     },
11258      /**
11259      * Mark fields in this form invalid in bulk.
11260      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
11261      * @return {BasicForm} this
11262      */
11263     markInvalid : function(errors){
11264         if(errors instanceof Array){
11265             for(var i = 0, len = errors.length; i < len; i++){
11266                 var fieldError = errors[i];
11267                 var f = this.findField(fieldError.id);
11268                 if(f){
11269                     f.markInvalid(fieldError.msg);
11270                 }
11271             }
11272         }else{
11273             var field, id;
11274             for(id in errors){
11275                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
11276                     field.markInvalid(errors[id]);
11277                 }
11278             }
11279         }
11280         //Roo.each(this.childForms || [], function (f) {
11281         //    f.markInvalid(errors);
11282         //});
11283
11284         return this;
11285     },
11286
11287     /**
11288      * Set values for fields in this form in bulk.
11289      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
11290      * @return {BasicForm} this
11291      */
11292     setValues : function(values){
11293         if(values instanceof Array){ // array of objects
11294             for(var i = 0, len = values.length; i < len; i++){
11295                 var v = values[i];
11296                 var f = this.findField(v.id);
11297                 if(f){
11298                     f.setValue(v.value);
11299                     if(this.trackResetOnLoad){
11300                         f.originalValue = f.getValue();
11301                     }
11302                 }
11303             }
11304         }else{ // object hash
11305             var field, id;
11306             for(id in values){
11307                 if(typeof values[id] != 'function' && (field = this.findField(id))){
11308
11309                     if (field.setFromData &&
11310                         field.valueField &&
11311                         field.displayField &&
11312                         // combos' with local stores can
11313                         // be queried via setValue()
11314                         // to set their value..
11315                         (field.store && !field.store.isLocal)
11316                         ) {
11317                         // it's a combo
11318                         var sd = { };
11319                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
11320                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
11321                         field.setFromData(sd);
11322
11323                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
11324                         
11325                         field.setFromData(values);
11326                         
11327                     } else {
11328                         field.setValue(values[id]);
11329                     }
11330
11331
11332                     if(this.trackResetOnLoad){
11333                         field.originalValue = field.getValue();
11334                     }
11335                 }
11336             }
11337         }
11338
11339         //Roo.each(this.childForms || [], function (f) {
11340         //    f.setValues(values);
11341         //});
11342
11343         return this;
11344     },
11345
11346     /**
11347      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
11348      * they are returned as an array.
11349      * @param {Boolean} asString
11350      * @return {Object}
11351      */
11352     getValues : function(asString){
11353         //if (this.childForms) {
11354             // copy values from the child forms
11355         //    Roo.each(this.childForms, function (f) {
11356         //        this.setValues(f.getValues());
11357         //    }, this);
11358         //}
11359
11360
11361
11362         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
11363         if(asString === true){
11364             return fs;
11365         }
11366         return Roo.urlDecode(fs);
11367     },
11368
11369     /**
11370      * Returns the fields in this form as an object with key/value pairs.
11371      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
11372      * @return {Object}
11373      */
11374     getFieldValues : function(with_hidden)
11375     {
11376         var items = this.getItems();
11377         var ret = {};
11378         items.each(function(f){
11379             
11380             if (!f.getName()) {
11381                 return;
11382             }
11383             
11384             var v = f.getValue();
11385             
11386             if (f.inputType =='radio') {
11387                 if (typeof(ret[f.getName()]) == 'undefined') {
11388                     ret[f.getName()] = ''; // empty..
11389                 }
11390
11391                 if (!f.el.dom.checked) {
11392                     return;
11393
11394                 }
11395                 v = f.el.dom.value;
11396
11397             }
11398             
11399             if(f.xtype == 'MoneyField'){
11400                 ret[f.currencyName] = f.getCurrency();
11401             }
11402
11403             // not sure if this supported any more..
11404             if ((typeof(v) == 'object') && f.getRawValue) {
11405                 v = f.getRawValue() ; // dates..
11406             }
11407             // combo boxes where name != hiddenName...
11408             if (f.name !== false && f.name != '' && f.name != f.getName()) {
11409                 ret[f.name] = f.getRawValue();
11410             }
11411             ret[f.getName()] = v;
11412         });
11413
11414         return ret;
11415     },
11416
11417     /**
11418      * Clears all invalid messages in this form.
11419      * @return {BasicForm} this
11420      */
11421     clearInvalid : function(){
11422         var items = this.getItems();
11423
11424         items.each(function(f){
11425            f.clearInvalid();
11426         });
11427
11428         return this;
11429     },
11430
11431     /**
11432      * Resets this form.
11433      * @return {BasicForm} this
11434      */
11435     reset : function(){
11436         var items = this.getItems();
11437         items.each(function(f){
11438             f.reset();
11439         });
11440
11441         Roo.each(this.childForms || [], function (f) {
11442             f.reset();
11443         });
11444
11445
11446         return this;
11447     },
11448     
11449     getItems : function()
11450     {
11451         var r=new Roo.util.MixedCollection(false, function(o){
11452             return o.id || (o.id = Roo.id());
11453         });
11454         var iter = function(el) {
11455             if (el.inputEl) {
11456                 r.add(el);
11457             }
11458             if (!el.items) {
11459                 return;
11460             }
11461             Roo.each(el.items,function(e) {
11462                 iter(e);
11463             });
11464         };
11465
11466         iter(this);
11467         return r;
11468     },
11469     
11470     hideFields : function(items)
11471     {
11472         Roo.each(items, function(i){
11473             
11474             var f = this.findField(i);
11475             
11476             if(!f){
11477                 return;
11478             }
11479             
11480             f.hide();
11481             
11482         }, this);
11483     },
11484     
11485     showFields : function(items)
11486     {
11487         Roo.each(items, function(i){
11488             
11489             var f = this.findField(i);
11490             
11491             if(!f){
11492                 return;
11493             }
11494             
11495             f.show();
11496             
11497         }, this);
11498     }
11499
11500 });
11501
11502 Roo.apply(Roo.bootstrap.Form, {
11503     
11504     popover : {
11505         
11506         padding : 5,
11507         
11508         isApplied : false,
11509         
11510         isMasked : false,
11511         
11512         form : false,
11513         
11514         target : false,
11515         
11516         toolTip : false,
11517         
11518         intervalID : false,
11519         
11520         maskEl : false,
11521         
11522         apply : function()
11523         {
11524             if(this.isApplied){
11525                 return;
11526             }
11527             
11528             this.maskEl = {
11529                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11530                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11531                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11532                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11533             };
11534             
11535             this.maskEl.top.enableDisplayMode("block");
11536             this.maskEl.left.enableDisplayMode("block");
11537             this.maskEl.bottom.enableDisplayMode("block");
11538             this.maskEl.right.enableDisplayMode("block");
11539             
11540             this.toolTip = new Roo.bootstrap.Tooltip({
11541                 cls : 'roo-form-error-popover',
11542                 alignment : {
11543                     'left' : ['r-l', [-2,0], 'right'],
11544                     'right' : ['l-r', [2,0], 'left'],
11545                     'bottom' : ['tl-bl', [0,2], 'top'],
11546                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11547                 }
11548             });
11549             
11550             this.toolTip.render(Roo.get(document.body));
11551
11552             this.toolTip.el.enableDisplayMode("block");
11553             
11554             Roo.get(document.body).on('click', function(){
11555                 this.unmask();
11556             }, this);
11557             
11558             Roo.get(document.body).on('touchstart', function(){
11559                 this.unmask();
11560             }, this);
11561             
11562             this.isApplied = true
11563         },
11564         
11565         mask : function(form, target)
11566         {
11567             this.form = form;
11568             
11569             this.target = target;
11570             
11571             if(!this.form.errorMask || !target.el){
11572                 return;
11573             }
11574             
11575             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11576             
11577             Roo.log(scrollable);
11578             
11579             var ot = this.target.el.calcOffsetsTo(scrollable);
11580             
11581             var scrollTo = ot[1] - this.form.maskOffset;
11582             
11583             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11584             
11585             scrollable.scrollTo('top', scrollTo);
11586             
11587             var box = this.target.el.getBox();
11588             Roo.log(box);
11589             var zIndex = Roo.bootstrap.Modal.zIndex++;
11590
11591             
11592             this.maskEl.top.setStyle('position', 'absolute');
11593             this.maskEl.top.setStyle('z-index', zIndex);
11594             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11595             this.maskEl.top.setLeft(0);
11596             this.maskEl.top.setTop(0);
11597             this.maskEl.top.show();
11598             
11599             this.maskEl.left.setStyle('position', 'absolute');
11600             this.maskEl.left.setStyle('z-index', zIndex);
11601             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11602             this.maskEl.left.setLeft(0);
11603             this.maskEl.left.setTop(box.y - this.padding);
11604             this.maskEl.left.show();
11605
11606             this.maskEl.bottom.setStyle('position', 'absolute');
11607             this.maskEl.bottom.setStyle('z-index', zIndex);
11608             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11609             this.maskEl.bottom.setLeft(0);
11610             this.maskEl.bottom.setTop(box.bottom + this.padding);
11611             this.maskEl.bottom.show();
11612
11613             this.maskEl.right.setStyle('position', 'absolute');
11614             this.maskEl.right.setStyle('z-index', zIndex);
11615             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11616             this.maskEl.right.setLeft(box.right + this.padding);
11617             this.maskEl.right.setTop(box.y - this.padding);
11618             this.maskEl.right.show();
11619
11620             this.toolTip.bindEl = this.target.el;
11621
11622             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11623
11624             var tip = this.target.blankText;
11625
11626             if(this.target.getValue() !== '' ) {
11627                 
11628                 if (this.target.invalidText.length) {
11629                     tip = this.target.invalidText;
11630                 } else if (this.target.regexText.length){
11631                     tip = this.target.regexText;
11632                 }
11633             }
11634
11635             this.toolTip.show(tip);
11636
11637             this.intervalID = window.setInterval(function() {
11638                 Roo.bootstrap.Form.popover.unmask();
11639             }, 10000);
11640
11641             window.onwheel = function(){ return false;};
11642             
11643             (function(){ this.isMasked = true; }).defer(500, this);
11644             
11645         },
11646         
11647         unmask : function()
11648         {
11649             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11650                 return;
11651             }
11652             
11653             this.maskEl.top.setStyle('position', 'absolute');
11654             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11655             this.maskEl.top.hide();
11656
11657             this.maskEl.left.setStyle('position', 'absolute');
11658             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11659             this.maskEl.left.hide();
11660
11661             this.maskEl.bottom.setStyle('position', 'absolute');
11662             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11663             this.maskEl.bottom.hide();
11664
11665             this.maskEl.right.setStyle('position', 'absolute');
11666             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11667             this.maskEl.right.hide();
11668             
11669             this.toolTip.hide();
11670             
11671             this.toolTip.el.hide();
11672             
11673             window.onwheel = function(){ return true;};
11674             
11675             if(this.intervalID){
11676                 window.clearInterval(this.intervalID);
11677                 this.intervalID = false;
11678             }
11679             
11680             this.isMasked = false;
11681             
11682         }
11683         
11684     }
11685     
11686 });
11687
11688 /*
11689  * Based on:
11690  * Ext JS Library 1.1.1
11691  * Copyright(c) 2006-2007, Ext JS, LLC.
11692  *
11693  * Originally Released Under LGPL - original licence link has changed is not relivant.
11694  *
11695  * Fork - LGPL
11696  * <script type="text/javascript">
11697  */
11698 /**
11699  * @class Roo.form.VTypes
11700  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11701  * @singleton
11702  */
11703 Roo.form.VTypes = function(){
11704     // closure these in so they are only created once.
11705     var alpha = /^[a-zA-Z_]+$/;
11706     var alphanum = /^[a-zA-Z0-9_]+$/;
11707     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11708     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11709
11710     // All these messages and functions are configurable
11711     return {
11712         /**
11713          * The function used to validate email addresses
11714          * @param {String} value The email address
11715          */
11716         'email' : function(v){
11717             return email.test(v);
11718         },
11719         /**
11720          * The error text to display when the email validation function returns false
11721          * @type String
11722          */
11723         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11724         /**
11725          * The keystroke filter mask to be applied on email input
11726          * @type RegExp
11727          */
11728         'emailMask' : /[a-z0-9_\.\-@]/i,
11729
11730         /**
11731          * The function used to validate URLs
11732          * @param {String} value The URL
11733          */
11734         'url' : function(v){
11735             return url.test(v);
11736         },
11737         /**
11738          * The error text to display when the url validation function returns false
11739          * @type String
11740          */
11741         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11742         
11743         /**
11744          * The function used to validate alpha values
11745          * @param {String} value The value
11746          */
11747         'alpha' : function(v){
11748             return alpha.test(v);
11749         },
11750         /**
11751          * The error text to display when the alpha validation function returns false
11752          * @type String
11753          */
11754         'alphaText' : 'This field should only contain letters and _',
11755         /**
11756          * The keystroke filter mask to be applied on alpha input
11757          * @type RegExp
11758          */
11759         'alphaMask' : /[a-z_]/i,
11760
11761         /**
11762          * The function used to validate alphanumeric values
11763          * @param {String} value The value
11764          */
11765         'alphanum' : function(v){
11766             return alphanum.test(v);
11767         },
11768         /**
11769          * The error text to display when the alphanumeric validation function returns false
11770          * @type String
11771          */
11772         'alphanumText' : 'This field should only contain letters, numbers and _',
11773         /**
11774          * The keystroke filter mask to be applied on alphanumeric input
11775          * @type RegExp
11776          */
11777         'alphanumMask' : /[a-z0-9_]/i
11778     };
11779 }();/*
11780  * - LGPL
11781  *
11782  * Input
11783  * 
11784  */
11785
11786 /**
11787  * @class Roo.bootstrap.Input
11788  * @extends Roo.bootstrap.Component
11789  * Bootstrap Input class
11790  * @cfg {Boolean} disabled is it disabled
11791  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11792  * @cfg {String} name name of the input
11793  * @cfg {string} fieldLabel - the label associated
11794  * @cfg {string} placeholder - placeholder to put in text.
11795  * @cfg {string}  before - input group add on before
11796  * @cfg {string} after - input group add on after
11797  * @cfg {string} size - (lg|sm) or leave empty..
11798  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11799  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11800  * @cfg {Number} md colspan out of 12 for computer-sized screens
11801  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11802  * @cfg {string} value default value of the input
11803  * @cfg {Number} labelWidth set the width of label 
11804  * @cfg {Number} labellg set the width of label (1-12)
11805  * @cfg {Number} labelmd set the width of label (1-12)
11806  * @cfg {Number} labelsm set the width of label (1-12)
11807  * @cfg {Number} labelxs set the width of label (1-12)
11808  * @cfg {String} labelAlign (top|left)
11809  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11810  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11811  * @cfg {String} indicatorpos (left|right) default left
11812  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11813  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11814  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11815
11816  * @cfg {String} align (left|center|right) Default left
11817  * @cfg {Boolean} forceFeedback (true|false) Default false
11818  * 
11819  * @constructor
11820  * Create a new Input
11821  * @param {Object} config The config object
11822  */
11823
11824 Roo.bootstrap.Input = function(config){
11825     
11826     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11827     
11828     this.addEvents({
11829         /**
11830          * @event focus
11831          * Fires when this field receives input focus.
11832          * @param {Roo.form.Field} this
11833          */
11834         focus : true,
11835         /**
11836          * @event blur
11837          * Fires when this field loses input focus.
11838          * @param {Roo.form.Field} this
11839          */
11840         blur : true,
11841         /**
11842          * @event specialkey
11843          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11844          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11845          * @param {Roo.form.Field} this
11846          * @param {Roo.EventObject} e The event object
11847          */
11848         specialkey : true,
11849         /**
11850          * @event change
11851          * Fires just before the field blurs if the field value has changed.
11852          * @param {Roo.form.Field} this
11853          * @param {Mixed} newValue The new value
11854          * @param {Mixed} oldValue The original value
11855          */
11856         change : true,
11857         /**
11858          * @event invalid
11859          * Fires after the field has been marked as invalid.
11860          * @param {Roo.form.Field} this
11861          * @param {String} msg The validation message
11862          */
11863         invalid : true,
11864         /**
11865          * @event valid
11866          * Fires after the field has been validated with no errors.
11867          * @param {Roo.form.Field} this
11868          */
11869         valid : true,
11870          /**
11871          * @event keyup
11872          * Fires after the key up
11873          * @param {Roo.form.Field} this
11874          * @param {Roo.EventObject}  e The event Object
11875          */
11876         keyup : true,
11877         /**
11878          * @event paste
11879          * Fires after the user pastes into input
11880          * @param {Roo.form.Field} this
11881          * @param {Roo.EventObject}  e The event Object
11882          */
11883         paste : true
11884     });
11885 };
11886
11887 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11888      /**
11889      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11890       automatic validation (defaults to "keyup").
11891      */
11892     validationEvent : "keyup",
11893      /**
11894      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11895      */
11896     validateOnBlur : true,
11897     /**
11898      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11899      */
11900     validationDelay : 250,
11901      /**
11902      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11903      */
11904     focusClass : "x-form-focus",  // not needed???
11905     
11906        
11907     /**
11908      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11909      */
11910     invalidClass : "has-warning",
11911     
11912     /**
11913      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11914      */
11915     validClass : "has-success",
11916     
11917     /**
11918      * @cfg {Boolean} hasFeedback (true|false) default true
11919      */
11920     hasFeedback : true,
11921     
11922     /**
11923      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11924      */
11925     invalidFeedbackClass : "glyphicon-warning-sign",
11926     
11927     /**
11928      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11929      */
11930     validFeedbackClass : "glyphicon-ok",
11931     
11932     /**
11933      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11934      */
11935     selectOnFocus : false,
11936     
11937      /**
11938      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11939      */
11940     maskRe : null,
11941        /**
11942      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11943      */
11944     vtype : null,
11945     
11946       /**
11947      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11948      */
11949     disableKeyFilter : false,
11950     
11951        /**
11952      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11953      */
11954     disabled : false,
11955      /**
11956      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11957      */
11958     allowBlank : true,
11959     /**
11960      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11961      */
11962     blankText : "Please complete this mandatory field",
11963     
11964      /**
11965      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11966      */
11967     minLength : 0,
11968     /**
11969      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11970      */
11971     maxLength : Number.MAX_VALUE,
11972     /**
11973      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11974      */
11975     minLengthText : "The minimum length for this field is {0}",
11976     /**
11977      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11978      */
11979     maxLengthText : "The maximum length for this field is {0}",
11980   
11981     
11982     /**
11983      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11984      * If available, this function will be called only after the basic validators all return true, and will be passed the
11985      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11986      */
11987     validator : null,
11988     /**
11989      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11990      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11991      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11992      */
11993     regex : null,
11994     /**
11995      * @cfg {String} regexText -- Depricated - use Invalid Text
11996      */
11997     regexText : "",
11998     
11999     /**
12000      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
12001      */
12002     invalidText : "",
12003     
12004     
12005     
12006     autocomplete: false,
12007     
12008     
12009     fieldLabel : '',
12010     inputType : 'text',
12011     
12012     name : false,
12013     placeholder: false,
12014     before : false,
12015     after : false,
12016     size : false,
12017     hasFocus : false,
12018     preventMark: false,
12019     isFormField : true,
12020     value : '',
12021     labelWidth : 2,
12022     labelAlign : false,
12023     readOnly : false,
12024     align : false,
12025     formatedValue : false,
12026     forceFeedback : false,
12027     
12028     indicatorpos : 'left',
12029     
12030     labellg : 0,
12031     labelmd : 0,
12032     labelsm : 0,
12033     labelxs : 0,
12034     
12035     capture : '',
12036     accept : '',
12037     
12038     parentLabelAlign : function()
12039     {
12040         var parent = this;
12041         while (parent.parent()) {
12042             parent = parent.parent();
12043             if (typeof(parent.labelAlign) !='undefined') {
12044                 return parent.labelAlign;
12045             }
12046         }
12047         return 'left';
12048         
12049     },
12050     
12051     getAutoCreate : function()
12052     {
12053         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12054         
12055         var id = Roo.id();
12056         
12057         var cfg = {};
12058         
12059         if(this.inputType != 'hidden'){
12060             cfg.cls = 'form-group' //input-group
12061         }
12062         
12063         var input =  {
12064             tag: 'input',
12065             id : id,
12066             type : this.inputType,
12067             value : this.value,
12068             cls : 'form-control',
12069             placeholder : this.placeholder || '',
12070             autocomplete : this.autocomplete || 'new-password'
12071         };
12072         if (this.inputType == 'file') {
12073             input.style = 'overflow:hidden'; // why not in CSS?
12074         }
12075         
12076         if(this.capture.length){
12077             input.capture = this.capture;
12078         }
12079         
12080         if(this.accept.length){
12081             input.accept = this.accept + "/*";
12082         }
12083         
12084         if(this.align){
12085             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
12086         }
12087         
12088         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12089             input.maxLength = this.maxLength;
12090         }
12091         
12092         if (this.disabled) {
12093             input.disabled=true;
12094         }
12095         
12096         if (this.readOnly) {
12097             input.readonly=true;
12098         }
12099         
12100         if (this.name) {
12101             input.name = this.name;
12102         }
12103         
12104         if (this.size) {
12105             input.cls += ' input-' + this.size;
12106         }
12107         
12108         var settings=this;
12109         ['xs','sm','md','lg'].map(function(size){
12110             if (settings[size]) {
12111                 cfg.cls += ' col-' + size + '-' + settings[size];
12112             }
12113         });
12114         
12115         var inputblock = input;
12116         
12117         var feedback = {
12118             tag: 'span',
12119             cls: 'glyphicon form-control-feedback'
12120         };
12121             
12122         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12123             
12124             inputblock = {
12125                 cls : 'has-feedback',
12126                 cn :  [
12127                     input,
12128                     feedback
12129                 ] 
12130             };  
12131         }
12132         
12133         if (this.before || this.after) {
12134             
12135             inputblock = {
12136                 cls : 'input-group',
12137                 cn :  [] 
12138             };
12139             
12140             if (this.before && typeof(this.before) == 'string') {
12141                 
12142                 inputblock.cn.push({
12143                     tag :'span',
12144                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
12145                     html : this.before
12146                 });
12147             }
12148             if (this.before && typeof(this.before) == 'object') {
12149                 this.before = Roo.factory(this.before);
12150                 
12151                 inputblock.cn.push({
12152                     tag :'span',
12153                     cls : 'roo-input-before input-group-prepend   input-group-' +
12154                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12155                 });
12156             }
12157             
12158             inputblock.cn.push(input);
12159             
12160             if (this.after && typeof(this.after) == 'string') {
12161                 inputblock.cn.push({
12162                     tag :'span',
12163                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
12164                     html : this.after
12165                 });
12166             }
12167             if (this.after && typeof(this.after) == 'object') {
12168                 this.after = Roo.factory(this.after);
12169                 
12170                 inputblock.cn.push({
12171                     tag :'span',
12172                     cls : 'roo-input-after input-group-append  input-group-' +
12173                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
12174                 });
12175             }
12176             
12177             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178                 inputblock.cls += ' has-feedback';
12179                 inputblock.cn.push(feedback);
12180             }
12181         };
12182         var indicator = {
12183             tag : 'i',
12184             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12185             tooltip : 'This field is required'
12186         };
12187         if (this.allowBlank ) {
12188             indicator.style = this.allowBlank ? ' display:none' : '';
12189         }
12190         if (align ==='left' && this.fieldLabel.length) {
12191             
12192             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12193             
12194             cfg.cn = [
12195                 indicator,
12196                 {
12197                     tag: 'label',
12198                     'for' :  id,
12199                     cls : 'control-label col-form-label',
12200                     html : this.fieldLabel
12201
12202                 },
12203                 {
12204                     cls : "", 
12205                     cn: [
12206                         inputblock
12207                     ]
12208                 }
12209             ];
12210             
12211             var labelCfg = cfg.cn[1];
12212             var contentCfg = cfg.cn[2];
12213             
12214             if(this.indicatorpos == 'right'){
12215                 cfg.cn = [
12216                     {
12217                         tag: 'label',
12218                         'for' :  id,
12219                         cls : 'control-label col-form-label',
12220                         cn : [
12221                             {
12222                                 tag : 'span',
12223                                 html : this.fieldLabel
12224                             },
12225                             indicator
12226                         ]
12227                     },
12228                     {
12229                         cls : "",
12230                         cn: [
12231                             inputblock
12232                         ]
12233                     }
12234
12235                 ];
12236                 
12237                 labelCfg = cfg.cn[0];
12238                 contentCfg = cfg.cn[1];
12239             
12240             }
12241             
12242             if(this.labelWidth > 12){
12243                 labelCfg.style = "width: " + this.labelWidth + 'px';
12244             }
12245             
12246             if(this.labelWidth < 13 && this.labelmd == 0){
12247                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
12248             }
12249             
12250             if(this.labellg > 0){
12251                 labelCfg.cls += ' col-lg-' + this.labellg;
12252                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12253             }
12254             
12255             if(this.labelmd > 0){
12256                 labelCfg.cls += ' col-md-' + this.labelmd;
12257                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12258             }
12259             
12260             if(this.labelsm > 0){
12261                 labelCfg.cls += ' col-sm-' + this.labelsm;
12262                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12263             }
12264             
12265             if(this.labelxs > 0){
12266                 labelCfg.cls += ' col-xs-' + this.labelxs;
12267                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12268             }
12269             
12270             
12271         } else if ( this.fieldLabel.length) {
12272                 
12273             
12274             
12275             cfg.cn = [
12276                 {
12277                     tag : 'i',
12278                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
12279                     tooltip : 'This field is required',
12280                     style : this.allowBlank ? ' display:none' : '' 
12281                 },
12282                 {
12283                     tag: 'label',
12284                    //cls : 'input-group-addon',
12285                     html : this.fieldLabel
12286
12287                 },
12288
12289                inputblock
12290
12291            ];
12292            
12293            if(this.indicatorpos == 'right'){
12294        
12295                 cfg.cn = [
12296                     {
12297                         tag: 'label',
12298                        //cls : 'input-group-addon',
12299                         html : this.fieldLabel
12300
12301                     },
12302                     {
12303                         tag : 'i',
12304                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
12305                         tooltip : 'This field is required',
12306                         style : this.allowBlank ? ' display:none' : '' 
12307                     },
12308
12309                    inputblock
12310
12311                ];
12312
12313             }
12314
12315         } else {
12316             
12317             cfg.cn = [
12318
12319                     inputblock
12320
12321             ];
12322                 
12323                 
12324         };
12325         
12326         if (this.parentType === 'Navbar' &&  this.parent().bar) {
12327            cfg.cls += ' navbar-form';
12328         }
12329         
12330         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
12331             // on BS4 we do this only if not form 
12332             cfg.cls += ' navbar-form';
12333             cfg.tag = 'li';
12334         }
12335         
12336         return cfg;
12337         
12338     },
12339     /**
12340      * return the real input element.
12341      */
12342     inputEl: function ()
12343     {
12344         return this.el.select('input.form-control',true).first();
12345     },
12346     
12347     tooltipEl : function()
12348     {
12349         return this.inputEl();
12350     },
12351     
12352     indicatorEl : function()
12353     {
12354         if (Roo.bootstrap.version == 4) {
12355             return false; // not enabled in v4 yet.
12356         }
12357         
12358         var indicator = this.el.select('i.roo-required-indicator',true).first();
12359         
12360         if(!indicator){
12361             return false;
12362         }
12363         
12364         return indicator;
12365         
12366     },
12367     
12368     setDisabled : function(v)
12369     {
12370         var i  = this.inputEl().dom;
12371         if (!v) {
12372             i.removeAttribute('disabled');
12373             return;
12374             
12375         }
12376         i.setAttribute('disabled','true');
12377     },
12378     initEvents : function()
12379     {
12380           
12381         this.inputEl().on("keydown" , this.fireKey,  this);
12382         this.inputEl().on("focus", this.onFocus,  this);
12383         this.inputEl().on("blur", this.onBlur,  this);
12384         
12385         this.inputEl().relayEvent('keyup', this);
12386         this.inputEl().relayEvent('paste', this);
12387         
12388         this.indicator = this.indicatorEl();
12389         
12390         if(this.indicator){
12391             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
12392         }
12393  
12394         // reference to original value for reset
12395         this.originalValue = this.getValue();
12396         //Roo.form.TextField.superclass.initEvents.call(this);
12397         if(this.validationEvent == 'keyup'){
12398             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
12399             this.inputEl().on('keyup', this.filterValidation, this);
12400         }
12401         else if(this.validationEvent !== false){
12402             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
12403         }
12404         
12405         if(this.selectOnFocus){
12406             this.on("focus", this.preFocus, this);
12407             
12408         }
12409         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
12410             this.inputEl().on("keypress", this.filterKeys, this);
12411         } else {
12412             this.inputEl().relayEvent('keypress', this);
12413         }
12414        /* if(this.grow){
12415             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
12416             this.el.on("click", this.autoSize,  this);
12417         }
12418         */
12419         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
12420             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
12421         }
12422         
12423         if (typeof(this.before) == 'object') {
12424             this.before.render(this.el.select('.roo-input-before',true).first());
12425         }
12426         if (typeof(this.after) == 'object') {
12427             this.after.render(this.el.select('.roo-input-after',true).first());
12428         }
12429         
12430         this.inputEl().on('change', this.onChange, this);
12431         
12432     },
12433     filterValidation : function(e){
12434         if(!e.isNavKeyPress()){
12435             this.validationTask.delay(this.validationDelay);
12436         }
12437     },
12438      /**
12439      * Validates the field value
12440      * @return {Boolean} True if the value is valid, else false
12441      */
12442     validate : function(){
12443         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
12444         if(this.disabled || this.validateValue(this.getRawValue())){
12445             this.markValid();
12446             return true;
12447         }
12448         
12449         this.markInvalid();
12450         return false;
12451     },
12452     
12453     
12454     /**
12455      * Validates a value according to the field's validation rules and marks the field as invalid
12456      * if the validation fails
12457      * @param {Mixed} value The value to validate
12458      * @return {Boolean} True if the value is valid, else false
12459      */
12460     validateValue : function(value)
12461     {
12462         if(this.getVisibilityEl().hasClass('hidden')){
12463             return true;
12464         }
12465         
12466         if(value.length < 1)  { // if it's blank
12467             if(this.allowBlank){
12468                 return true;
12469             }
12470             return false;
12471         }
12472         
12473         if(value.length < this.minLength){
12474             return false;
12475         }
12476         if(value.length > this.maxLength){
12477             return false;
12478         }
12479         if(this.vtype){
12480             var vt = Roo.form.VTypes;
12481             if(!vt[this.vtype](value, this)){
12482                 return false;
12483             }
12484         }
12485         if(typeof this.validator == "function"){
12486             var msg = this.validator(value);
12487             if(msg !== true){
12488                 return false;
12489             }
12490             if (typeof(msg) == 'string') {
12491                 this.invalidText = msg;
12492             }
12493         }
12494         
12495         if(this.regex && !this.regex.test(value)){
12496             return false;
12497         }
12498         
12499         return true;
12500     },
12501     
12502      // private
12503     fireKey : function(e){
12504         //Roo.log('field ' + e.getKey());
12505         if(e.isNavKeyPress()){
12506             this.fireEvent("specialkey", this, e);
12507         }
12508     },
12509     focus : function (selectText){
12510         if(this.rendered){
12511             this.inputEl().focus();
12512             if(selectText === true){
12513                 this.inputEl().dom.select();
12514             }
12515         }
12516         return this;
12517     } ,
12518     
12519     onFocus : function(){
12520         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12521            // this.el.addClass(this.focusClass);
12522         }
12523         if(!this.hasFocus){
12524             this.hasFocus = true;
12525             this.startValue = this.getValue();
12526             this.fireEvent("focus", this);
12527         }
12528     },
12529     
12530     beforeBlur : Roo.emptyFn,
12531
12532     
12533     // private
12534     onBlur : function(){
12535         this.beforeBlur();
12536         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12537             //this.el.removeClass(this.focusClass);
12538         }
12539         this.hasFocus = false;
12540         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12541             this.validate();
12542         }
12543         var v = this.getValue();
12544         if(String(v) !== String(this.startValue)){
12545             this.fireEvent('change', this, v, this.startValue);
12546         }
12547         this.fireEvent("blur", this);
12548     },
12549     
12550     onChange : function(e)
12551     {
12552         var v = this.getValue();
12553         if(String(v) !== String(this.startValue)){
12554             this.fireEvent('change', this, v, this.startValue);
12555         }
12556         
12557     },
12558     
12559     /**
12560      * Resets the current field value to the originally loaded value and clears any validation messages
12561      */
12562     reset : function(){
12563         this.setValue(this.originalValue);
12564         this.validate();
12565     },
12566      /**
12567      * Returns the name of the field
12568      * @return {Mixed} name The name field
12569      */
12570     getName: function(){
12571         return this.name;
12572     },
12573      /**
12574      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12575      * @return {Mixed} value The field value
12576      */
12577     getValue : function(){
12578         
12579         var v = this.inputEl().getValue();
12580         
12581         return v;
12582     },
12583     /**
12584      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12585      * @return {Mixed} value The field value
12586      */
12587     getRawValue : function(){
12588         var v = this.inputEl().getValue();
12589         
12590         return v;
12591     },
12592     
12593     /**
12594      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12595      * @param {Mixed} value The value to set
12596      */
12597     setRawValue : function(v){
12598         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12599     },
12600     
12601     selectText : function(start, end){
12602         var v = this.getRawValue();
12603         if(v.length > 0){
12604             start = start === undefined ? 0 : start;
12605             end = end === undefined ? v.length : end;
12606             var d = this.inputEl().dom;
12607             if(d.setSelectionRange){
12608                 d.setSelectionRange(start, end);
12609             }else if(d.createTextRange){
12610                 var range = d.createTextRange();
12611                 range.moveStart("character", start);
12612                 range.moveEnd("character", v.length-end);
12613                 range.select();
12614             }
12615         }
12616     },
12617     
12618     /**
12619      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12620      * @param {Mixed} value The value to set
12621      */
12622     setValue : function(v){
12623         this.value = v;
12624         if(this.rendered){
12625             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12626             this.validate();
12627         }
12628     },
12629     
12630     /*
12631     processValue : function(value){
12632         if(this.stripCharsRe){
12633             var newValue = value.replace(this.stripCharsRe, '');
12634             if(newValue !== value){
12635                 this.setRawValue(newValue);
12636                 return newValue;
12637             }
12638         }
12639         return value;
12640     },
12641   */
12642     preFocus : function(){
12643         
12644         if(this.selectOnFocus){
12645             this.inputEl().dom.select();
12646         }
12647     },
12648     filterKeys : function(e){
12649         var k = e.getKey();
12650         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12651             return;
12652         }
12653         var c = e.getCharCode(), cc = String.fromCharCode(c);
12654         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12655             return;
12656         }
12657         if(!this.maskRe.test(cc)){
12658             e.stopEvent();
12659         }
12660     },
12661      /**
12662      * Clear any invalid styles/messages for this field
12663      */
12664     clearInvalid : function(){
12665         
12666         if(!this.el || this.preventMark){ // not rendered
12667             return;
12668         }
12669         
12670         
12671         this.el.removeClass([this.invalidClass, 'is-invalid']);
12672         
12673         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12674             
12675             var feedback = this.el.select('.form-control-feedback', true).first();
12676             
12677             if(feedback){
12678                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12679             }
12680             
12681         }
12682         
12683         if(this.indicator){
12684             this.indicator.removeClass('visible');
12685             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12686         }
12687         
12688         this.fireEvent('valid', this);
12689     },
12690     
12691      /**
12692      * Mark this field as valid
12693      */
12694     markValid : function()
12695     {
12696         if(!this.el  || this.preventMark){ // not rendered...
12697             return;
12698         }
12699         
12700         this.el.removeClass([this.invalidClass, this.validClass]);
12701         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12702
12703         var feedback = this.el.select('.form-control-feedback', true).first();
12704             
12705         if(feedback){
12706             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12707         }
12708         
12709         if(this.indicator){
12710             this.indicator.removeClass('visible');
12711             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12712         }
12713         
12714         if(this.disabled){
12715             return;
12716         }
12717         
12718            
12719         if(this.allowBlank && !this.getRawValue().length){
12720             return;
12721         }
12722         if (Roo.bootstrap.version == 3) {
12723             this.el.addClass(this.validClass);
12724         } else {
12725             this.inputEl().addClass('is-valid');
12726         }
12727
12728         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12729             
12730             var feedback = this.el.select('.form-control-feedback', true).first();
12731             
12732             if(feedback){
12733                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12734                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12735             }
12736             
12737         }
12738         
12739         this.fireEvent('valid', this);
12740     },
12741     
12742      /**
12743      * Mark this field as invalid
12744      * @param {String} msg The validation message
12745      */
12746     markInvalid : function(msg)
12747     {
12748         if(!this.el  || this.preventMark){ // not rendered
12749             return;
12750         }
12751         
12752         this.el.removeClass([this.invalidClass, this.validClass]);
12753         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12754         
12755         var feedback = this.el.select('.form-control-feedback', true).first();
12756             
12757         if(feedback){
12758             this.el.select('.form-control-feedback', true).first().removeClass(
12759                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12760         }
12761
12762         if(this.disabled){
12763             return;
12764         }
12765         
12766         if(this.allowBlank && !this.getRawValue().length){
12767             return;
12768         }
12769         
12770         if(this.indicator){
12771             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12772             this.indicator.addClass('visible');
12773         }
12774         if (Roo.bootstrap.version == 3) {
12775             this.el.addClass(this.invalidClass);
12776         } else {
12777             this.inputEl().addClass('is-invalid');
12778         }
12779         
12780         
12781         
12782         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12783             
12784             var feedback = this.el.select('.form-control-feedback', true).first();
12785             
12786             if(feedback){
12787                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12788                 
12789                 if(this.getValue().length || this.forceFeedback){
12790                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12791                 }
12792                 
12793             }
12794             
12795         }
12796         
12797         this.fireEvent('invalid', this, msg);
12798     },
12799     // private
12800     SafariOnKeyDown : function(event)
12801     {
12802         // this is a workaround for a password hang bug on chrome/ webkit.
12803         if (this.inputEl().dom.type != 'password') {
12804             return;
12805         }
12806         
12807         var isSelectAll = false;
12808         
12809         if(this.inputEl().dom.selectionEnd > 0){
12810             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12811         }
12812         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12813             event.preventDefault();
12814             this.setValue('');
12815             return;
12816         }
12817         
12818         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12819             
12820             event.preventDefault();
12821             // this is very hacky as keydown always get's upper case.
12822             //
12823             var cc = String.fromCharCode(event.getCharCode());
12824             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12825             
12826         }
12827     },
12828     adjustWidth : function(tag, w){
12829         tag = tag.toLowerCase();
12830         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12831             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12832                 if(tag == 'input'){
12833                     return w + 2;
12834                 }
12835                 if(tag == 'textarea'){
12836                     return w-2;
12837                 }
12838             }else if(Roo.isOpera){
12839                 if(tag == 'input'){
12840                     return w + 2;
12841                 }
12842                 if(tag == 'textarea'){
12843                     return w-2;
12844                 }
12845             }
12846         }
12847         return w;
12848     },
12849     
12850     setFieldLabel : function(v)
12851     {
12852         if(!this.rendered){
12853             return;
12854         }
12855         
12856         if(this.indicatorEl()){
12857             var ar = this.el.select('label > span',true);
12858             
12859             if (ar.elements.length) {
12860                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12861                 this.fieldLabel = v;
12862                 return;
12863             }
12864             
12865             var br = this.el.select('label',true);
12866             
12867             if(br.elements.length) {
12868                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12869                 this.fieldLabel = v;
12870                 return;
12871             }
12872             
12873             Roo.log('Cannot Found any of label > span || label in input');
12874             return;
12875         }
12876         
12877         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12878         this.fieldLabel = v;
12879         
12880         
12881     }
12882 });
12883
12884  
12885 /*
12886  * - LGPL
12887  *
12888  * Input
12889  * 
12890  */
12891
12892 /**
12893  * @class Roo.bootstrap.TextArea
12894  * @extends Roo.bootstrap.Input
12895  * Bootstrap TextArea class
12896  * @cfg {Number} cols Specifies the visible width of a text area
12897  * @cfg {Number} rows Specifies the visible number of lines in a text area
12898  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12899  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12900  * @cfg {string} html text
12901  * 
12902  * @constructor
12903  * Create a new TextArea
12904  * @param {Object} config The config object
12905  */
12906
12907 Roo.bootstrap.TextArea = function(config){
12908     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12909    
12910 };
12911
12912 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12913      
12914     cols : false,
12915     rows : 5,
12916     readOnly : false,
12917     warp : 'soft',
12918     resize : false,
12919     value: false,
12920     html: false,
12921     
12922     getAutoCreate : function(){
12923         
12924         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12925         
12926         var id = Roo.id();
12927         
12928         var cfg = {};
12929         
12930         if(this.inputType != 'hidden'){
12931             cfg.cls = 'form-group' //input-group
12932         }
12933         
12934         var input =  {
12935             tag: 'textarea',
12936             id : id,
12937             warp : this.warp,
12938             rows : this.rows,
12939             value : this.value || '',
12940             html: this.html || '',
12941             cls : 'form-control',
12942             placeholder : this.placeholder || '' 
12943             
12944         };
12945         
12946         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12947             input.maxLength = this.maxLength;
12948         }
12949         
12950         if(this.resize){
12951             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12952         }
12953         
12954         if(this.cols){
12955             input.cols = this.cols;
12956         }
12957         
12958         if (this.readOnly) {
12959             input.readonly = true;
12960         }
12961         
12962         if (this.name) {
12963             input.name = this.name;
12964         }
12965         
12966         if (this.size) {
12967             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12968         }
12969         
12970         var settings=this;
12971         ['xs','sm','md','lg'].map(function(size){
12972             if (settings[size]) {
12973                 cfg.cls += ' col-' + size + '-' + settings[size];
12974             }
12975         });
12976         
12977         var inputblock = input;
12978         
12979         if(this.hasFeedback && !this.allowBlank){
12980             
12981             var feedback = {
12982                 tag: 'span',
12983                 cls: 'glyphicon form-control-feedback'
12984             };
12985
12986             inputblock = {
12987                 cls : 'has-feedback',
12988                 cn :  [
12989                     input,
12990                     feedback
12991                 ] 
12992             };  
12993         }
12994         
12995         
12996         if (this.before || this.after) {
12997             
12998             inputblock = {
12999                 cls : 'input-group',
13000                 cn :  [] 
13001             };
13002             if (this.before) {
13003                 inputblock.cn.push({
13004                     tag :'span',
13005                     cls : 'input-group-addon',
13006                     html : this.before
13007                 });
13008             }
13009             
13010             inputblock.cn.push(input);
13011             
13012             if(this.hasFeedback && !this.allowBlank){
13013                 inputblock.cls += ' has-feedback';
13014                 inputblock.cn.push(feedback);
13015             }
13016             
13017             if (this.after) {
13018                 inputblock.cn.push({
13019                     tag :'span',
13020                     cls : 'input-group-addon',
13021                     html : this.after
13022                 });
13023             }
13024             
13025         }
13026         
13027         if (align ==='left' && this.fieldLabel.length) {
13028             cfg.cn = [
13029                 {
13030                     tag: 'label',
13031                     'for' :  id,
13032                     cls : 'control-label',
13033                     html : this.fieldLabel
13034                 },
13035                 {
13036                     cls : "",
13037                     cn: [
13038                         inputblock
13039                     ]
13040                 }
13041
13042             ];
13043             
13044             if(this.labelWidth > 12){
13045                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
13046             }
13047
13048             if(this.labelWidth < 13 && this.labelmd == 0){
13049                 this.labelmd = this.labelWidth;
13050             }
13051
13052             if(this.labellg > 0){
13053                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
13054                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
13055             }
13056
13057             if(this.labelmd > 0){
13058                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
13059                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
13060             }
13061
13062             if(this.labelsm > 0){
13063                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
13064                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
13065             }
13066
13067             if(this.labelxs > 0){
13068                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
13069                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
13070             }
13071             
13072         } else if ( this.fieldLabel.length) {
13073             cfg.cn = [
13074
13075                {
13076                    tag: 'label',
13077                    //cls : 'input-group-addon',
13078                    html : this.fieldLabel
13079
13080                },
13081
13082                inputblock
13083
13084            ];
13085
13086         } else {
13087
13088             cfg.cn = [
13089
13090                 inputblock
13091
13092             ];
13093                 
13094         }
13095         
13096         if (this.disabled) {
13097             input.disabled=true;
13098         }
13099         
13100         return cfg;
13101         
13102     },
13103     /**
13104      * return the real textarea element.
13105      */
13106     inputEl: function ()
13107     {
13108         return this.el.select('textarea.form-control',true).first();
13109     },
13110     
13111     /**
13112      * Clear any invalid styles/messages for this field
13113      */
13114     clearInvalid : function()
13115     {
13116         
13117         if(!this.el || this.preventMark){ // not rendered
13118             return;
13119         }
13120         
13121         var label = this.el.select('label', true).first();
13122         var icon = this.el.select('i.fa-star', true).first();
13123         
13124         if(label && icon){
13125             icon.remove();
13126         }
13127         this.el.removeClass( this.validClass);
13128         this.inputEl().removeClass('is-invalid');
13129          
13130         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13131             
13132             var feedback = this.el.select('.form-control-feedback', true).first();
13133             
13134             if(feedback){
13135                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
13136             }
13137             
13138         }
13139         
13140         this.fireEvent('valid', this);
13141     },
13142     
13143      /**
13144      * Mark this field as valid
13145      */
13146     markValid : function()
13147     {
13148         if(!this.el  || this.preventMark){ // not rendered
13149             return;
13150         }
13151         
13152         this.el.removeClass([this.invalidClass, this.validClass]);
13153         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13154         
13155         var feedback = this.el.select('.form-control-feedback', true).first();
13156             
13157         if(feedback){
13158             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13159         }
13160
13161         if(this.disabled || this.allowBlank){
13162             return;
13163         }
13164         
13165         var label = this.el.select('label', true).first();
13166         var icon = this.el.select('i.fa-star', true).first();
13167         
13168         if(label && icon){
13169             icon.remove();
13170         }
13171         if (Roo.bootstrap.version == 3) {
13172             this.el.addClass(this.validClass);
13173         } else {
13174             this.inputEl().addClass('is-valid');
13175         }
13176         
13177         
13178         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
13179             
13180             var feedback = this.el.select('.form-control-feedback', true).first();
13181             
13182             if(feedback){
13183                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13184                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
13185             }
13186             
13187         }
13188         
13189         this.fireEvent('valid', this);
13190     },
13191     
13192      /**
13193      * Mark this field as invalid
13194      * @param {String} msg The validation message
13195      */
13196     markInvalid : function(msg)
13197     {
13198         if(!this.el  || this.preventMark){ // not rendered
13199             return;
13200         }
13201         
13202         this.el.removeClass([this.invalidClass, this.validClass]);
13203         this.inputEl().removeClass(['is-valid', 'is-invalid']);
13204         
13205         var feedback = this.el.select('.form-control-feedback', true).first();
13206             
13207         if(feedback){
13208             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13209         }
13210
13211         if(this.disabled || this.allowBlank){
13212             return;
13213         }
13214         
13215         var label = this.el.select('label', true).first();
13216         var icon = this.el.select('i.fa-star', true).first();
13217         
13218         if(!this.getValue().length && label && !icon){
13219             this.el.createChild({
13220                 tag : 'i',
13221                 cls : 'text-danger fa fa-lg fa-star',
13222                 tooltip : 'This field is required',
13223                 style : 'margin-right:5px;'
13224             }, label, true);
13225         }
13226         
13227         if (Roo.bootstrap.version == 3) {
13228             this.el.addClass(this.invalidClass);
13229         } else {
13230             this.inputEl().addClass('is-invalid');
13231         }
13232         
13233         // fixme ... this may be depricated need to test..
13234         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
13235             
13236             var feedback = this.el.select('.form-control-feedback', true).first();
13237             
13238             if(feedback){
13239                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
13240                 
13241                 if(this.getValue().length || this.forceFeedback){
13242                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
13243                 }
13244                 
13245             }
13246             
13247         }
13248         
13249         this.fireEvent('invalid', this, msg);
13250     }
13251 });
13252
13253  
13254 /*
13255  * - LGPL
13256  *
13257  * trigger field - base class for combo..
13258  * 
13259  */
13260  
13261 /**
13262  * @class Roo.bootstrap.TriggerField
13263  * @extends Roo.bootstrap.Input
13264  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
13265  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
13266  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
13267  * for which you can provide a custom implementation.  For example:
13268  * <pre><code>
13269 var trigger = new Roo.bootstrap.TriggerField();
13270 trigger.onTriggerClick = myTriggerFn;
13271 trigger.applyTo('my-field');
13272 </code></pre>
13273  *
13274  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
13275  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
13276  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
13277  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
13278  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
13279
13280  * @constructor
13281  * Create a new TriggerField.
13282  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
13283  * to the base TextField)
13284  */
13285 Roo.bootstrap.TriggerField = function(config){
13286     this.mimicing = false;
13287     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
13288 };
13289
13290 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
13291     /**
13292      * @cfg {String} triggerClass A CSS class to apply to the trigger
13293      */
13294      /**
13295      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
13296      */
13297     hideTrigger:false,
13298
13299     /**
13300      * @cfg {Boolean} removable (true|false) special filter default false
13301      */
13302     removable : false,
13303     
13304     /** @cfg {Boolean} grow @hide */
13305     /** @cfg {Number} growMin @hide */
13306     /** @cfg {Number} growMax @hide */
13307
13308     /**
13309      * @hide 
13310      * @method
13311      */
13312     autoSize: Roo.emptyFn,
13313     // private
13314     monitorTab : true,
13315     // private
13316     deferHeight : true,
13317
13318     
13319     actionMode : 'wrap',
13320     
13321     caret : false,
13322     
13323     
13324     getAutoCreate : function(){
13325        
13326         var align = this.labelAlign || this.parentLabelAlign();
13327         
13328         var id = Roo.id();
13329         
13330         var cfg = {
13331             cls: 'form-group' //input-group
13332         };
13333         
13334         
13335         var input =  {
13336             tag: 'input',
13337             id : id,
13338             type : this.inputType,
13339             cls : 'form-control',
13340             autocomplete: 'new-password',
13341             placeholder : this.placeholder || '' 
13342             
13343         };
13344         if (this.name) {
13345             input.name = this.name;
13346         }
13347         if (this.size) {
13348             input.cls += ' input-' + this.size;
13349         }
13350         
13351         if (this.disabled) {
13352             input.disabled=true;
13353         }
13354         
13355         var inputblock = input;
13356         
13357         if(this.hasFeedback && !this.allowBlank){
13358             
13359             var feedback = {
13360                 tag: 'span',
13361                 cls: 'glyphicon form-control-feedback'
13362             };
13363             
13364             if(this.removable && !this.editable  ){
13365                 inputblock = {
13366                     cls : 'has-feedback',
13367                     cn :  [
13368                         inputblock,
13369                         {
13370                             tag: 'button',
13371                             html : 'x',
13372                             cls : 'roo-combo-removable-btn close'
13373                         },
13374                         feedback
13375                     ] 
13376                 };
13377             } else {
13378                 inputblock = {
13379                     cls : 'has-feedback',
13380                     cn :  [
13381                         inputblock,
13382                         feedback
13383                     ] 
13384                 };
13385             }
13386
13387         } else {
13388             if(this.removable && !this.editable ){
13389                 inputblock = {
13390                     cls : 'roo-removable',
13391                     cn :  [
13392                         inputblock,
13393                         {
13394                             tag: 'button',
13395                             html : 'x',
13396                             cls : 'roo-combo-removable-btn close'
13397                         }
13398                     ] 
13399                 };
13400             }
13401         }
13402         
13403         if (this.before || this.after) {
13404             
13405             inputblock = {
13406                 cls : 'input-group',
13407                 cn :  [] 
13408             };
13409             if (this.before) {
13410                 inputblock.cn.push({
13411                     tag :'span',
13412                     cls : 'input-group-addon input-group-prepend input-group-text',
13413                     html : this.before
13414                 });
13415             }
13416             
13417             inputblock.cn.push(input);
13418             
13419             if(this.hasFeedback && !this.allowBlank){
13420                 inputblock.cls += ' has-feedback';
13421                 inputblock.cn.push(feedback);
13422             }
13423             
13424             if (this.after) {
13425                 inputblock.cn.push({
13426                     tag :'span',
13427                     cls : 'input-group-addon input-group-append input-group-text',
13428                     html : this.after
13429                 });
13430             }
13431             
13432         };
13433         
13434       
13435         
13436         var ibwrap = inputblock;
13437         
13438         if(this.multiple){
13439             ibwrap = {
13440                 tag: 'ul',
13441                 cls: 'roo-select2-choices',
13442                 cn:[
13443                     {
13444                         tag: 'li',
13445                         cls: 'roo-select2-search-field',
13446                         cn: [
13447
13448                             inputblock
13449                         ]
13450                     }
13451                 ]
13452             };
13453                 
13454         }
13455         
13456         var combobox = {
13457             cls: 'roo-select2-container input-group',
13458             cn: [
13459                  {
13460                     tag: 'input',
13461                     type : 'hidden',
13462                     cls: 'form-hidden-field'
13463                 },
13464                 ibwrap
13465             ]
13466         };
13467         
13468         if(!this.multiple && this.showToggleBtn){
13469             
13470             var caret = {
13471                         tag: 'span',
13472                         cls: 'caret'
13473              };
13474             if (this.caret != false) {
13475                 caret = {
13476                      tag: 'i',
13477                      cls: 'fa fa-' + this.caret
13478                 };
13479                 
13480             }
13481             
13482             combobox.cn.push({
13483                 tag :'span',
13484                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13485                 cn : [
13486                     Roo.bootstrap.version == 3 ? caret : '',
13487                     {
13488                         tag: 'span',
13489                         cls: 'combobox-clear',
13490                         cn  : [
13491                             {
13492                                 tag : 'i',
13493                                 cls: 'icon-remove'
13494                             }
13495                         ]
13496                     }
13497                 ]
13498
13499             })
13500         }
13501         
13502         if(this.multiple){
13503             combobox.cls += ' roo-select2-container-multi';
13504         }
13505          var indicator = {
13506             tag : 'i',
13507             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13508             tooltip : 'This field is required'
13509         };
13510         if (Roo.bootstrap.version == 4) {
13511             indicator = {
13512                 tag : 'i',
13513                 style : 'display:none'
13514             };
13515         }
13516         
13517         
13518         if (align ==='left' && this.fieldLabel.length) {
13519             
13520             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13521
13522             cfg.cn = [
13523                 indicator,
13524                 {
13525                     tag: 'label',
13526                     'for' :  id,
13527                     cls : 'control-label',
13528                     html : this.fieldLabel
13529
13530                 },
13531                 {
13532                     cls : "", 
13533                     cn: [
13534                         combobox
13535                     ]
13536                 }
13537
13538             ];
13539             
13540             var labelCfg = cfg.cn[1];
13541             var contentCfg = cfg.cn[2];
13542             
13543             if(this.indicatorpos == 'right'){
13544                 cfg.cn = [
13545                     {
13546                         tag: 'label',
13547                         'for' :  id,
13548                         cls : 'control-label',
13549                         cn : [
13550                             {
13551                                 tag : 'span',
13552                                 html : this.fieldLabel
13553                             },
13554                             indicator
13555                         ]
13556                     },
13557                     {
13558                         cls : "", 
13559                         cn: [
13560                             combobox
13561                         ]
13562                     }
13563
13564                 ];
13565                 
13566                 labelCfg = cfg.cn[0];
13567                 contentCfg = cfg.cn[1];
13568             }
13569             
13570             if(this.labelWidth > 12){
13571                 labelCfg.style = "width: " + this.labelWidth + 'px';
13572             }
13573             
13574             if(this.labelWidth < 13 && this.labelmd == 0){
13575                 this.labelmd = this.labelWidth;
13576             }
13577             
13578             if(this.labellg > 0){
13579                 labelCfg.cls += ' col-lg-' + this.labellg;
13580                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13581             }
13582             
13583             if(this.labelmd > 0){
13584                 labelCfg.cls += ' col-md-' + this.labelmd;
13585                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13586             }
13587             
13588             if(this.labelsm > 0){
13589                 labelCfg.cls += ' col-sm-' + this.labelsm;
13590                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13591             }
13592             
13593             if(this.labelxs > 0){
13594                 labelCfg.cls += ' col-xs-' + this.labelxs;
13595                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13596             }
13597             
13598         } else if ( this.fieldLabel.length) {
13599 //                Roo.log(" label");
13600             cfg.cn = [
13601                 indicator,
13602                {
13603                    tag: 'label',
13604                    //cls : 'input-group-addon',
13605                    html : this.fieldLabel
13606
13607                },
13608
13609                combobox
13610
13611             ];
13612             
13613             if(this.indicatorpos == 'right'){
13614                 
13615                 cfg.cn = [
13616                     {
13617                        tag: 'label',
13618                        cn : [
13619                            {
13620                                tag : 'span',
13621                                html : this.fieldLabel
13622                            },
13623                            indicator
13624                        ]
13625
13626                     },
13627                     combobox
13628
13629                 ];
13630
13631             }
13632
13633         } else {
13634             
13635 //                Roo.log(" no label && no align");
13636                 cfg = combobox
13637                      
13638                 
13639         }
13640         
13641         var settings=this;
13642         ['xs','sm','md','lg'].map(function(size){
13643             if (settings[size]) {
13644                 cfg.cls += ' col-' + size + '-' + settings[size];
13645             }
13646         });
13647         
13648         return cfg;
13649         
13650     },
13651     
13652     
13653     
13654     // private
13655     onResize : function(w, h){
13656 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13657 //        if(typeof w == 'number'){
13658 //            var x = w - this.trigger.getWidth();
13659 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13660 //            this.trigger.setStyle('left', x+'px');
13661 //        }
13662     },
13663
13664     // private
13665     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13666
13667     // private
13668     getResizeEl : function(){
13669         return this.inputEl();
13670     },
13671
13672     // private
13673     getPositionEl : function(){
13674         return this.inputEl();
13675     },
13676
13677     // private
13678     alignErrorIcon : function(){
13679         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13680     },
13681
13682     // private
13683     initEvents : function(){
13684         
13685         this.createList();
13686         
13687         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13688         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13689         if(!this.multiple && this.showToggleBtn){
13690             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13691             if(this.hideTrigger){
13692                 this.trigger.setDisplayed(false);
13693             }
13694             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13695         }
13696         
13697         if(this.multiple){
13698             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13699         }
13700         
13701         if(this.removable && !this.editable && !this.tickable){
13702             var close = this.closeTriggerEl();
13703             
13704             if(close){
13705                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13706                 close.on('click', this.removeBtnClick, this, close);
13707             }
13708         }
13709         
13710         //this.trigger.addClassOnOver('x-form-trigger-over');
13711         //this.trigger.addClassOnClick('x-form-trigger-click');
13712         
13713         //if(!this.width){
13714         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13715         //}
13716     },
13717     
13718     closeTriggerEl : function()
13719     {
13720         var close = this.el.select('.roo-combo-removable-btn', true).first();
13721         return close ? close : false;
13722     },
13723     
13724     removeBtnClick : function(e, h, el)
13725     {
13726         e.preventDefault();
13727         
13728         if(this.fireEvent("remove", this) !== false){
13729             this.reset();
13730             this.fireEvent("afterremove", this)
13731         }
13732     },
13733     
13734     createList : function()
13735     {
13736         this.list = Roo.get(document.body).createChild({
13737             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13738             cls: 'typeahead typeahead-long dropdown-menu shadow',
13739             style: 'display:none'
13740         });
13741         
13742         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13743         
13744     },
13745
13746     // private
13747     initTrigger : function(){
13748        
13749     },
13750
13751     // private
13752     onDestroy : function(){
13753         if(this.trigger){
13754             this.trigger.removeAllListeners();
13755           //  this.trigger.remove();
13756         }
13757         //if(this.wrap){
13758         //    this.wrap.remove();
13759         //}
13760         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13761     },
13762
13763     // private
13764     onFocus : function(){
13765         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13766         /*
13767         if(!this.mimicing){
13768             this.wrap.addClass('x-trigger-wrap-focus');
13769             this.mimicing = true;
13770             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13771             if(this.monitorTab){
13772                 this.el.on("keydown", this.checkTab, this);
13773             }
13774         }
13775         */
13776     },
13777
13778     // private
13779     checkTab : function(e){
13780         if(e.getKey() == e.TAB){
13781             this.triggerBlur();
13782         }
13783     },
13784
13785     // private
13786     onBlur : function(){
13787         // do nothing
13788     },
13789
13790     // private
13791     mimicBlur : function(e, t){
13792         /*
13793         if(!this.wrap.contains(t) && this.validateBlur()){
13794             this.triggerBlur();
13795         }
13796         */
13797     },
13798
13799     // private
13800     triggerBlur : function(){
13801         this.mimicing = false;
13802         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13803         if(this.monitorTab){
13804             this.el.un("keydown", this.checkTab, this);
13805         }
13806         //this.wrap.removeClass('x-trigger-wrap-focus');
13807         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13808     },
13809
13810     // private
13811     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13812     validateBlur : function(e, t){
13813         return true;
13814     },
13815
13816     // private
13817     onDisable : function(){
13818         this.inputEl().dom.disabled = true;
13819         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13820         //if(this.wrap){
13821         //    this.wrap.addClass('x-item-disabled');
13822         //}
13823     },
13824
13825     // private
13826     onEnable : function(){
13827         this.inputEl().dom.disabled = false;
13828         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13829         //if(this.wrap){
13830         //    this.el.removeClass('x-item-disabled');
13831         //}
13832     },
13833
13834     // private
13835     onShow : function(){
13836         var ae = this.getActionEl();
13837         
13838         if(ae){
13839             ae.dom.style.display = '';
13840             ae.dom.style.visibility = 'visible';
13841         }
13842     },
13843
13844     // private
13845     
13846     onHide : function(){
13847         var ae = this.getActionEl();
13848         ae.dom.style.display = 'none';
13849     },
13850
13851     /**
13852      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13853      * by an implementing function.
13854      * @method
13855      * @param {EventObject} e
13856      */
13857     onTriggerClick : Roo.emptyFn
13858 });
13859  
13860 /*
13861 * Licence: LGPL
13862 */
13863
13864 /**
13865  * @class Roo.bootstrap.CardUploader
13866  * @extends Roo.bootstrap.Button
13867  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13868  * @cfg {Number} errorTimeout default 3000
13869  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13870  * @cfg {Array}  html The button text.
13871
13872  *
13873  * @constructor
13874  * Create a new CardUploader
13875  * @param {Object} config The config object
13876  */
13877
13878 Roo.bootstrap.CardUploader = function(config){
13879     
13880  
13881     
13882     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13883     
13884     
13885     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13886         return r.data.id
13887      });
13888     
13889      this.addEvents({
13890          // raw events
13891         /**
13892          * @event preview
13893          * When a image is clicked on - and needs to display a slideshow or similar..
13894          * @param {Roo.bootstrap.Card} this
13895          * @param {Object} The image information data 
13896          *
13897          */
13898         'preview' : true,
13899          /**
13900          * @event download
13901          * When a the download link is clicked
13902          * @param {Roo.bootstrap.Card} this
13903          * @param {Object} The image information data  contains 
13904          */
13905         'download' : true
13906         
13907     });
13908 };
13909  
13910 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13911     
13912      
13913     errorTimeout : 3000,
13914      
13915     images : false,
13916    
13917     fileCollection : false,
13918     allowBlank : true,
13919     
13920     getAutoCreate : function()
13921     {
13922         
13923         var cfg =  {
13924             cls :'form-group' ,
13925             cn : [
13926                
13927                 {
13928                     tag: 'label',
13929                    //cls : 'input-group-addon',
13930                     html : this.fieldLabel
13931
13932                 },
13933
13934                 {
13935                     tag: 'input',
13936                     type : 'hidden',
13937                     name : this.name,
13938                     value : this.value,
13939                     cls : 'd-none  form-control'
13940                 },
13941                 
13942                 {
13943                     tag: 'input',
13944                     multiple : 'multiple',
13945                     type : 'file',
13946                     cls : 'd-none  roo-card-upload-selector'
13947                 },
13948                 
13949                 {
13950                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13951                 },
13952                 {
13953                     cls : 'card-columns roo-card-uploader-container'
13954                 }
13955
13956             ]
13957         };
13958            
13959          
13960         return cfg;
13961     },
13962     
13963     getChildContainer : function() /// what children are added to.
13964     {
13965         return this.containerEl;
13966     },
13967    
13968     getButtonContainer : function() /// what children are added to.
13969     {
13970         return this.el.select(".roo-card-uploader-button-container").first();
13971     },
13972    
13973     initEvents : function()
13974     {
13975         
13976         Roo.bootstrap.Input.prototype.initEvents.call(this);
13977         
13978         var t = this;
13979         this.addxtype({
13980             xns: Roo.bootstrap,
13981
13982             xtype : 'Button',
13983             container_method : 'getButtonContainer' ,            
13984             html :  this.html, // fix changable?
13985             cls : 'w-100 ',
13986             listeners : {
13987                 'click' : function(btn, e) {
13988                     t.onClick(e);
13989                 }
13990             }
13991         });
13992         
13993         
13994         
13995         
13996         this.urlAPI = (window.createObjectURL && window) || 
13997                                 (window.URL && URL.revokeObjectURL && URL) || 
13998                                 (window.webkitURL && webkitURL);
13999                         
14000          
14001          
14002          
14003         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
14004         
14005         this.selectorEl.on('change', this.onFileSelected, this);
14006         if (this.images) {
14007             var t = this;
14008             this.images.forEach(function(img) {
14009                 t.addCard(img)
14010             });
14011             this.images = false;
14012         }
14013         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
14014          
14015        
14016     },
14017     
14018    
14019     onClick : function(e)
14020     {
14021         e.preventDefault();
14022          
14023         this.selectorEl.dom.click();
14024          
14025     },
14026     
14027     onFileSelected : function(e)
14028     {
14029         e.preventDefault();
14030         
14031         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
14032             return;
14033         }
14034         
14035         Roo.each(this.selectorEl.dom.files, function(file){    
14036             this.addFile(file);
14037         }, this);
14038          
14039     },
14040     
14041       
14042     
14043       
14044     
14045     addFile : function(file)
14046     {
14047            
14048         if(typeof(file) === 'string'){
14049             throw "Add file by name?"; // should not happen
14050             return;
14051         }
14052         
14053         if(!file || !this.urlAPI){
14054             return;
14055         }
14056         
14057         // file;
14058         // file.type;
14059         
14060         var _this = this;
14061         
14062         
14063         var url = _this.urlAPI.createObjectURL( file);
14064            
14065         this.addCard({
14066             id : Roo.bootstrap.CardUploader.ID--,
14067             is_uploaded : false,
14068             src : url,
14069             srcfile : file,
14070             title : file.name,
14071             mimetype : file.type,
14072             preview : false,
14073             is_deleted : 0
14074         });
14075         
14076     },
14077     
14078     /**
14079      * addCard - add an Attachment to the uploader
14080      * @param data - the data about the image to upload
14081      *
14082      * {
14083           id : 123
14084           title : "Title of file",
14085           is_uploaded : false,
14086           src : "http://.....",
14087           srcfile : { the File upload object },
14088           mimetype : file.type,
14089           preview : false,
14090           is_deleted : 0
14091           .. any other data...
14092         }
14093      *
14094      * 
14095     */
14096     
14097     addCard : function (data)
14098     {
14099         // hidden input element?
14100         // if the file is not an image...
14101         //then we need to use something other that and header_image
14102         var t = this;
14103         //   remove.....
14104         var footer = [
14105             {
14106                 xns : Roo.bootstrap,
14107                 xtype : 'CardFooter',
14108                  items: [
14109                     {
14110                         xns : Roo.bootstrap,
14111                         xtype : 'Element',
14112                         cls : 'd-flex',
14113                         items : [
14114                             
14115                             {
14116                                 xns : Roo.bootstrap,
14117                                 xtype : 'Button',
14118                                 html : String.format("<small>{0}</small>", data.title),
14119                                 cls : 'col-10 text-left',
14120                                 size: 'sm',
14121                                 weight: 'link',
14122                                 fa : 'download',
14123                                 listeners : {
14124                                     click : function() {
14125                                      
14126                                         t.fireEvent( "download", t, data );
14127                                     }
14128                                 }
14129                             },
14130                           
14131                             {
14132                                 xns : Roo.bootstrap,
14133                                 xtype : 'Button',
14134                                 style: 'max-height: 28px; ',
14135                                 size : 'sm',
14136                                 weight: 'danger',
14137                                 cls : 'col-2',
14138                                 fa : 'times',
14139                                 listeners : {
14140                                     click : function() {
14141                                         t.removeCard(data.id)
14142                                     }
14143                                 }
14144                             }
14145                         ]
14146                     }
14147                     
14148                 ] 
14149             }
14150             
14151         ];
14152         
14153         var cn = this.addxtype(
14154             {
14155                  
14156                 xns : Roo.bootstrap,
14157                 xtype : 'Card',
14158                 closeable : true,
14159                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
14160                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
14161                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
14162                 data : data,
14163                 html : false,
14164                  
14165                 items : footer,
14166                 initEvents : function() {
14167                     Roo.bootstrap.Card.prototype.initEvents.call(this);
14168                     var card = this;
14169                     this.imgEl = this.el.select('.card-img-top').first();
14170                     if (this.imgEl) {
14171                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
14172                         this.imgEl.set({ 'pointer' : 'cursor' });
14173                                   
14174                     }
14175                     this.getCardFooter().addClass('p-1');
14176                     
14177                   
14178                 }
14179                 
14180             }
14181         );
14182         // dont' really need ot update items.
14183         // this.items.push(cn);
14184         this.fileCollection.add(cn);
14185         
14186         if (!data.srcfile) {
14187             this.updateInput();
14188             return;
14189         }
14190             
14191         var _t = this;
14192         var reader = new FileReader();
14193         reader.addEventListener("load", function() {  
14194             data.srcdata =  reader.result;
14195             _t.updateInput();
14196         });
14197         reader.readAsDataURL(data.srcfile);
14198         
14199         
14200         
14201     },
14202     removeCard : function(id)
14203     {
14204         
14205         var card  = this.fileCollection.get(id);
14206         card.data.is_deleted = 1;
14207         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
14208         //this.fileCollection.remove(card);
14209         //this.items = this.items.filter(function(e) { return e != card });
14210         // dont' really need ot update items.
14211         card.el.dom.parentNode.removeChild(card.el.dom);
14212         this.updateInput();
14213
14214         
14215     },
14216     reset: function()
14217     {
14218         this.fileCollection.each(function(card) {
14219             if (card.el.dom && card.el.dom.parentNode) {
14220                 card.el.dom.parentNode.removeChild(card.el.dom);
14221             }
14222         });
14223         this.fileCollection.clear();
14224         this.updateInput();
14225     },
14226     
14227     updateInput : function()
14228     {
14229          var data = [];
14230         this.fileCollection.each(function(e) {
14231             data.push(e.data);
14232             
14233         });
14234         this.inputEl().dom.value = JSON.stringify(data);
14235         
14236         
14237         
14238     }
14239     
14240     
14241 });
14242
14243
14244 Roo.bootstrap.CardUploader.ID = -1;/*
14245  * Based on:
14246  * Ext JS Library 1.1.1
14247  * Copyright(c) 2006-2007, Ext JS, LLC.
14248  *
14249  * Originally Released Under LGPL - original licence link has changed is not relivant.
14250  *
14251  * Fork - LGPL
14252  * <script type="text/javascript">
14253  */
14254
14255
14256 /**
14257  * @class Roo.data.SortTypes
14258  * @singleton
14259  * Defines the default sorting (casting?) comparison functions used when sorting data.
14260  */
14261 Roo.data.SortTypes = {
14262     /**
14263      * Default sort that does nothing
14264      * @param {Mixed} s The value being converted
14265      * @return {Mixed} The comparison value
14266      */
14267     none : function(s){
14268         return s;
14269     },
14270     
14271     /**
14272      * The regular expression used to strip tags
14273      * @type {RegExp}
14274      * @property
14275      */
14276     stripTagsRE : /<\/?[^>]+>/gi,
14277     
14278     /**
14279      * Strips all HTML tags to sort on text only
14280      * @param {Mixed} s The value being converted
14281      * @return {String} The comparison value
14282      */
14283     asText : function(s){
14284         return String(s).replace(this.stripTagsRE, "");
14285     },
14286     
14287     /**
14288      * Strips all HTML tags to sort on text only - Case insensitive
14289      * @param {Mixed} s The value being converted
14290      * @return {String} The comparison value
14291      */
14292     asUCText : function(s){
14293         return String(s).toUpperCase().replace(this.stripTagsRE, "");
14294     },
14295     
14296     /**
14297      * Case insensitive string
14298      * @param {Mixed} s The value being converted
14299      * @return {String} The comparison value
14300      */
14301     asUCString : function(s) {
14302         return String(s).toUpperCase();
14303     },
14304     
14305     /**
14306      * Date sorting
14307      * @param {Mixed} s The value being converted
14308      * @return {Number} The comparison value
14309      */
14310     asDate : function(s) {
14311         if(!s){
14312             return 0;
14313         }
14314         if(s instanceof Date){
14315             return s.getTime();
14316         }
14317         return Date.parse(String(s));
14318     },
14319     
14320     /**
14321      * Float sorting
14322      * @param {Mixed} s The value being converted
14323      * @return {Float} The comparison value
14324      */
14325     asFloat : function(s) {
14326         var val = parseFloat(String(s).replace(/,/g, ""));
14327         if(isNaN(val)) {
14328             val = 0;
14329         }
14330         return val;
14331     },
14332     
14333     /**
14334      * Integer sorting
14335      * @param {Mixed} s The value being converted
14336      * @return {Number} The comparison value
14337      */
14338     asInt : function(s) {
14339         var val = parseInt(String(s).replace(/,/g, ""));
14340         if(isNaN(val)) {
14341             val = 0;
14342         }
14343         return val;
14344     }
14345 };/*
14346  * Based on:
14347  * Ext JS Library 1.1.1
14348  * Copyright(c) 2006-2007, Ext JS, LLC.
14349  *
14350  * Originally Released Under LGPL - original licence link has changed is not relivant.
14351  *
14352  * Fork - LGPL
14353  * <script type="text/javascript">
14354  */
14355
14356 /**
14357 * @class Roo.data.Record
14358  * Instances of this class encapsulate both record <em>definition</em> information, and record
14359  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
14360  * to access Records cached in an {@link Roo.data.Store} object.<br>
14361  * <p>
14362  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
14363  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
14364  * objects.<br>
14365  * <p>
14366  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
14367  * @constructor
14368  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
14369  * {@link #create}. The parameters are the same.
14370  * @param {Array} data An associative Array of data values keyed by the field name.
14371  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
14372  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
14373  * not specified an integer id is generated.
14374  */
14375 Roo.data.Record = function(data, id){
14376     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
14377     this.data = data;
14378 };
14379
14380 /**
14381  * Generate a constructor for a specific record layout.
14382  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
14383  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
14384  * Each field definition object may contain the following properties: <ul>
14385  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
14386  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
14387  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
14388  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
14389  * is being used, then this is a string containing the javascript expression to reference the data relative to 
14390  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
14391  * to the data item relative to the record element. If the mapping expression is the same as the field name,
14392  * this may be omitted.</p></li>
14393  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
14394  * <ul><li>auto (Default, implies no conversion)</li>
14395  * <li>string</li>
14396  * <li>int</li>
14397  * <li>float</li>
14398  * <li>boolean</li>
14399  * <li>date</li></ul></p></li>
14400  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
14401  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
14402  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
14403  * by the Reader into an object that will be stored in the Record. It is passed the
14404  * following parameters:<ul>
14405  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
14406  * </ul></p></li>
14407  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
14408  * </ul>
14409  * <br>usage:<br><pre><code>
14410 var TopicRecord = Roo.data.Record.create(
14411     {name: 'title', mapping: 'topic_title'},
14412     {name: 'author', mapping: 'username'},
14413     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
14414     {name: 'lastPost', mapping: 'post_time', type: 'date'},
14415     {name: 'lastPoster', mapping: 'user2'},
14416     {name: 'excerpt', mapping: 'post_text'}
14417 );
14418
14419 var myNewRecord = new TopicRecord({
14420     title: 'Do my job please',
14421     author: 'noobie',
14422     totalPosts: 1,
14423     lastPost: new Date(),
14424     lastPoster: 'Animal',
14425     excerpt: 'No way dude!'
14426 });
14427 myStore.add(myNewRecord);
14428 </code></pre>
14429  * @method create
14430  * @static
14431  */
14432 Roo.data.Record.create = function(o){
14433     var f = function(){
14434         f.superclass.constructor.apply(this, arguments);
14435     };
14436     Roo.extend(f, Roo.data.Record);
14437     var p = f.prototype;
14438     p.fields = new Roo.util.MixedCollection(false, function(field){
14439         return field.name;
14440     });
14441     for(var i = 0, len = o.length; i < len; i++){
14442         p.fields.add(new Roo.data.Field(o[i]));
14443     }
14444     f.getField = function(name){
14445         return p.fields.get(name);  
14446     };
14447     return f;
14448 };
14449
14450 Roo.data.Record.AUTO_ID = 1000;
14451 Roo.data.Record.EDIT = 'edit';
14452 Roo.data.Record.REJECT = 'reject';
14453 Roo.data.Record.COMMIT = 'commit';
14454
14455 Roo.data.Record.prototype = {
14456     /**
14457      * Readonly flag - true if this record has been modified.
14458      * @type Boolean
14459      */
14460     dirty : false,
14461     editing : false,
14462     error: null,
14463     modified: null,
14464
14465     // private
14466     join : function(store){
14467         this.store = store;
14468     },
14469
14470     /**
14471      * Set the named field to the specified value.
14472      * @param {String} name The name of the field to set.
14473      * @param {Object} value The value to set the field to.
14474      */
14475     set : function(name, value){
14476         if(this.data[name] == value){
14477             return;
14478         }
14479         this.dirty = true;
14480         if(!this.modified){
14481             this.modified = {};
14482         }
14483         if(typeof this.modified[name] == 'undefined'){
14484             this.modified[name] = this.data[name];
14485         }
14486         this.data[name] = value;
14487         if(!this.editing && this.store){
14488             this.store.afterEdit(this);
14489         }       
14490     },
14491
14492     /**
14493      * Get the value of the named field.
14494      * @param {String} name The name of the field to get the value of.
14495      * @return {Object} The value of the field.
14496      */
14497     get : function(name){
14498         return this.data[name]; 
14499     },
14500
14501     // private
14502     beginEdit : function(){
14503         this.editing = true;
14504         this.modified = {}; 
14505     },
14506
14507     // private
14508     cancelEdit : function(){
14509         this.editing = false;
14510         delete this.modified;
14511     },
14512
14513     // private
14514     endEdit : function(){
14515         this.editing = false;
14516         if(this.dirty && this.store){
14517             this.store.afterEdit(this);
14518         }
14519     },
14520
14521     /**
14522      * Usually called by the {@link Roo.data.Store} which owns the Record.
14523      * Rejects all changes made to the Record since either creation, or the last commit operation.
14524      * Modified fields are reverted to their original values.
14525      * <p>
14526      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14527      * of reject operations.
14528      */
14529     reject : function(){
14530         var m = this.modified;
14531         for(var n in m){
14532             if(typeof m[n] != "function"){
14533                 this.data[n] = m[n];
14534             }
14535         }
14536         this.dirty = false;
14537         delete this.modified;
14538         this.editing = false;
14539         if(this.store){
14540             this.store.afterReject(this);
14541         }
14542     },
14543
14544     /**
14545      * Usually called by the {@link Roo.data.Store} which owns the Record.
14546      * Commits all changes made to the Record since either creation, or the last commit operation.
14547      * <p>
14548      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14549      * of commit operations.
14550      */
14551     commit : function(){
14552         this.dirty = false;
14553         delete this.modified;
14554         this.editing = false;
14555         if(this.store){
14556             this.store.afterCommit(this);
14557         }
14558     },
14559
14560     // private
14561     hasError : function(){
14562         return this.error != null;
14563     },
14564
14565     // private
14566     clearError : function(){
14567         this.error = null;
14568     },
14569
14570     /**
14571      * Creates a copy of this record.
14572      * @param {String} id (optional) A new record id if you don't want to use this record's id
14573      * @return {Record}
14574      */
14575     copy : function(newId) {
14576         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14577     }
14578 };/*
14579  * Based on:
14580  * Ext JS Library 1.1.1
14581  * Copyright(c) 2006-2007, Ext JS, LLC.
14582  *
14583  * Originally Released Under LGPL - original licence link has changed is not relivant.
14584  *
14585  * Fork - LGPL
14586  * <script type="text/javascript">
14587  */
14588
14589
14590
14591 /**
14592  * @class Roo.data.Store
14593  * @extends Roo.util.Observable
14594  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14595  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14596  * <p>
14597  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
14598  * has no knowledge of the format of the data returned by the Proxy.<br>
14599  * <p>
14600  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14601  * instances from the data object. These records are cached and made available through accessor functions.
14602  * @constructor
14603  * Creates a new Store.
14604  * @param {Object} config A config object containing the objects needed for the Store to access data,
14605  * and read the data into Records.
14606  */
14607 Roo.data.Store = function(config){
14608     this.data = new Roo.util.MixedCollection(false);
14609     this.data.getKey = function(o){
14610         return o.id;
14611     };
14612     this.baseParams = {};
14613     // private
14614     this.paramNames = {
14615         "start" : "start",
14616         "limit" : "limit",
14617         "sort" : "sort",
14618         "dir" : "dir",
14619         "multisort" : "_multisort"
14620     };
14621
14622     if(config && config.data){
14623         this.inlineData = config.data;
14624         delete config.data;
14625     }
14626
14627     Roo.apply(this, config);
14628     
14629     if(this.reader){ // reader passed
14630         this.reader = Roo.factory(this.reader, Roo.data);
14631         this.reader.xmodule = this.xmodule || false;
14632         if(!this.recordType){
14633             this.recordType = this.reader.recordType;
14634         }
14635         if(this.reader.onMetaChange){
14636             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14637         }
14638     }
14639
14640     if(this.recordType){
14641         this.fields = this.recordType.prototype.fields;
14642     }
14643     this.modified = [];
14644
14645     this.addEvents({
14646         /**
14647          * @event datachanged
14648          * Fires when the data cache has changed, and a widget which is using this Store
14649          * as a Record cache should refresh its view.
14650          * @param {Store} this
14651          */
14652         datachanged : true,
14653         /**
14654          * @event metachange
14655          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14656          * @param {Store} this
14657          * @param {Object} meta The JSON metadata
14658          */
14659         metachange : true,
14660         /**
14661          * @event add
14662          * Fires when Records have been added to the Store
14663          * @param {Store} this
14664          * @param {Roo.data.Record[]} records The array of Records added
14665          * @param {Number} index The index at which the record(s) were added
14666          */
14667         add : true,
14668         /**
14669          * @event remove
14670          * Fires when a Record has been removed from the Store
14671          * @param {Store} this
14672          * @param {Roo.data.Record} record The Record that was removed
14673          * @param {Number} index The index at which the record was removed
14674          */
14675         remove : true,
14676         /**
14677          * @event update
14678          * Fires when a Record has been updated
14679          * @param {Store} this
14680          * @param {Roo.data.Record} record The Record that was updated
14681          * @param {String} operation The update operation being performed.  Value may be one of:
14682          * <pre><code>
14683  Roo.data.Record.EDIT
14684  Roo.data.Record.REJECT
14685  Roo.data.Record.COMMIT
14686          * </code></pre>
14687          */
14688         update : true,
14689         /**
14690          * @event clear
14691          * Fires when the data cache has been cleared.
14692          * @param {Store} this
14693          */
14694         clear : true,
14695         /**
14696          * @event beforeload
14697          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14698          * the load action will be canceled.
14699          * @param {Store} this
14700          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14701          */
14702         beforeload : true,
14703         /**
14704          * @event beforeloadadd
14705          * Fires after a new set of Records has been loaded.
14706          * @param {Store} this
14707          * @param {Roo.data.Record[]} records The Records that were loaded
14708          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14709          */
14710         beforeloadadd : true,
14711         /**
14712          * @event load
14713          * Fires after a new set of Records has been loaded, before they are added to the store.
14714          * @param {Store} this
14715          * @param {Roo.data.Record[]} records The Records that were loaded
14716          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14717          * @params {Object} return from reader
14718          */
14719         load : true,
14720         /**
14721          * @event loadexception
14722          * Fires if an exception occurs in the Proxy during loading.
14723          * Called with the signature of the Proxy's "loadexception" event.
14724          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14725          * 
14726          * @param {Proxy} 
14727          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14728          * @param {Object} load options 
14729          * @param {Object} jsonData from your request (normally this contains the Exception)
14730          */
14731         loadexception : true
14732     });
14733     
14734     if(this.proxy){
14735         this.proxy = Roo.factory(this.proxy, Roo.data);
14736         this.proxy.xmodule = this.xmodule || false;
14737         this.relayEvents(this.proxy,  ["loadexception"]);
14738     }
14739     this.sortToggle = {};
14740     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14741
14742     Roo.data.Store.superclass.constructor.call(this);
14743
14744     if(this.inlineData){
14745         this.loadData(this.inlineData);
14746         delete this.inlineData;
14747     }
14748 };
14749
14750 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14751      /**
14752     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14753     * without a remote query - used by combo/forms at present.
14754     */
14755     
14756     /**
14757     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14758     */
14759     /**
14760     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14761     */
14762     /**
14763     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14764     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14765     */
14766     /**
14767     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14768     * on any HTTP request
14769     */
14770     /**
14771     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14772     */
14773     /**
14774     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14775     */
14776     multiSort: false,
14777     /**
14778     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14779     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14780     */
14781     remoteSort : false,
14782
14783     /**
14784     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14785      * loaded or when a record is removed. (defaults to false).
14786     */
14787     pruneModifiedRecords : false,
14788
14789     // private
14790     lastOptions : null,
14791
14792     /**
14793      * Add Records to the Store and fires the add event.
14794      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14795      */
14796     add : function(records){
14797         records = [].concat(records);
14798         for(var i = 0, len = records.length; i < len; i++){
14799             records[i].join(this);
14800         }
14801         var index = this.data.length;
14802         this.data.addAll(records);
14803         this.fireEvent("add", this, records, index);
14804     },
14805
14806     /**
14807      * Remove a Record from the Store and fires the remove event.
14808      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14809      */
14810     remove : function(record){
14811         var index = this.data.indexOf(record);
14812         this.data.removeAt(index);
14813  
14814         if(this.pruneModifiedRecords){
14815             this.modified.remove(record);
14816         }
14817         this.fireEvent("remove", this, record, index);
14818     },
14819
14820     /**
14821      * Remove all Records from the Store and fires the clear event.
14822      */
14823     removeAll : function(){
14824         this.data.clear();
14825         if(this.pruneModifiedRecords){
14826             this.modified = [];
14827         }
14828         this.fireEvent("clear", this);
14829     },
14830
14831     /**
14832      * Inserts Records to the Store at the given index and fires the add event.
14833      * @param {Number} index The start index at which to insert the passed Records.
14834      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14835      */
14836     insert : function(index, records){
14837         records = [].concat(records);
14838         for(var i = 0, len = records.length; i < len; i++){
14839             this.data.insert(index, records[i]);
14840             records[i].join(this);
14841         }
14842         this.fireEvent("add", this, records, index);
14843     },
14844
14845     /**
14846      * Get the index within the cache of the passed Record.
14847      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14848      * @return {Number} The index of the passed Record. Returns -1 if not found.
14849      */
14850     indexOf : function(record){
14851         return this.data.indexOf(record);
14852     },
14853
14854     /**
14855      * Get the index within the cache of the Record with the passed id.
14856      * @param {String} id The id of the Record to find.
14857      * @return {Number} The index of the Record. Returns -1 if not found.
14858      */
14859     indexOfId : function(id){
14860         return this.data.indexOfKey(id);
14861     },
14862
14863     /**
14864      * Get the Record with the specified id.
14865      * @param {String} id The id of the Record to find.
14866      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14867      */
14868     getById : function(id){
14869         return this.data.key(id);
14870     },
14871
14872     /**
14873      * Get the Record at the specified index.
14874      * @param {Number} index The index of the Record to find.
14875      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14876      */
14877     getAt : function(index){
14878         return this.data.itemAt(index);
14879     },
14880
14881     /**
14882      * Returns a range of Records between specified indices.
14883      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14884      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14885      * @return {Roo.data.Record[]} An array of Records
14886      */
14887     getRange : function(start, end){
14888         return this.data.getRange(start, end);
14889     },
14890
14891     // private
14892     storeOptions : function(o){
14893         o = Roo.apply({}, o);
14894         delete o.callback;
14895         delete o.scope;
14896         this.lastOptions = o;
14897     },
14898
14899     /**
14900      * Loads the Record cache from the configured Proxy using the configured Reader.
14901      * <p>
14902      * If using remote paging, then the first load call must specify the <em>start</em>
14903      * and <em>limit</em> properties in the options.params property to establish the initial
14904      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14905      * <p>
14906      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14907      * and this call will return before the new data has been loaded. Perform any post-processing
14908      * in a callback function, or in a "load" event handler.</strong>
14909      * <p>
14910      * @param {Object} options An object containing properties which control loading options:<ul>
14911      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14912      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14913      * passed the following arguments:<ul>
14914      * <li>r : Roo.data.Record[]</li>
14915      * <li>options: Options object from the load call</li>
14916      * <li>success: Boolean success indicator</li></ul></li>
14917      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14918      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14919      * </ul>
14920      */
14921     load : function(options){
14922         options = options || {};
14923         if(this.fireEvent("beforeload", this, options) !== false){
14924             this.storeOptions(options);
14925             var p = Roo.apply(options.params || {}, this.baseParams);
14926             // if meta was not loaded from remote source.. try requesting it.
14927             if (!this.reader.metaFromRemote) {
14928                 p._requestMeta = 1;
14929             }
14930             if(this.sortInfo && this.remoteSort){
14931                 var pn = this.paramNames;
14932                 p[pn["sort"]] = this.sortInfo.field;
14933                 p[pn["dir"]] = this.sortInfo.direction;
14934             }
14935             if (this.multiSort) {
14936                 var pn = this.paramNames;
14937                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14938             }
14939             
14940             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14941         }
14942     },
14943
14944     /**
14945      * Reloads the Record cache from the configured Proxy using the configured Reader and
14946      * the options from the last load operation performed.
14947      * @param {Object} options (optional) An object containing properties which may override the options
14948      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14949      * the most recently used options are reused).
14950      */
14951     reload : function(options){
14952         this.load(Roo.applyIf(options||{}, this.lastOptions));
14953     },
14954
14955     // private
14956     // Called as a callback by the Reader during a load operation.
14957     loadRecords : function(o, options, success){
14958         if(!o || success === false){
14959             if(success !== false){
14960                 this.fireEvent("load", this, [], options, o);
14961             }
14962             if(options.callback){
14963                 options.callback.call(options.scope || this, [], options, false);
14964             }
14965             return;
14966         }
14967         // if data returned failure - throw an exception.
14968         if (o.success === false) {
14969             // show a message if no listener is registered.
14970             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14971                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14972             }
14973             // loadmask wil be hooked into this..
14974             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14975             return;
14976         }
14977         var r = o.records, t = o.totalRecords || r.length;
14978         
14979         this.fireEvent("beforeloadadd", this, r, options, o);
14980         
14981         if(!options || options.add !== true){
14982             if(this.pruneModifiedRecords){
14983                 this.modified = [];
14984             }
14985             for(var i = 0, len = r.length; i < len; i++){
14986                 r[i].join(this);
14987             }
14988             if(this.snapshot){
14989                 this.data = this.snapshot;
14990                 delete this.snapshot;
14991             }
14992             this.data.clear();
14993             this.data.addAll(r);
14994             this.totalLength = t;
14995             this.applySort();
14996             this.fireEvent("datachanged", this);
14997         }else{
14998             this.totalLength = Math.max(t, this.data.length+r.length);
14999             this.add(r);
15000         }
15001         
15002         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
15003                 
15004             var e = new Roo.data.Record({});
15005
15006             e.set(this.parent.displayField, this.parent.emptyTitle);
15007             e.set(this.parent.valueField, '');
15008
15009             this.insert(0, e);
15010         }
15011             
15012         this.fireEvent("load", this, r, options, o);
15013         if(options.callback){
15014             options.callback.call(options.scope || this, r, options, true);
15015         }
15016     },
15017
15018
15019     /**
15020      * Loads data from a passed data block. A Reader which understands the format of the data
15021      * must have been configured in the constructor.
15022      * @param {Object} data The data block from which to read the Records.  The format of the data expected
15023      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
15024      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
15025      */
15026     loadData : function(o, append){
15027         var r = this.reader.readRecords(o);
15028         this.loadRecords(r, {add: append}, true);
15029     },
15030     
15031      /**
15032      * using 'cn' the nested child reader read the child array into it's child stores.
15033      * @param {Object} rec The record with a 'children array
15034      */
15035     loadDataFromChildren : function(rec)
15036     {
15037         this.loadData(this.reader.toLoadData(rec));
15038     },
15039     
15040
15041     /**
15042      * Gets the number of cached records.
15043      * <p>
15044      * <em>If using paging, this may not be the total size of the dataset. If the data object
15045      * used by the Reader contains the dataset size, then the getTotalCount() function returns
15046      * the data set size</em>
15047      */
15048     getCount : function(){
15049         return this.data.length || 0;
15050     },
15051
15052     /**
15053      * Gets the total number of records in the dataset as returned by the server.
15054      * <p>
15055      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
15056      * the dataset size</em>
15057      */
15058     getTotalCount : function(){
15059         return this.totalLength || 0;
15060     },
15061
15062     /**
15063      * Returns the sort state of the Store as an object with two properties:
15064      * <pre><code>
15065  field {String} The name of the field by which the Records are sorted
15066  direction {String} The sort order, "ASC" or "DESC"
15067      * </code></pre>
15068      */
15069     getSortState : function(){
15070         return this.sortInfo;
15071     },
15072
15073     // private
15074     applySort : function(){
15075         if(this.sortInfo && !this.remoteSort){
15076             var s = this.sortInfo, f = s.field;
15077             var st = this.fields.get(f).sortType;
15078             var fn = function(r1, r2){
15079                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
15080                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
15081             };
15082             this.data.sort(s.direction, fn);
15083             if(this.snapshot && this.snapshot != this.data){
15084                 this.snapshot.sort(s.direction, fn);
15085             }
15086         }
15087     },
15088
15089     /**
15090      * Sets the default sort column and order to be used by the next load operation.
15091      * @param {String} fieldName The name of the field to sort by.
15092      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15093      */
15094     setDefaultSort : function(field, dir){
15095         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
15096     },
15097
15098     /**
15099      * Sort the Records.
15100      * If remote sorting is used, the sort is performed on the server, and the cache is
15101      * reloaded. If local sorting is used, the cache is sorted internally.
15102      * @param {String} fieldName The name of the field to sort by.
15103      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
15104      */
15105     sort : function(fieldName, dir){
15106         var f = this.fields.get(fieldName);
15107         if(!dir){
15108             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
15109             
15110             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
15111                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
15112             }else{
15113                 dir = f.sortDir;
15114             }
15115         }
15116         this.sortToggle[f.name] = dir;
15117         this.sortInfo = {field: f.name, direction: dir};
15118         if(!this.remoteSort){
15119             this.applySort();
15120             this.fireEvent("datachanged", this);
15121         }else{
15122             this.load(this.lastOptions);
15123         }
15124     },
15125
15126     /**
15127      * Calls the specified function for each of the Records in the cache.
15128      * @param {Function} fn The function to call. The Record is passed as the first parameter.
15129      * Returning <em>false</em> aborts and exits the iteration.
15130      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
15131      */
15132     each : function(fn, scope){
15133         this.data.each(fn, scope);
15134     },
15135
15136     /**
15137      * Gets all records modified since the last commit.  Modified records are persisted across load operations
15138      * (e.g., during paging).
15139      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
15140      */
15141     getModifiedRecords : function(){
15142         return this.modified;
15143     },
15144
15145     // private
15146     createFilterFn : function(property, value, anyMatch){
15147         if(!value.exec){ // not a regex
15148             value = String(value);
15149             if(value.length == 0){
15150                 return false;
15151             }
15152             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
15153         }
15154         return function(r){
15155             return value.test(r.data[property]);
15156         };
15157     },
15158
15159     /**
15160      * Sums the value of <i>property</i> for each record between start and end and returns the result.
15161      * @param {String} property A field on your records
15162      * @param {Number} start The record index to start at (defaults to 0)
15163      * @param {Number} end The last record index to include (defaults to length - 1)
15164      * @return {Number} The sum
15165      */
15166     sum : function(property, start, end){
15167         var rs = this.data.items, v = 0;
15168         start = start || 0;
15169         end = (end || end === 0) ? end : rs.length-1;
15170
15171         for(var i = start; i <= end; i++){
15172             v += (rs[i].data[property] || 0);
15173         }
15174         return v;
15175     },
15176
15177     /**
15178      * Filter the records by a specified property.
15179      * @param {String} field A field on your records
15180      * @param {String/RegExp} value Either a string that the field
15181      * should start with or a RegExp to test against the field
15182      * @param {Boolean} anyMatch True to match any part not just the beginning
15183      */
15184     filter : function(property, value, anyMatch){
15185         var fn = this.createFilterFn(property, value, anyMatch);
15186         return fn ? this.filterBy(fn) : this.clearFilter();
15187     },
15188
15189     /**
15190      * Filter by a function. The specified function will be called with each
15191      * record in this data source. If the function returns true the record is included,
15192      * otherwise it is filtered.
15193      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15194      * @param {Object} scope (optional) The scope of the function (defaults to this)
15195      */
15196     filterBy : function(fn, scope){
15197         this.snapshot = this.snapshot || this.data;
15198         this.data = this.queryBy(fn, scope||this);
15199         this.fireEvent("datachanged", this);
15200     },
15201
15202     /**
15203      * Query the records by a specified property.
15204      * @param {String} field A field on your records
15205      * @param {String/RegExp} value Either a string that the field
15206      * should start with or a RegExp to test against the field
15207      * @param {Boolean} anyMatch True to match any part not just the beginning
15208      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15209      */
15210     query : function(property, value, anyMatch){
15211         var fn = this.createFilterFn(property, value, anyMatch);
15212         return fn ? this.queryBy(fn) : this.data.clone();
15213     },
15214
15215     /**
15216      * Query by a function. The specified function will be called with each
15217      * record in this data source. If the function returns true the record is included
15218      * in the results.
15219      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
15220      * @param {Object} scope (optional) The scope of the function (defaults to this)
15221       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
15222      **/
15223     queryBy : function(fn, scope){
15224         var data = this.snapshot || this.data;
15225         return data.filterBy(fn, scope||this);
15226     },
15227
15228     /**
15229      * Collects unique values for a particular dataIndex from this store.
15230      * @param {String} dataIndex The property to collect
15231      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
15232      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
15233      * @return {Array} An array of the unique values
15234      **/
15235     collect : function(dataIndex, allowNull, bypassFilter){
15236         var d = (bypassFilter === true && this.snapshot) ?
15237                 this.snapshot.items : this.data.items;
15238         var v, sv, r = [], l = {};
15239         for(var i = 0, len = d.length; i < len; i++){
15240             v = d[i].data[dataIndex];
15241             sv = String(v);
15242             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
15243                 l[sv] = true;
15244                 r[r.length] = v;
15245             }
15246         }
15247         return r;
15248     },
15249
15250     /**
15251      * Revert to a view of the Record cache with no filtering applied.
15252      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
15253      */
15254     clearFilter : function(suppressEvent){
15255         if(this.snapshot && this.snapshot != this.data){
15256             this.data = this.snapshot;
15257             delete this.snapshot;
15258             if(suppressEvent !== true){
15259                 this.fireEvent("datachanged", this);
15260             }
15261         }
15262     },
15263
15264     // private
15265     afterEdit : function(record){
15266         if(this.modified.indexOf(record) == -1){
15267             this.modified.push(record);
15268         }
15269         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
15270     },
15271     
15272     // private
15273     afterReject : function(record){
15274         this.modified.remove(record);
15275         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
15276     },
15277
15278     // private
15279     afterCommit : function(record){
15280         this.modified.remove(record);
15281         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
15282     },
15283
15284     /**
15285      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
15286      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
15287      */
15288     commitChanges : function(){
15289         var m = this.modified.slice(0);
15290         this.modified = [];
15291         for(var i = 0, len = m.length; i < len; i++){
15292             m[i].commit();
15293         }
15294     },
15295
15296     /**
15297      * Cancel outstanding changes on all changed records.
15298      */
15299     rejectChanges : function(){
15300         var m = this.modified.slice(0);
15301         this.modified = [];
15302         for(var i = 0, len = m.length; i < len; i++){
15303             m[i].reject();
15304         }
15305     },
15306
15307     onMetaChange : function(meta, rtype, o){
15308         this.recordType = rtype;
15309         this.fields = rtype.prototype.fields;
15310         delete this.snapshot;
15311         this.sortInfo = meta.sortInfo || this.sortInfo;
15312         this.modified = [];
15313         this.fireEvent('metachange', this, this.reader.meta);
15314     },
15315     
15316     moveIndex : function(data, type)
15317     {
15318         var index = this.indexOf(data);
15319         
15320         var newIndex = index + type;
15321         
15322         this.remove(data);
15323         
15324         this.insert(newIndex, data);
15325         
15326     }
15327 });/*
15328  * Based on:
15329  * Ext JS Library 1.1.1
15330  * Copyright(c) 2006-2007, Ext JS, LLC.
15331  *
15332  * Originally Released Under LGPL - original licence link has changed is not relivant.
15333  *
15334  * Fork - LGPL
15335  * <script type="text/javascript">
15336  */
15337
15338 /**
15339  * @class Roo.data.SimpleStore
15340  * @extends Roo.data.Store
15341  * Small helper class to make creating Stores from Array data easier.
15342  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
15343  * @cfg {Array} fields An array of field definition objects, or field name strings.
15344  * @cfg {Object} an existing reader (eg. copied from another store)
15345  * @cfg {Array} data The multi-dimensional array of data
15346  * @constructor
15347  * @param {Object} config
15348  */
15349 Roo.data.SimpleStore = function(config)
15350 {
15351     Roo.data.SimpleStore.superclass.constructor.call(this, {
15352         isLocal : true,
15353         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
15354                 id: config.id
15355             },
15356             Roo.data.Record.create(config.fields)
15357         ),
15358         proxy : new Roo.data.MemoryProxy(config.data)
15359     });
15360     this.load();
15361 };
15362 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
15363  * Based on:
15364  * Ext JS Library 1.1.1
15365  * Copyright(c) 2006-2007, Ext JS, LLC.
15366  *
15367  * Originally Released Under LGPL - original licence link has changed is not relivant.
15368  *
15369  * Fork - LGPL
15370  * <script type="text/javascript">
15371  */
15372
15373 /**
15374 /**
15375  * @extends Roo.data.Store
15376  * @class Roo.data.JsonStore
15377  * Small helper class to make creating Stores for JSON data easier. <br/>
15378 <pre><code>
15379 var store = new Roo.data.JsonStore({
15380     url: 'get-images.php',
15381     root: 'images',
15382     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
15383 });
15384 </code></pre>
15385  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
15386  * JsonReader and HttpProxy (unless inline data is provided).</b>
15387  * @cfg {Array} fields An array of field definition objects, or field name strings.
15388  * @constructor
15389  * @param {Object} config
15390  */
15391 Roo.data.JsonStore = function(c){
15392     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
15393         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
15394         reader: new Roo.data.JsonReader(c, c.fields)
15395     }));
15396 };
15397 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
15398  * Based on:
15399  * Ext JS Library 1.1.1
15400  * Copyright(c) 2006-2007, Ext JS, LLC.
15401  *
15402  * Originally Released Under LGPL - original licence link has changed is not relivant.
15403  *
15404  * Fork - LGPL
15405  * <script type="text/javascript">
15406  */
15407
15408  
15409 Roo.data.Field = function(config){
15410     if(typeof config == "string"){
15411         config = {name: config};
15412     }
15413     Roo.apply(this, config);
15414     
15415     if(!this.type){
15416         this.type = "auto";
15417     }
15418     
15419     var st = Roo.data.SortTypes;
15420     // named sortTypes are supported, here we look them up
15421     if(typeof this.sortType == "string"){
15422         this.sortType = st[this.sortType];
15423     }
15424     
15425     // set default sortType for strings and dates
15426     if(!this.sortType){
15427         switch(this.type){
15428             case "string":
15429                 this.sortType = st.asUCString;
15430                 break;
15431             case "date":
15432                 this.sortType = st.asDate;
15433                 break;
15434             default:
15435                 this.sortType = st.none;
15436         }
15437     }
15438
15439     // define once
15440     var stripRe = /[\$,%]/g;
15441
15442     // prebuilt conversion function for this field, instead of
15443     // switching every time we're reading a value
15444     if(!this.convert){
15445         var cv, dateFormat = this.dateFormat;
15446         switch(this.type){
15447             case "":
15448             case "auto":
15449             case undefined:
15450                 cv = function(v){ return v; };
15451                 break;
15452             case "string":
15453                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
15454                 break;
15455             case "int":
15456                 cv = function(v){
15457                     return v !== undefined && v !== null && v !== '' ?
15458                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15459                     };
15460                 break;
15461             case "float":
15462                 cv = function(v){
15463                     return v !== undefined && v !== null && v !== '' ?
15464                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15465                     };
15466                 break;
15467             case "bool":
15468             case "boolean":
15469                 cv = function(v){ return v === true || v === "true" || v == 1; };
15470                 break;
15471             case "date":
15472                 cv = function(v){
15473                     if(!v){
15474                         return '';
15475                     }
15476                     if(v instanceof Date){
15477                         return v;
15478                     }
15479                     if(dateFormat){
15480                         if(dateFormat == "timestamp"){
15481                             return new Date(v*1000);
15482                         }
15483                         return Date.parseDate(v, dateFormat);
15484                     }
15485                     var parsed = Date.parse(v);
15486                     return parsed ? new Date(parsed) : null;
15487                 };
15488              break;
15489             
15490         }
15491         this.convert = cv;
15492     }
15493 };
15494
15495 Roo.data.Field.prototype = {
15496     dateFormat: null,
15497     defaultValue: "",
15498     mapping: null,
15499     sortType : null,
15500     sortDir : "ASC"
15501 };/*
15502  * Based on:
15503  * Ext JS Library 1.1.1
15504  * Copyright(c) 2006-2007, Ext JS, LLC.
15505  *
15506  * Originally Released Under LGPL - original licence link has changed is not relivant.
15507  *
15508  * Fork - LGPL
15509  * <script type="text/javascript">
15510  */
15511  
15512 // Base class for reading structured data from a data source.  This class is intended to be
15513 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15514
15515 /**
15516  * @class Roo.data.DataReader
15517  * Base class for reading structured data from a data source.  This class is intended to be
15518  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15519  */
15520
15521 Roo.data.DataReader = function(meta, recordType){
15522     
15523     this.meta = meta;
15524     
15525     this.recordType = recordType instanceof Array ? 
15526         Roo.data.Record.create(recordType) : recordType;
15527 };
15528
15529 Roo.data.DataReader.prototype = {
15530     
15531     
15532     readerType : 'Data',
15533      /**
15534      * Create an empty record
15535      * @param {Object} data (optional) - overlay some values
15536      * @return {Roo.data.Record} record created.
15537      */
15538     newRow :  function(d) {
15539         var da =  {};
15540         this.recordType.prototype.fields.each(function(c) {
15541             switch( c.type) {
15542                 case 'int' : da[c.name] = 0; break;
15543                 case 'date' : da[c.name] = new Date(); break;
15544                 case 'float' : da[c.name] = 0.0; break;
15545                 case 'boolean' : da[c.name] = false; break;
15546                 default : da[c.name] = ""; break;
15547             }
15548             
15549         });
15550         return new this.recordType(Roo.apply(da, d));
15551     }
15552     
15553     
15554 };/*
15555  * Based on:
15556  * Ext JS Library 1.1.1
15557  * Copyright(c) 2006-2007, Ext JS, LLC.
15558  *
15559  * Originally Released Under LGPL - original licence link has changed is not relivant.
15560  *
15561  * Fork - LGPL
15562  * <script type="text/javascript">
15563  */
15564
15565 /**
15566  * @class Roo.data.DataProxy
15567  * @extends Roo.data.Observable
15568  * This class is an abstract base class for implementations which provide retrieval of
15569  * unformatted data objects.<br>
15570  * <p>
15571  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15572  * (of the appropriate type which knows how to parse the data object) to provide a block of
15573  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15574  * <p>
15575  * Custom implementations must implement the load method as described in
15576  * {@link Roo.data.HttpProxy#load}.
15577  */
15578 Roo.data.DataProxy = function(){
15579     this.addEvents({
15580         /**
15581          * @event beforeload
15582          * Fires before a network request is made to retrieve a data object.
15583          * @param {Object} This DataProxy object.
15584          * @param {Object} params The params parameter to the load function.
15585          */
15586         beforeload : true,
15587         /**
15588          * @event load
15589          * Fires before the load method's callback is called.
15590          * @param {Object} This DataProxy object.
15591          * @param {Object} o The data object.
15592          * @param {Object} arg The callback argument object passed to the load function.
15593          */
15594         load : true,
15595         /**
15596          * @event loadexception
15597          * Fires if an Exception occurs during data retrieval.
15598          * @param {Object} This DataProxy object.
15599          * @param {Object} o The data object.
15600          * @param {Object} arg The callback argument object passed to the load function.
15601          * @param {Object} e The Exception.
15602          */
15603         loadexception : true
15604     });
15605     Roo.data.DataProxy.superclass.constructor.call(this);
15606 };
15607
15608 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15609
15610     /**
15611      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15612      */
15613 /*
15614  * Based on:
15615  * Ext JS Library 1.1.1
15616  * Copyright(c) 2006-2007, Ext JS, LLC.
15617  *
15618  * Originally Released Under LGPL - original licence link has changed is not relivant.
15619  *
15620  * Fork - LGPL
15621  * <script type="text/javascript">
15622  */
15623 /**
15624  * @class Roo.data.MemoryProxy
15625  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15626  * to the Reader when its load method is called.
15627  * @constructor
15628  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15629  */
15630 Roo.data.MemoryProxy = function(data){
15631     if (data.data) {
15632         data = data.data;
15633     }
15634     Roo.data.MemoryProxy.superclass.constructor.call(this);
15635     this.data = data;
15636 };
15637
15638 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15639     
15640     /**
15641      * Load data from the requested source (in this case an in-memory
15642      * data object passed to the constructor), read the data object into
15643      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15644      * process that block using the passed callback.
15645      * @param {Object} params This parameter is not used by the MemoryProxy class.
15646      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15647      * object into a block of Roo.data.Records.
15648      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15649      * The function must be passed <ul>
15650      * <li>The Record block object</li>
15651      * <li>The "arg" argument from the load function</li>
15652      * <li>A boolean success indicator</li>
15653      * </ul>
15654      * @param {Object} scope The scope in which to call the callback
15655      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15656      */
15657     load : function(params, reader, callback, scope, arg){
15658         params = params || {};
15659         var result;
15660         try {
15661             result = reader.readRecords(params.data ? params.data :this.data);
15662         }catch(e){
15663             this.fireEvent("loadexception", this, arg, null, e);
15664             callback.call(scope, null, arg, false);
15665             return;
15666         }
15667         callback.call(scope, result, arg, true);
15668     },
15669     
15670     // private
15671     update : function(params, records){
15672         
15673     }
15674 });/*
15675  * Based on:
15676  * Ext JS Library 1.1.1
15677  * Copyright(c) 2006-2007, Ext JS, LLC.
15678  *
15679  * Originally Released Under LGPL - original licence link has changed is not relivant.
15680  *
15681  * Fork - LGPL
15682  * <script type="text/javascript">
15683  */
15684 /**
15685  * @class Roo.data.HttpProxy
15686  * @extends Roo.data.DataProxy
15687  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15688  * configured to reference a certain URL.<br><br>
15689  * <p>
15690  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15691  * from which the running page was served.<br><br>
15692  * <p>
15693  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15694  * <p>
15695  * Be aware that to enable the browser to parse an XML document, the server must set
15696  * the Content-Type header in the HTTP response to "text/xml".
15697  * @constructor
15698  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15699  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15700  * will be used to make the request.
15701  */
15702 Roo.data.HttpProxy = function(conn){
15703     Roo.data.HttpProxy.superclass.constructor.call(this);
15704     // is conn a conn config or a real conn?
15705     this.conn = conn;
15706     this.useAjax = !conn || !conn.events;
15707   
15708 };
15709
15710 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15711     // thse are take from connection...
15712     
15713     /**
15714      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15715      */
15716     /**
15717      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15718      * extra parameters to each request made by this object. (defaults to undefined)
15719      */
15720     /**
15721      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15722      *  to each request made by this object. (defaults to undefined)
15723      */
15724     /**
15725      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
15726      */
15727     /**
15728      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15729      */
15730      /**
15731      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15732      * @type Boolean
15733      */
15734   
15735
15736     /**
15737      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15738      * @type Boolean
15739      */
15740     /**
15741      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15742      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15743      * a finer-grained basis than the DataProxy events.
15744      */
15745     getConnection : function(){
15746         return this.useAjax ? Roo.Ajax : this.conn;
15747     },
15748
15749     /**
15750      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15751      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15752      * process that block using the passed callback.
15753      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15754      * for the request to the remote server.
15755      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15756      * object into a block of Roo.data.Records.
15757      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15758      * The function must be passed <ul>
15759      * <li>The Record block object</li>
15760      * <li>The "arg" argument from the load function</li>
15761      * <li>A boolean success indicator</li>
15762      * </ul>
15763      * @param {Object} scope The scope in which to call the callback
15764      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15765      */
15766     load : function(params, reader, callback, scope, arg){
15767         if(this.fireEvent("beforeload", this, params) !== false){
15768             var  o = {
15769                 params : params || {},
15770                 request: {
15771                     callback : callback,
15772                     scope : scope,
15773                     arg : arg
15774                 },
15775                 reader: reader,
15776                 callback : this.loadResponse,
15777                 scope: this
15778             };
15779             if(this.useAjax){
15780                 Roo.applyIf(o, this.conn);
15781                 if(this.activeRequest){
15782                     Roo.Ajax.abort(this.activeRequest);
15783                 }
15784                 this.activeRequest = Roo.Ajax.request(o);
15785             }else{
15786                 this.conn.request(o);
15787             }
15788         }else{
15789             callback.call(scope||this, null, arg, false);
15790         }
15791     },
15792
15793     // private
15794     loadResponse : function(o, success, response){
15795         delete this.activeRequest;
15796         if(!success){
15797             this.fireEvent("loadexception", this, o, response);
15798             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15799             return;
15800         }
15801         var result;
15802         try {
15803             result = o.reader.read(response);
15804         }catch(e){
15805             this.fireEvent("loadexception", this, o, response, e);
15806             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15807             return;
15808         }
15809         
15810         this.fireEvent("load", this, o, o.request.arg);
15811         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15812     },
15813
15814     // private
15815     update : function(dataSet){
15816
15817     },
15818
15819     // private
15820     updateResponse : function(dataSet){
15821
15822     }
15823 });/*
15824  * Based on:
15825  * Ext JS Library 1.1.1
15826  * Copyright(c) 2006-2007, Ext JS, LLC.
15827  *
15828  * Originally Released Under LGPL - original licence link has changed is not relivant.
15829  *
15830  * Fork - LGPL
15831  * <script type="text/javascript">
15832  */
15833
15834 /**
15835  * @class Roo.data.ScriptTagProxy
15836  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15837  * other than the originating domain of the running page.<br><br>
15838  * <p>
15839  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
15840  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15841  * <p>
15842  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15843  * source code that is used as the source inside a &lt;script> tag.<br><br>
15844  * <p>
15845  * In order for the browser to process the returned data, the server must wrap the data object
15846  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15847  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15848  * depending on whether the callback name was passed:
15849  * <p>
15850  * <pre><code>
15851 boolean scriptTag = false;
15852 String cb = request.getParameter("callback");
15853 if (cb != null) {
15854     scriptTag = true;
15855     response.setContentType("text/javascript");
15856 } else {
15857     response.setContentType("application/x-json");
15858 }
15859 Writer out = response.getWriter();
15860 if (scriptTag) {
15861     out.write(cb + "(");
15862 }
15863 out.print(dataBlock.toJsonString());
15864 if (scriptTag) {
15865     out.write(");");
15866 }
15867 </pre></code>
15868  *
15869  * @constructor
15870  * @param {Object} config A configuration object.
15871  */
15872 Roo.data.ScriptTagProxy = function(config){
15873     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15874     Roo.apply(this, config);
15875     this.head = document.getElementsByTagName("head")[0];
15876 };
15877
15878 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15879
15880 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15881     /**
15882      * @cfg {String} url The URL from which to request the data object.
15883      */
15884     /**
15885      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15886      */
15887     timeout : 30000,
15888     /**
15889      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15890      * the server the name of the callback function set up by the load call to process the returned data object.
15891      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15892      * javascript output which calls this named function passing the data object as its only parameter.
15893      */
15894     callbackParam : "callback",
15895     /**
15896      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15897      * name to the request.
15898      */
15899     nocache : true,
15900
15901     /**
15902      * Load data from the configured URL, read the data object into
15903      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15904      * process that block using the passed callback.
15905      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15906      * for the request to the remote server.
15907      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15908      * object into a block of Roo.data.Records.
15909      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15910      * The function must be passed <ul>
15911      * <li>The Record block object</li>
15912      * <li>The "arg" argument from the load function</li>
15913      * <li>A boolean success indicator</li>
15914      * </ul>
15915      * @param {Object} scope The scope in which to call the callback
15916      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15917      */
15918     load : function(params, reader, callback, scope, arg){
15919         if(this.fireEvent("beforeload", this, params) !== false){
15920
15921             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15922
15923             var url = this.url;
15924             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15925             if(this.nocache){
15926                 url += "&_dc=" + (new Date().getTime());
15927             }
15928             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15929             var trans = {
15930                 id : transId,
15931                 cb : "stcCallback"+transId,
15932                 scriptId : "stcScript"+transId,
15933                 params : params,
15934                 arg : arg,
15935                 url : url,
15936                 callback : callback,
15937                 scope : scope,
15938                 reader : reader
15939             };
15940             var conn = this;
15941
15942             window[trans.cb] = function(o){
15943                 conn.handleResponse(o, trans);
15944             };
15945
15946             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15947
15948             if(this.autoAbort !== false){
15949                 this.abort();
15950             }
15951
15952             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15953
15954             var script = document.createElement("script");
15955             script.setAttribute("src", url);
15956             script.setAttribute("type", "text/javascript");
15957             script.setAttribute("id", trans.scriptId);
15958             this.head.appendChild(script);
15959
15960             this.trans = trans;
15961         }else{
15962             callback.call(scope||this, null, arg, false);
15963         }
15964     },
15965
15966     // private
15967     isLoading : function(){
15968         return this.trans ? true : false;
15969     },
15970
15971     /**
15972      * Abort the current server request.
15973      */
15974     abort : function(){
15975         if(this.isLoading()){
15976             this.destroyTrans(this.trans);
15977         }
15978     },
15979
15980     // private
15981     destroyTrans : function(trans, isLoaded){
15982         this.head.removeChild(document.getElementById(trans.scriptId));
15983         clearTimeout(trans.timeoutId);
15984         if(isLoaded){
15985             window[trans.cb] = undefined;
15986             try{
15987                 delete window[trans.cb];
15988             }catch(e){}
15989         }else{
15990             // if hasn't been loaded, wait for load to remove it to prevent script error
15991             window[trans.cb] = function(){
15992                 window[trans.cb] = undefined;
15993                 try{
15994                     delete window[trans.cb];
15995                 }catch(e){}
15996             };
15997         }
15998     },
15999
16000     // private
16001     handleResponse : function(o, trans){
16002         this.trans = false;
16003         this.destroyTrans(trans, true);
16004         var result;
16005         try {
16006             result = trans.reader.readRecords(o);
16007         }catch(e){
16008             this.fireEvent("loadexception", this, o, trans.arg, e);
16009             trans.callback.call(trans.scope||window, null, trans.arg, false);
16010             return;
16011         }
16012         this.fireEvent("load", this, o, trans.arg);
16013         trans.callback.call(trans.scope||window, result, trans.arg, true);
16014     },
16015
16016     // private
16017     handleFailure : function(trans){
16018         this.trans = false;
16019         this.destroyTrans(trans, false);
16020         this.fireEvent("loadexception", this, null, trans.arg);
16021         trans.callback.call(trans.scope||window, null, trans.arg, false);
16022     }
16023 });/*
16024  * Based on:
16025  * Ext JS Library 1.1.1
16026  * Copyright(c) 2006-2007, Ext JS, LLC.
16027  *
16028  * Originally Released Under LGPL - original licence link has changed is not relivant.
16029  *
16030  * Fork - LGPL
16031  * <script type="text/javascript">
16032  */
16033
16034 /**
16035  * @class Roo.data.JsonReader
16036  * @extends Roo.data.DataReader
16037  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
16038  * based on mappings in a provided Roo.data.Record constructor.
16039  * 
16040  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
16041  * in the reply previously. 
16042  * 
16043  * <p>
16044  * Example code:
16045  * <pre><code>
16046 var RecordDef = Roo.data.Record.create([
16047     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
16048     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
16049 ]);
16050 var myReader = new Roo.data.JsonReader({
16051     totalProperty: "results",    // The property which contains the total dataset size (optional)
16052     root: "rows",                // The property which contains an Array of row objects
16053     id: "id"                     // The property within each row object that provides an ID for the record (optional)
16054 }, RecordDef);
16055 </code></pre>
16056  * <p>
16057  * This would consume a JSON file like this:
16058  * <pre><code>
16059 { 'results': 2, 'rows': [
16060     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
16061     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
16062 }
16063 </code></pre>
16064  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
16065  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
16066  * paged from the remote server.
16067  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
16068  * @cfg {String} root name of the property which contains the Array of row objects.
16069  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16070  * @cfg {Array} fields Array of field definition objects
16071  * @constructor
16072  * Create a new JsonReader
16073  * @param {Object} meta Metadata configuration options
16074  * @param {Object} recordType Either an Array of field definition objects,
16075  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
16076  */
16077 Roo.data.JsonReader = function(meta, recordType){
16078     
16079     meta = meta || {};
16080     // set some defaults:
16081     Roo.applyIf(meta, {
16082         totalProperty: 'total',
16083         successProperty : 'success',
16084         root : 'data',
16085         id : 'id'
16086     });
16087     
16088     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16089 };
16090 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
16091     
16092     readerType : 'Json',
16093     
16094     /**
16095      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
16096      * Used by Store query builder to append _requestMeta to params.
16097      * 
16098      */
16099     metaFromRemote : false,
16100     /**
16101      * This method is only used by a DataProxy which has retrieved data from a remote server.
16102      * @param {Object} response The XHR object which contains the JSON data in its responseText.
16103      * @return {Object} data A data block which is used by an Roo.data.Store object as
16104      * a cache of Roo.data.Records.
16105      */
16106     read : function(response){
16107         var json = response.responseText;
16108        
16109         var o = /* eval:var:o */ eval("("+json+")");
16110         if(!o) {
16111             throw {message: "JsonReader.read: Json object not found"};
16112         }
16113         
16114         if(o.metaData){
16115             
16116             delete this.ef;
16117             this.metaFromRemote = true;
16118             this.meta = o.metaData;
16119             this.recordType = Roo.data.Record.create(o.metaData.fields);
16120             this.onMetaChange(this.meta, this.recordType, o);
16121         }
16122         return this.readRecords(o);
16123     },
16124
16125     // private function a store will implement
16126     onMetaChange : function(meta, recordType, o){
16127
16128     },
16129
16130     /**
16131          * @ignore
16132          */
16133     simpleAccess: function(obj, subsc) {
16134         return obj[subsc];
16135     },
16136
16137         /**
16138          * @ignore
16139          */
16140     getJsonAccessor: function(){
16141         var re = /[\[\.]/;
16142         return function(expr) {
16143             try {
16144                 return(re.test(expr))
16145                     ? new Function("obj", "return obj." + expr)
16146                     : function(obj){
16147                         return obj[expr];
16148                     };
16149             } catch(e){}
16150             return Roo.emptyFn;
16151         };
16152     }(),
16153
16154     /**
16155      * Create a data block containing Roo.data.Records from an XML document.
16156      * @param {Object} o An object which contains an Array of row objects in the property specified
16157      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
16158      * which contains the total size of the dataset.
16159      * @return {Object} data A data block which is used by an Roo.data.Store object as
16160      * a cache of Roo.data.Records.
16161      */
16162     readRecords : function(o){
16163         /**
16164          * After any data loads, the raw JSON data is available for further custom processing.
16165          * @type Object
16166          */
16167         this.o = o;
16168         var s = this.meta, Record = this.recordType,
16169             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
16170
16171 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
16172         if (!this.ef) {
16173             if(s.totalProperty) {
16174                     this.getTotal = this.getJsonAccessor(s.totalProperty);
16175                 }
16176                 if(s.successProperty) {
16177                     this.getSuccess = this.getJsonAccessor(s.successProperty);
16178                 }
16179                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
16180                 if (s.id) {
16181                         var g = this.getJsonAccessor(s.id);
16182                         this.getId = function(rec) {
16183                                 var r = g(rec);  
16184                                 return (r === undefined || r === "") ? null : r;
16185                         };
16186                 } else {
16187                         this.getId = function(){return null;};
16188                 }
16189             this.ef = [];
16190             for(var jj = 0; jj < fl; jj++){
16191                 f = fi[jj];
16192                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
16193                 this.ef[jj] = this.getJsonAccessor(map);
16194             }
16195         }
16196
16197         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
16198         if(s.totalProperty){
16199             var vt = parseInt(this.getTotal(o), 10);
16200             if(!isNaN(vt)){
16201                 totalRecords = vt;
16202             }
16203         }
16204         if(s.successProperty){
16205             var vs = this.getSuccess(o);
16206             if(vs === false || vs === 'false'){
16207                 success = false;
16208             }
16209         }
16210         var records = [];
16211         for(var i = 0; i < c; i++){
16212                 var n = root[i];
16213             var values = {};
16214             var id = this.getId(n);
16215             for(var j = 0; j < fl; j++){
16216                 f = fi[j];
16217             var v = this.ef[j](n);
16218             if (!f.convert) {
16219                 Roo.log('missing convert for ' + f.name);
16220                 Roo.log(f);
16221                 continue;
16222             }
16223             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
16224             }
16225             var record = new Record(values, id);
16226             record.json = n;
16227             records[i] = record;
16228         }
16229         return {
16230             raw : o,
16231             success : success,
16232             records : records,
16233             totalRecords : totalRecords
16234         };
16235     },
16236     // used when loading children.. @see loadDataFromChildren
16237     toLoadData: function(rec)
16238     {
16239         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16240         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16241         return { data : data, total : data.length };
16242         
16243     }
16244 });/*
16245  * Based on:
16246  * Ext JS Library 1.1.1
16247  * Copyright(c) 2006-2007, Ext JS, LLC.
16248  *
16249  * Originally Released Under LGPL - original licence link has changed is not relivant.
16250  *
16251  * Fork - LGPL
16252  * <script type="text/javascript">
16253  */
16254
16255 /**
16256  * @class Roo.data.ArrayReader
16257  * @extends Roo.data.DataReader
16258  * Data reader class to create an Array of Roo.data.Record objects from an Array.
16259  * Each element of that Array represents a row of data fields. The
16260  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
16261  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
16262  * <p>
16263  * Example code:.
16264  * <pre><code>
16265 var RecordDef = Roo.data.Record.create([
16266     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
16267     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
16268 ]);
16269 var myReader = new Roo.data.ArrayReader({
16270     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
16271 }, RecordDef);
16272 </code></pre>
16273  * <p>
16274  * This would consume an Array like this:
16275  * <pre><code>
16276 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
16277   </code></pre>
16278  
16279  * @constructor
16280  * Create a new JsonReader
16281  * @param {Object} meta Metadata configuration options.
16282  * @param {Object|Array} recordType Either an Array of field definition objects
16283  * 
16284  * @cfg {Array} fields Array of field definition objects
16285  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
16286  * as specified to {@link Roo.data.Record#create},
16287  * or an {@link Roo.data.Record} object
16288  *
16289  * 
16290  * created using {@link Roo.data.Record#create}.
16291  */
16292 Roo.data.ArrayReader = function(meta, recordType)
16293 {    
16294     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
16295 };
16296
16297 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
16298     
16299       /**
16300      * Create a data block containing Roo.data.Records from an XML document.
16301      * @param {Object} o An Array of row objects which represents the dataset.
16302      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
16303      * a cache of Roo.data.Records.
16304      */
16305     readRecords : function(o)
16306     {
16307         var sid = this.meta ? this.meta.id : null;
16308         var recordType = this.recordType, fields = recordType.prototype.fields;
16309         var records = [];
16310         var root = o;
16311         for(var i = 0; i < root.length; i++){
16312             var n = root[i];
16313             var values = {};
16314             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
16315             for(var j = 0, jlen = fields.length; j < jlen; j++){
16316                 var f = fields.items[j];
16317                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
16318                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
16319                 v = f.convert(v);
16320                 values[f.name] = v;
16321             }
16322             var record = new recordType(values, id);
16323             record.json = n;
16324             records[records.length] = record;
16325         }
16326         return {
16327             records : records,
16328             totalRecords : records.length
16329         };
16330     },
16331     // used when loading children.. @see loadDataFromChildren
16332     toLoadData: function(rec)
16333     {
16334         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
16335         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
16336         
16337     }
16338     
16339     
16340 });/*
16341  * - LGPL
16342  * * 
16343  */
16344
16345 /**
16346  * @class Roo.bootstrap.ComboBox
16347  * @extends Roo.bootstrap.TriggerField
16348  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
16349  * @cfg {Boolean} append (true|false) default false
16350  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
16351  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
16352  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
16353  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
16354  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
16355  * @cfg {Boolean} animate default true
16356  * @cfg {Boolean} emptyResultText only for touch device
16357  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
16358  * @cfg {String} emptyTitle default ''
16359  * @cfg {Number} width fixed with? experimental
16360  * @constructor
16361  * Create a new ComboBox.
16362  * @param {Object} config Configuration options
16363  */
16364 Roo.bootstrap.ComboBox = function(config){
16365     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
16366     this.addEvents({
16367         /**
16368          * @event expand
16369          * Fires when the dropdown list is expanded
16370         * @param {Roo.bootstrap.ComboBox} combo This combo box
16371         */
16372         'expand' : true,
16373         /**
16374          * @event collapse
16375          * Fires when the dropdown list is collapsed
16376         * @param {Roo.bootstrap.ComboBox} combo This combo box
16377         */
16378         'collapse' : true,
16379         /**
16380          * @event beforeselect
16381          * Fires before a list item is selected. Return false to cancel the selection.
16382         * @param {Roo.bootstrap.ComboBox} combo This combo box
16383         * @param {Roo.data.Record} record The data record returned from the underlying store
16384         * @param {Number} index The index of the selected item in the dropdown list
16385         */
16386         'beforeselect' : true,
16387         /**
16388          * @event select
16389          * Fires when a list item is selected
16390         * @param {Roo.bootstrap.ComboBox} combo This combo box
16391         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
16392         * @param {Number} index The index of the selected item in the dropdown list
16393         */
16394         'select' : true,
16395         /**
16396          * @event beforequery
16397          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
16398          * The event object passed has these properties:
16399         * @param {Roo.bootstrap.ComboBox} combo This combo box
16400         * @param {String} query The query
16401         * @param {Boolean} forceAll true to force "all" query
16402         * @param {Boolean} cancel true to cancel the query
16403         * @param {Object} e The query event object
16404         */
16405         'beforequery': true,
16406          /**
16407          * @event add
16408          * Fires when the 'add' icon is pressed (add a listener to enable add button)
16409         * @param {Roo.bootstrap.ComboBox} combo This combo box
16410         */
16411         'add' : true,
16412         /**
16413          * @event edit
16414          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
16415         * @param {Roo.bootstrap.ComboBox} combo This combo box
16416         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
16417         */
16418         'edit' : true,
16419         /**
16420          * @event remove
16421          * Fires when the remove value from the combobox array
16422         * @param {Roo.bootstrap.ComboBox} combo This combo box
16423         */
16424         'remove' : true,
16425         /**
16426          * @event afterremove
16427          * Fires when the remove value from the combobox array
16428         * @param {Roo.bootstrap.ComboBox} combo This combo box
16429         */
16430         'afterremove' : true,
16431         /**
16432          * @event specialfilter
16433          * Fires when specialfilter
16434             * @param {Roo.bootstrap.ComboBox} combo This combo box
16435             */
16436         'specialfilter' : true,
16437         /**
16438          * @event tick
16439          * Fires when tick the element
16440             * @param {Roo.bootstrap.ComboBox} combo This combo box
16441             */
16442         'tick' : true,
16443         /**
16444          * @event touchviewdisplay
16445          * Fires when touch view require special display (default is using displayField)
16446             * @param {Roo.bootstrap.ComboBox} combo This combo box
16447             * @param {Object} cfg set html .
16448             */
16449         'touchviewdisplay' : true
16450         
16451     });
16452     
16453     this.item = [];
16454     this.tickItems = [];
16455     
16456     this.selectedIndex = -1;
16457     if(this.mode == 'local'){
16458         if(config.queryDelay === undefined){
16459             this.queryDelay = 10;
16460         }
16461         if(config.minChars === undefined){
16462             this.minChars = 0;
16463         }
16464     }
16465 };
16466
16467 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16468      
16469     /**
16470      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16471      * rendering into an Roo.Editor, defaults to false)
16472      */
16473     /**
16474      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16475      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16476      */
16477     /**
16478      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16479      */
16480     /**
16481      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16482      * the dropdown list (defaults to undefined, with no header element)
16483      */
16484
16485      /**
16486      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16487      */
16488      
16489      /**
16490      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16491      */
16492     listWidth: undefined,
16493     /**
16494      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16495      * mode = 'remote' or 'text' if mode = 'local')
16496      */
16497     displayField: undefined,
16498     
16499     /**
16500      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16501      * mode = 'remote' or 'value' if mode = 'local'). 
16502      * Note: use of a valueField requires the user make a selection
16503      * in order for a value to be mapped.
16504      */
16505     valueField: undefined,
16506     /**
16507      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16508      */
16509     modalTitle : '',
16510     
16511     /**
16512      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16513      * field's data value (defaults to the underlying DOM element's name)
16514      */
16515     hiddenName: undefined,
16516     /**
16517      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16518      */
16519     listClass: '',
16520     /**
16521      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16522      */
16523     selectedClass: 'active',
16524     
16525     /**
16526      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16527      */
16528     shadow:'sides',
16529     /**
16530      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16531      * anchor positions (defaults to 'tl-bl')
16532      */
16533     listAlign: 'tl-bl?',
16534     /**
16535      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16536      */
16537     maxHeight: 300,
16538     /**
16539      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16540      * query specified by the allQuery config option (defaults to 'query')
16541      */
16542     triggerAction: 'query',
16543     /**
16544      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16545      * (defaults to 4, does not apply if editable = false)
16546      */
16547     minChars : 4,
16548     /**
16549      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16550      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16551      */
16552     typeAhead: false,
16553     /**
16554      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16555      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16556      */
16557     queryDelay: 500,
16558     /**
16559      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16560      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16561      */
16562     pageSize: 0,
16563     /**
16564      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16565      * when editable = true (defaults to false)
16566      */
16567     selectOnFocus:false,
16568     /**
16569      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16570      */
16571     queryParam: 'query',
16572     /**
16573      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16574      * when mode = 'remote' (defaults to 'Loading...')
16575      */
16576     loadingText: 'Loading...',
16577     /**
16578      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16579      */
16580     resizable: false,
16581     /**
16582      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16583      */
16584     handleHeight : 8,
16585     /**
16586      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16587      * traditional select (defaults to true)
16588      */
16589     editable: true,
16590     /**
16591      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16592      */
16593     allQuery: '',
16594     /**
16595      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16596      */
16597     mode: 'remote',
16598     /**
16599      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16600      * listWidth has a higher value)
16601      */
16602     minListWidth : 70,
16603     /**
16604      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16605      * allow the user to set arbitrary text into the field (defaults to false)
16606      */
16607     forceSelection:false,
16608     /**
16609      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16610      * if typeAhead = true (defaults to 250)
16611      */
16612     typeAheadDelay : 250,
16613     /**
16614      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16615      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16616      */
16617     valueNotFoundText : undefined,
16618     /**
16619      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16620      */
16621     blockFocus : false,
16622     
16623     /**
16624      * @cfg {Boolean} disableClear Disable showing of clear button.
16625      */
16626     disableClear : false,
16627     /**
16628      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16629      */
16630     alwaysQuery : false,
16631     
16632     /**
16633      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16634      */
16635     multiple : false,
16636     
16637     /**
16638      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16639      */
16640     invalidClass : "has-warning",
16641     
16642     /**
16643      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16644      */
16645     validClass : "has-success",
16646     
16647     /**
16648      * @cfg {Boolean} specialFilter (true|false) special filter default false
16649      */
16650     specialFilter : false,
16651     
16652     /**
16653      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16654      */
16655     mobileTouchView : true,
16656     
16657     /**
16658      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16659      */
16660     useNativeIOS : false,
16661     
16662     /**
16663      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16664      */
16665     mobile_restrict_height : false,
16666     
16667     ios_options : false,
16668     
16669     //private
16670     addicon : false,
16671     editicon: false,
16672     
16673     page: 0,
16674     hasQuery: false,
16675     append: false,
16676     loadNext: false,
16677     autoFocus : true,
16678     tickable : false,
16679     btnPosition : 'right',
16680     triggerList : true,
16681     showToggleBtn : true,
16682     animate : true,
16683     emptyResultText: 'Empty',
16684     triggerText : 'Select',
16685     emptyTitle : '',
16686     width : false,
16687     
16688     // element that contains real text value.. (when hidden is used..)
16689     
16690     getAutoCreate : function()
16691     {   
16692         var cfg = false;
16693         //render
16694         /*
16695          * Render classic select for iso
16696          */
16697         
16698         if(Roo.isIOS && this.useNativeIOS){
16699             cfg = this.getAutoCreateNativeIOS();
16700             return cfg;
16701         }
16702         
16703         /*
16704          * Touch Devices
16705          */
16706         
16707         if(Roo.isTouch && this.mobileTouchView){
16708             cfg = this.getAutoCreateTouchView();
16709             return cfg;;
16710         }
16711         
16712         /*
16713          *  Normal ComboBox
16714          */
16715         if(!this.tickable){
16716             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16717             return cfg;
16718         }
16719         
16720         /*
16721          *  ComboBox with tickable selections
16722          */
16723              
16724         var align = this.labelAlign || this.parentLabelAlign();
16725         
16726         cfg = {
16727             cls : 'form-group roo-combobox-tickable' //input-group
16728         };
16729         
16730         var btn_text_select = '';
16731         var btn_text_done = '';
16732         var btn_text_cancel = '';
16733         
16734         if (this.btn_text_show) {
16735             btn_text_select = 'Select';
16736             btn_text_done = 'Done';
16737             btn_text_cancel = 'Cancel'; 
16738         }
16739         
16740         var buttons = {
16741             tag : 'div',
16742             cls : 'tickable-buttons',
16743             cn : [
16744                 {
16745                     tag : 'button',
16746                     type : 'button',
16747                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16748                     //html : this.triggerText
16749                     html: btn_text_select
16750                 },
16751                 {
16752                     tag : 'button',
16753                     type : 'button',
16754                     name : 'ok',
16755                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16756                     //html : 'Done'
16757                     html: btn_text_done
16758                 },
16759                 {
16760                     tag : 'button',
16761                     type : 'button',
16762                     name : 'cancel',
16763                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16764                     //html : 'Cancel'
16765                     html: btn_text_cancel
16766                 }
16767             ]
16768         };
16769         
16770         if(this.editable){
16771             buttons.cn.unshift({
16772                 tag: 'input',
16773                 cls: 'roo-select2-search-field-input'
16774             });
16775         }
16776         
16777         var _this = this;
16778         
16779         Roo.each(buttons.cn, function(c){
16780             if (_this.size) {
16781                 c.cls += ' btn-' + _this.size;
16782             }
16783
16784             if (_this.disabled) {
16785                 c.disabled = true;
16786             }
16787         });
16788         
16789         var box = {
16790             tag: 'div',
16791             style : 'display: contents',
16792             cn: [
16793                 {
16794                     tag: 'input',
16795                     type : 'hidden',
16796                     cls: 'form-hidden-field'
16797                 },
16798                 {
16799                     tag: 'ul',
16800                     cls: 'roo-select2-choices',
16801                     cn:[
16802                         {
16803                             tag: 'li',
16804                             cls: 'roo-select2-search-field',
16805                             cn: [
16806                                 buttons
16807                             ]
16808                         }
16809                     ]
16810                 }
16811             ]
16812         };
16813         
16814         var combobox = {
16815             cls: 'roo-select2-container input-group roo-select2-container-multi',
16816             cn: [
16817                 
16818                 box
16819 //                {
16820 //                    tag: 'ul',
16821 //                    cls: 'typeahead typeahead-long dropdown-menu',
16822 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16823 //                }
16824             ]
16825         };
16826         
16827         if(this.hasFeedback && !this.allowBlank){
16828             
16829             var feedback = {
16830                 tag: 'span',
16831                 cls: 'glyphicon form-control-feedback'
16832             };
16833
16834             combobox.cn.push(feedback);
16835         }
16836         
16837         
16838         
16839         var indicator = {
16840             tag : 'i',
16841             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16842             tooltip : 'This field is required'
16843         };
16844         if (Roo.bootstrap.version == 4) {
16845             indicator = {
16846                 tag : 'i',
16847                 style : 'display:none'
16848             };
16849         }
16850         if (align ==='left' && this.fieldLabel.length) {
16851             
16852             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16853             
16854             cfg.cn = [
16855                 indicator,
16856                 {
16857                     tag: 'label',
16858                     'for' :  id,
16859                     cls : 'control-label col-form-label',
16860                     html : this.fieldLabel
16861
16862                 },
16863                 {
16864                     cls : "", 
16865                     cn: [
16866                         combobox
16867                     ]
16868                 }
16869
16870             ];
16871             
16872             var labelCfg = cfg.cn[1];
16873             var contentCfg = cfg.cn[2];
16874             
16875
16876             if(this.indicatorpos == 'right'){
16877                 
16878                 cfg.cn = [
16879                     {
16880                         tag: 'label',
16881                         'for' :  id,
16882                         cls : 'control-label col-form-label',
16883                         cn : [
16884                             {
16885                                 tag : 'span',
16886                                 html : this.fieldLabel
16887                             },
16888                             indicator
16889                         ]
16890                     },
16891                     {
16892                         cls : "",
16893                         cn: [
16894                             combobox
16895                         ]
16896                     }
16897
16898                 ];
16899                 
16900                 
16901                 
16902                 labelCfg = cfg.cn[0];
16903                 contentCfg = cfg.cn[1];
16904             
16905             }
16906             
16907             if(this.labelWidth > 12){
16908                 labelCfg.style = "width: " + this.labelWidth + 'px';
16909             }
16910             if(this.width * 1 > 0){
16911                 contentCfg.style = "width: " + this.width + 'px';
16912             }
16913             if(this.labelWidth < 13 && this.labelmd == 0){
16914                 this.labelmd = this.labelWidth;
16915             }
16916             
16917             if(this.labellg > 0){
16918                 labelCfg.cls += ' col-lg-' + this.labellg;
16919                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16920             }
16921             
16922             if(this.labelmd > 0){
16923                 labelCfg.cls += ' col-md-' + this.labelmd;
16924                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16925             }
16926             
16927             if(this.labelsm > 0){
16928                 labelCfg.cls += ' col-sm-' + this.labelsm;
16929                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16930             }
16931             
16932             if(this.labelxs > 0){
16933                 labelCfg.cls += ' col-xs-' + this.labelxs;
16934                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16935             }
16936                 
16937                 
16938         } else if ( this.fieldLabel.length) {
16939 //                Roo.log(" label");
16940                  cfg.cn = [
16941                    indicator,
16942                     {
16943                         tag: 'label',
16944                         //cls : 'input-group-addon',
16945                         html : this.fieldLabel
16946                     },
16947                     combobox
16948                 ];
16949                 
16950                 if(this.indicatorpos == 'right'){
16951                     cfg.cn = [
16952                         {
16953                             tag: 'label',
16954                             //cls : 'input-group-addon',
16955                             html : this.fieldLabel
16956                         },
16957                         indicator,
16958                         combobox
16959                     ];
16960                     
16961                 }
16962
16963         } else {
16964             
16965 //                Roo.log(" no label && no align");
16966                 cfg = combobox
16967                      
16968                 
16969         }
16970          
16971         var settings=this;
16972         ['xs','sm','md','lg'].map(function(size){
16973             if (settings[size]) {
16974                 cfg.cls += ' col-' + size + '-' + settings[size];
16975             }
16976         });
16977         
16978         return cfg;
16979         
16980     },
16981     
16982     _initEventsCalled : false,
16983     
16984     // private
16985     initEvents: function()
16986     {   
16987         if (this._initEventsCalled) { // as we call render... prevent looping...
16988             return;
16989         }
16990         this._initEventsCalled = true;
16991         
16992         if (!this.store) {
16993             throw "can not find store for combo";
16994         }
16995         
16996         this.indicator = this.indicatorEl();
16997         
16998         this.store = Roo.factory(this.store, Roo.data);
16999         this.store.parent = this;
17000         
17001         // if we are building from html. then this element is so complex, that we can not really
17002         // use the rendered HTML.
17003         // so we have to trash and replace the previous code.
17004         if (Roo.XComponent.build_from_html) {
17005             // remove this element....
17006             var e = this.el.dom, k=0;
17007             while (e ) { e = e.previousSibling;  ++k;}
17008
17009             this.el.remove();
17010             
17011             this.el=false;
17012             this.rendered = false;
17013             
17014             this.render(this.parent().getChildContainer(true), k);
17015         }
17016         
17017         if(Roo.isIOS && this.useNativeIOS){
17018             this.initIOSView();
17019             return;
17020         }
17021         
17022         /*
17023          * Touch Devices
17024          */
17025         
17026         if(Roo.isTouch && this.mobileTouchView){
17027             this.initTouchView();
17028             return;
17029         }
17030         
17031         if(this.tickable){
17032             this.initTickableEvents();
17033             return;
17034         }
17035         
17036         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
17037         
17038         if(this.hiddenName){
17039             
17040             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17041             
17042             this.hiddenField.dom.value =
17043                 this.hiddenValue !== undefined ? this.hiddenValue :
17044                 this.value !== undefined ? this.value : '';
17045
17046             // prevent input submission
17047             this.el.dom.removeAttribute('name');
17048             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17049              
17050              
17051         }
17052         //if(Roo.isGecko){
17053         //    this.el.dom.setAttribute('autocomplete', 'off');
17054         //}
17055         
17056         var cls = 'x-combo-list';
17057         
17058         //this.list = new Roo.Layer({
17059         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
17060         //});
17061         
17062         var _this = this;
17063         
17064         (function(){
17065             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17066             _this.list.setWidth(lw);
17067         }).defer(100);
17068         
17069         this.list.on('mouseover', this.onViewOver, this);
17070         this.list.on('mousemove', this.onViewMove, this);
17071         this.list.on('scroll', this.onViewScroll, this);
17072         
17073         /*
17074         this.list.swallowEvent('mousewheel');
17075         this.assetHeight = 0;
17076
17077         if(this.title){
17078             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
17079             this.assetHeight += this.header.getHeight();
17080         }
17081
17082         this.innerList = this.list.createChild({cls:cls+'-inner'});
17083         this.innerList.on('mouseover', this.onViewOver, this);
17084         this.innerList.on('mousemove', this.onViewMove, this);
17085         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17086         
17087         if(this.allowBlank && !this.pageSize && !this.disableClear){
17088             this.footer = this.list.createChild({cls:cls+'-ft'});
17089             this.pageTb = new Roo.Toolbar(this.footer);
17090            
17091         }
17092         if(this.pageSize){
17093             this.footer = this.list.createChild({cls:cls+'-ft'});
17094             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
17095                     {pageSize: this.pageSize});
17096             
17097         }
17098         
17099         if (this.pageTb && this.allowBlank && !this.disableClear) {
17100             var _this = this;
17101             this.pageTb.add(new Roo.Toolbar.Fill(), {
17102                 cls: 'x-btn-icon x-btn-clear',
17103                 text: '&#160;',
17104                 handler: function()
17105                 {
17106                     _this.collapse();
17107                     _this.clearValue();
17108                     _this.onSelect(false, -1);
17109                 }
17110             });
17111         }
17112         if (this.footer) {
17113             this.assetHeight += this.footer.getHeight();
17114         }
17115         */
17116             
17117         if(!this.tpl){
17118             this.tpl = Roo.bootstrap.version == 4 ?
17119                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
17120                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
17121         }
17122
17123         this.view = new Roo.View(this.list, this.tpl, {
17124             singleSelect:true, store: this.store, selectedClass: this.selectedClass
17125         });
17126         //this.view.wrapEl.setDisplayed(false);
17127         this.view.on('click', this.onViewClick, this);
17128         
17129         
17130         this.store.on('beforeload', this.onBeforeLoad, this);
17131         this.store.on('load', this.onLoad, this);
17132         this.store.on('loadexception', this.onLoadException, this);
17133         /*
17134         if(this.resizable){
17135             this.resizer = new Roo.Resizable(this.list,  {
17136                pinned:true, handles:'se'
17137             });
17138             this.resizer.on('resize', function(r, w, h){
17139                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
17140                 this.listWidth = w;
17141                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
17142                 this.restrictHeight();
17143             }, this);
17144             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
17145         }
17146         */
17147         if(!this.editable){
17148             this.editable = true;
17149             this.setEditable(false);
17150         }
17151         
17152         /*
17153         
17154         if (typeof(this.events.add.listeners) != 'undefined') {
17155             
17156             this.addicon = this.wrap.createChild(
17157                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
17158        
17159             this.addicon.on('click', function(e) {
17160                 this.fireEvent('add', this);
17161             }, this);
17162         }
17163         if (typeof(this.events.edit.listeners) != 'undefined') {
17164             
17165             this.editicon = this.wrap.createChild(
17166                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
17167             if (this.addicon) {
17168                 this.editicon.setStyle('margin-left', '40px');
17169             }
17170             this.editicon.on('click', function(e) {
17171                 
17172                 // we fire even  if inothing is selected..
17173                 this.fireEvent('edit', this, this.lastData );
17174                 
17175             }, this);
17176         }
17177         */
17178         
17179         this.keyNav = new Roo.KeyNav(this.inputEl(), {
17180             "up" : function(e){
17181                 this.inKeyMode = true;
17182                 this.selectPrev();
17183             },
17184
17185             "down" : function(e){
17186                 if(!this.isExpanded()){
17187                     this.onTriggerClick();
17188                 }else{
17189                     this.inKeyMode = true;
17190                     this.selectNext();
17191                 }
17192             },
17193
17194             "enter" : function(e){
17195 //                this.onViewClick();
17196                 //return true;
17197                 this.collapse();
17198                 
17199                 if(this.fireEvent("specialkey", this, e)){
17200                     this.onViewClick(false);
17201                 }
17202                 
17203                 return true;
17204             },
17205
17206             "esc" : function(e){
17207                 this.collapse();
17208             },
17209
17210             "tab" : function(e){
17211                 this.collapse();
17212                 
17213                 if(this.fireEvent("specialkey", this, e)){
17214                     this.onViewClick(false);
17215                 }
17216                 
17217                 return true;
17218             },
17219
17220             scope : this,
17221
17222             doRelay : function(foo, bar, hname){
17223                 if(hname == 'down' || this.scope.isExpanded()){
17224                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17225                 }
17226                 return true;
17227             },
17228
17229             forceKeyDown: true
17230         });
17231         
17232         
17233         this.queryDelay = Math.max(this.queryDelay || 10,
17234                 this.mode == 'local' ? 10 : 250);
17235         
17236         
17237         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17238         
17239         if(this.typeAhead){
17240             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17241         }
17242         if(this.editable !== false){
17243             this.inputEl().on("keyup", this.onKeyUp, this);
17244         }
17245         if(this.forceSelection){
17246             this.inputEl().on('blur', this.doForce, this);
17247         }
17248         
17249         if(this.multiple){
17250             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17251             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17252         }
17253     },
17254     
17255     initTickableEvents: function()
17256     {   
17257         this.createList();
17258         
17259         if(this.hiddenName){
17260             
17261             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17262             
17263             this.hiddenField.dom.value =
17264                 this.hiddenValue !== undefined ? this.hiddenValue :
17265                 this.value !== undefined ? this.value : '';
17266
17267             // prevent input submission
17268             this.el.dom.removeAttribute('name');
17269             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17270              
17271              
17272         }
17273         
17274 //        this.list = this.el.select('ul.dropdown-menu',true).first();
17275         
17276         this.choices = this.el.select('ul.roo-select2-choices', true).first();
17277         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17278         if(this.triggerList){
17279             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
17280         }
17281          
17282         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
17283         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
17284         
17285         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
17286         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
17287         
17288         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
17289         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
17290         
17291         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
17292         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
17293         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
17294         
17295         this.okBtn.hide();
17296         this.cancelBtn.hide();
17297         
17298         var _this = this;
17299         
17300         (function(){
17301             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
17302             _this.list.setWidth(lw);
17303         }).defer(100);
17304         
17305         this.list.on('mouseover', this.onViewOver, this);
17306         this.list.on('mousemove', this.onViewMove, this);
17307         
17308         this.list.on('scroll', this.onViewScroll, this);
17309         
17310         if(!this.tpl){
17311             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
17312                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
17313         }
17314
17315         this.view = new Roo.View(this.list, this.tpl, {
17316             singleSelect:true,
17317             tickable:true,
17318             parent:this,
17319             store: this.store,
17320             selectedClass: this.selectedClass
17321         });
17322         
17323         //this.view.wrapEl.setDisplayed(false);
17324         this.view.on('click', this.onViewClick, this);
17325         
17326         
17327         
17328         this.store.on('beforeload', this.onBeforeLoad, this);
17329         this.store.on('load', this.onLoad, this);
17330         this.store.on('loadexception', this.onLoadException, this);
17331         
17332         if(this.editable){
17333             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
17334                 "up" : function(e){
17335                     this.inKeyMode = true;
17336                     this.selectPrev();
17337                 },
17338
17339                 "down" : function(e){
17340                     this.inKeyMode = true;
17341                     this.selectNext();
17342                 },
17343
17344                 "enter" : function(e){
17345                     if(this.fireEvent("specialkey", this, e)){
17346                         this.onViewClick(false);
17347                     }
17348                     
17349                     return true;
17350                 },
17351
17352                 "esc" : function(e){
17353                     this.onTickableFooterButtonClick(e, false, false);
17354                 },
17355
17356                 "tab" : function(e){
17357                     this.fireEvent("specialkey", this, e);
17358                     
17359                     this.onTickableFooterButtonClick(e, false, false);
17360                     
17361                     return true;
17362                 },
17363
17364                 scope : this,
17365
17366                 doRelay : function(e, fn, key){
17367                     if(this.scope.isExpanded()){
17368                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
17369                     }
17370                     return true;
17371                 },
17372
17373                 forceKeyDown: true
17374             });
17375         }
17376         
17377         this.queryDelay = Math.max(this.queryDelay || 10,
17378                 this.mode == 'local' ? 10 : 250);
17379         
17380         
17381         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
17382         
17383         if(this.typeAhead){
17384             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
17385         }
17386         
17387         if(this.editable !== false){
17388             this.tickableInputEl().on("keyup", this.onKeyUp, this);
17389         }
17390         
17391         this.indicator = this.indicatorEl();
17392         
17393         if(this.indicator){
17394             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
17395             this.indicator.hide();
17396         }
17397         
17398     },
17399
17400     onDestroy : function(){
17401         if(this.view){
17402             this.view.setStore(null);
17403             this.view.el.removeAllListeners();
17404             this.view.el.remove();
17405             this.view.purgeListeners();
17406         }
17407         if(this.list){
17408             this.list.dom.innerHTML  = '';
17409         }
17410         
17411         if(this.store){
17412             this.store.un('beforeload', this.onBeforeLoad, this);
17413             this.store.un('load', this.onLoad, this);
17414             this.store.un('loadexception', this.onLoadException, this);
17415         }
17416         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
17417     },
17418
17419     // private
17420     fireKey : function(e){
17421         if(e.isNavKeyPress() && !this.list.isVisible()){
17422             this.fireEvent("specialkey", this, e);
17423         }
17424     },
17425
17426     // private
17427     onResize: function(w, h)
17428     {
17429         
17430         
17431 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
17432 //        
17433 //        if(typeof w != 'number'){
17434 //            // we do not handle it!?!?
17435 //            return;
17436 //        }
17437 //        var tw = this.trigger.getWidth();
17438 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
17439 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
17440 //        var x = w - tw;
17441 //        this.inputEl().setWidth( this.adjustWidth('input', x));
17442 //            
17443 //        //this.trigger.setStyle('left', x+'px');
17444 //        
17445 //        if(this.list && this.listWidth === undefined){
17446 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
17447 //            this.list.setWidth(lw);
17448 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
17449 //        }
17450         
17451     
17452         
17453     },
17454
17455     /**
17456      * Allow or prevent the user from directly editing the field text.  If false is passed,
17457      * the user will only be able to select from the items defined in the dropdown list.  This method
17458      * is the runtime equivalent of setting the 'editable' config option at config time.
17459      * @param {Boolean} value True to allow the user to directly edit the field text
17460      */
17461     setEditable : function(value){
17462         if(value == this.editable){
17463             return;
17464         }
17465         this.editable = value;
17466         if(!value){
17467             this.inputEl().dom.setAttribute('readOnly', true);
17468             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17469             this.inputEl().addClass('x-combo-noedit');
17470         }else{
17471             this.inputEl().dom.removeAttribute('readOnly');
17472             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17473             this.inputEl().removeClass('x-combo-noedit');
17474         }
17475     },
17476
17477     // private
17478     
17479     onBeforeLoad : function(combo,opts){
17480         if(!this.hasFocus){
17481             return;
17482         }
17483          if (!opts.add) {
17484             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17485          }
17486         this.restrictHeight();
17487         this.selectedIndex = -1;
17488     },
17489
17490     // private
17491     onLoad : function(){
17492         
17493         this.hasQuery = false;
17494         
17495         if(!this.hasFocus){
17496             return;
17497         }
17498         
17499         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17500             this.loading.hide();
17501         }
17502         
17503         if(this.store.getCount() > 0){
17504             
17505             this.expand();
17506             this.restrictHeight();
17507             if(this.lastQuery == this.allQuery){
17508                 if(this.editable && !this.tickable){
17509                     this.inputEl().dom.select();
17510                 }
17511                 
17512                 if(
17513                     !this.selectByValue(this.value, true) &&
17514                     this.autoFocus && 
17515                     (
17516                         !this.store.lastOptions ||
17517                         typeof(this.store.lastOptions.add) == 'undefined' || 
17518                         this.store.lastOptions.add != true
17519                     )
17520                 ){
17521                     this.select(0, true);
17522                 }
17523             }else{
17524                 if(this.autoFocus){
17525                     this.selectNext();
17526                 }
17527                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17528                     this.taTask.delay(this.typeAheadDelay);
17529                 }
17530             }
17531         }else{
17532             this.onEmptyResults();
17533         }
17534         
17535         //this.el.focus();
17536     },
17537     // private
17538     onLoadException : function()
17539     {
17540         this.hasQuery = false;
17541         
17542         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17543             this.loading.hide();
17544         }
17545         
17546         if(this.tickable && this.editable){
17547             return;
17548         }
17549         
17550         this.collapse();
17551         // only causes errors at present
17552         //Roo.log(this.store.reader.jsonData);
17553         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17554             // fixme
17555             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17556         //}
17557         
17558         
17559     },
17560     // private
17561     onTypeAhead : function(){
17562         if(this.store.getCount() > 0){
17563             var r = this.store.getAt(0);
17564             var newValue = r.data[this.displayField];
17565             var len = newValue.length;
17566             var selStart = this.getRawValue().length;
17567             
17568             if(selStart != len){
17569                 this.setRawValue(newValue);
17570                 this.selectText(selStart, newValue.length);
17571             }
17572         }
17573     },
17574
17575     // private
17576     onSelect : function(record, index){
17577         
17578         if(this.fireEvent('beforeselect', this, record, index) !== false){
17579         
17580             this.setFromData(index > -1 ? record.data : false);
17581             
17582             this.collapse();
17583             this.fireEvent('select', this, record, index);
17584         }
17585     },
17586
17587     /**
17588      * Returns the currently selected field value or empty string if no value is set.
17589      * @return {String} value The selected value
17590      */
17591     getValue : function()
17592     {
17593         if(Roo.isIOS && this.useNativeIOS){
17594             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17595         }
17596         
17597         if(this.multiple){
17598             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17599         }
17600         
17601         if(this.valueField){
17602             return typeof this.value != 'undefined' ? this.value : '';
17603         }else{
17604             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17605         }
17606     },
17607     
17608     getRawValue : function()
17609     {
17610         if(Roo.isIOS && this.useNativeIOS){
17611             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17612         }
17613         
17614         var v = this.inputEl().getValue();
17615         
17616         return v;
17617     },
17618
17619     /**
17620      * Clears any text/value currently set in the field
17621      */
17622     clearValue : function(){
17623         
17624         if(this.hiddenField){
17625             this.hiddenField.dom.value = '';
17626         }
17627         this.value = '';
17628         this.setRawValue('');
17629         this.lastSelectionText = '';
17630         this.lastData = false;
17631         
17632         var close = this.closeTriggerEl();
17633         
17634         if(close){
17635             close.hide();
17636         }
17637         
17638         this.validate();
17639         
17640     },
17641
17642     /**
17643      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17644      * will be displayed in the field.  If the value does not match the data value of an existing item,
17645      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17646      * Otherwise the field will be blank (although the value will still be set).
17647      * @param {String} value The value to match
17648      */
17649     setValue : function(v)
17650     {
17651         if(Roo.isIOS && this.useNativeIOS){
17652             this.setIOSValue(v);
17653             return;
17654         }
17655         
17656         if(this.multiple){
17657             this.syncValue();
17658             return;
17659         }
17660         
17661         var text = v;
17662         if(this.valueField){
17663             var r = this.findRecord(this.valueField, v);
17664             if(r){
17665                 text = r.data[this.displayField];
17666             }else if(this.valueNotFoundText !== undefined){
17667                 text = this.valueNotFoundText;
17668             }
17669         }
17670         this.lastSelectionText = text;
17671         if(this.hiddenField){
17672             this.hiddenField.dom.value = v;
17673         }
17674         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17675         this.value = v;
17676         
17677         var close = this.closeTriggerEl();
17678         
17679         if(close){
17680             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17681         }
17682         
17683         this.validate();
17684     },
17685     /**
17686      * @property {Object} the last set data for the element
17687      */
17688     
17689     lastData : false,
17690     /**
17691      * Sets the value of the field based on a object which is related to the record format for the store.
17692      * @param {Object} value the value to set as. or false on reset?
17693      */
17694     setFromData : function(o){
17695         
17696         if(this.multiple){
17697             this.addItem(o);
17698             return;
17699         }
17700             
17701         var dv = ''; // display value
17702         var vv = ''; // value value..
17703         this.lastData = o;
17704         if (this.displayField) {
17705             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17706         } else {
17707             // this is an error condition!!!
17708             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17709         }
17710         
17711         if(this.valueField){
17712             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17713         }
17714         
17715         var close = this.closeTriggerEl();
17716         
17717         if(close){
17718             if(dv.length || vv * 1 > 0){
17719                 close.show() ;
17720                 this.blockFocus=true;
17721             } else {
17722                 close.hide();
17723             }             
17724         }
17725         
17726         if(this.hiddenField){
17727             this.hiddenField.dom.value = vv;
17728             
17729             this.lastSelectionText = dv;
17730             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17731             this.value = vv;
17732             return;
17733         }
17734         // no hidden field.. - we store the value in 'value', but still display
17735         // display field!!!!
17736         this.lastSelectionText = dv;
17737         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17738         this.value = vv;
17739         
17740         
17741         
17742     },
17743     // private
17744     reset : function(){
17745         // overridden so that last data is reset..
17746         
17747         if(this.multiple){
17748             this.clearItem();
17749             return;
17750         }
17751         
17752         this.setValue(this.originalValue);
17753         //this.clearInvalid();
17754         this.lastData = false;
17755         if (this.view) {
17756             this.view.clearSelections();
17757         }
17758         
17759         this.validate();
17760     },
17761     // private
17762     findRecord : function(prop, value){
17763         var record;
17764         if(this.store.getCount() > 0){
17765             this.store.each(function(r){
17766                 if(r.data[prop] == value){
17767                     record = r;
17768                     return false;
17769                 }
17770                 return true;
17771             });
17772         }
17773         return record;
17774     },
17775     
17776     getName: function()
17777     {
17778         // returns hidden if it's set..
17779         if (!this.rendered) {return ''};
17780         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17781         
17782     },
17783     // private
17784     onViewMove : function(e, t){
17785         this.inKeyMode = false;
17786     },
17787
17788     // private
17789     onViewOver : function(e, t){
17790         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17791             return;
17792         }
17793         var item = this.view.findItemFromChild(t);
17794         
17795         if(item){
17796             var index = this.view.indexOf(item);
17797             this.select(index, false);
17798         }
17799     },
17800
17801     // private
17802     onViewClick : function(view, doFocus, el, e)
17803     {
17804         var index = this.view.getSelectedIndexes()[0];
17805         
17806         var r = this.store.getAt(index);
17807         
17808         if(this.tickable){
17809             
17810             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17811                 return;
17812             }
17813             
17814             var rm = false;
17815             var _this = this;
17816             
17817             Roo.each(this.tickItems, function(v,k){
17818                 
17819                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17820                     Roo.log(v);
17821                     _this.tickItems.splice(k, 1);
17822                     
17823                     if(typeof(e) == 'undefined' && view == false){
17824                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17825                     }
17826                     
17827                     rm = true;
17828                     return;
17829                 }
17830             });
17831             
17832             if(rm){
17833                 return;
17834             }
17835             
17836             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17837                 this.tickItems.push(r.data);
17838             }
17839             
17840             if(typeof(e) == 'undefined' && view == false){
17841                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17842             }
17843                     
17844             return;
17845         }
17846         
17847         if(r){
17848             this.onSelect(r, index);
17849         }
17850         if(doFocus !== false && !this.blockFocus){
17851             this.inputEl().focus();
17852         }
17853     },
17854
17855     // private
17856     restrictHeight : function(){
17857         //this.innerList.dom.style.height = '';
17858         //var inner = this.innerList.dom;
17859         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17860         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17861         //this.list.beginUpdate();
17862         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17863         this.list.alignTo(this.inputEl(), this.listAlign);
17864         this.list.alignTo(this.inputEl(), this.listAlign);
17865         //this.list.endUpdate();
17866     },
17867
17868     // private
17869     onEmptyResults : function(){
17870         
17871         if(this.tickable && this.editable){
17872             this.hasFocus = false;
17873             this.restrictHeight();
17874             return;
17875         }
17876         
17877         this.collapse();
17878     },
17879
17880     /**
17881      * Returns true if the dropdown list is expanded, else false.
17882      */
17883     isExpanded : function(){
17884         return this.list.isVisible();
17885     },
17886
17887     /**
17888      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17889      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17890      * @param {String} value The data value of the item to select
17891      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17892      * selected item if it is not currently in view (defaults to true)
17893      * @return {Boolean} True if the value matched an item in the list, else false
17894      */
17895     selectByValue : function(v, scrollIntoView){
17896         if(v !== undefined && v !== null){
17897             var r = this.findRecord(this.valueField || this.displayField, v);
17898             if(r){
17899                 this.select(this.store.indexOf(r), scrollIntoView);
17900                 return true;
17901             }
17902         }
17903         return false;
17904     },
17905
17906     /**
17907      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17908      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17909      * @param {Number} index The zero-based index of the list item to select
17910      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17911      * selected item if it is not currently in view (defaults to true)
17912      */
17913     select : function(index, scrollIntoView){
17914         this.selectedIndex = index;
17915         this.view.select(index);
17916         if(scrollIntoView !== false){
17917             var el = this.view.getNode(index);
17918             /*
17919              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17920              */
17921             if(el){
17922                 this.list.scrollChildIntoView(el, false);
17923             }
17924         }
17925     },
17926
17927     // private
17928     selectNext : function(){
17929         var ct = this.store.getCount();
17930         if(ct > 0){
17931             if(this.selectedIndex == -1){
17932                 this.select(0);
17933             }else if(this.selectedIndex < ct-1){
17934                 this.select(this.selectedIndex+1);
17935             }
17936         }
17937     },
17938
17939     // private
17940     selectPrev : function(){
17941         var ct = this.store.getCount();
17942         if(ct > 0){
17943             if(this.selectedIndex == -1){
17944                 this.select(0);
17945             }else if(this.selectedIndex != 0){
17946                 this.select(this.selectedIndex-1);
17947             }
17948         }
17949     },
17950
17951     // private
17952     onKeyUp : function(e){
17953         if(this.editable !== false && !e.isSpecialKey()){
17954             this.lastKey = e.getKey();
17955             this.dqTask.delay(this.queryDelay);
17956         }
17957     },
17958
17959     // private
17960     validateBlur : function(){
17961         return !this.list || !this.list.isVisible();   
17962     },
17963
17964     // private
17965     initQuery : function(){
17966         
17967         var v = this.getRawValue();
17968         
17969         if(this.tickable && this.editable){
17970             v = this.tickableInputEl().getValue();
17971         }
17972         
17973         this.doQuery(v);
17974     },
17975
17976     // private
17977     doForce : function(){
17978         if(this.inputEl().dom.value.length > 0){
17979             this.inputEl().dom.value =
17980                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17981              
17982         }
17983     },
17984
17985     /**
17986      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17987      * query allowing the query action to be canceled if needed.
17988      * @param {String} query The SQL query to execute
17989      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17990      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17991      * saved in the current store (defaults to false)
17992      */
17993     doQuery : function(q, forceAll){
17994         
17995         if(q === undefined || q === null){
17996             q = '';
17997         }
17998         var qe = {
17999             query: q,
18000             forceAll: forceAll,
18001             combo: this,
18002             cancel:false
18003         };
18004         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
18005             return false;
18006         }
18007         q = qe.query;
18008         
18009         forceAll = qe.forceAll;
18010         if(forceAll === true || (q.length >= this.minChars)){
18011             
18012             this.hasQuery = true;
18013             
18014             if(this.lastQuery != q || this.alwaysQuery){
18015                 this.lastQuery = q;
18016                 if(this.mode == 'local'){
18017                     this.selectedIndex = -1;
18018                     if(forceAll){
18019                         this.store.clearFilter();
18020                     }else{
18021                         
18022                         if(this.specialFilter){
18023                             this.fireEvent('specialfilter', this);
18024                             this.onLoad();
18025                             return;
18026                         }
18027                         
18028                         this.store.filter(this.displayField, q);
18029                     }
18030                     
18031                     this.store.fireEvent("datachanged", this.store);
18032                     
18033                     this.onLoad();
18034                     
18035                     
18036                 }else{
18037                     
18038                     this.store.baseParams[this.queryParam] = q;
18039                     
18040                     var options = {params : this.getParams(q)};
18041                     
18042                     if(this.loadNext){
18043                         options.add = true;
18044                         options.params.start = this.page * this.pageSize;
18045                     }
18046                     
18047                     this.store.load(options);
18048                     
18049                     /*
18050                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
18051                      *  we should expand the list on onLoad
18052                      *  so command out it
18053                      */
18054 //                    this.expand();
18055                 }
18056             }else{
18057                 this.selectedIndex = -1;
18058                 this.onLoad();   
18059             }
18060         }
18061         
18062         this.loadNext = false;
18063     },
18064     
18065     // private
18066     getParams : function(q){
18067         var p = {};
18068         //p[this.queryParam] = q;
18069         
18070         if(this.pageSize){
18071             p.start = 0;
18072             p.limit = this.pageSize;
18073         }
18074         return p;
18075     },
18076
18077     /**
18078      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
18079      */
18080     collapse : function(){
18081         if(!this.isExpanded()){
18082             return;
18083         }
18084         
18085         this.list.hide();
18086         
18087         this.hasFocus = false;
18088         
18089         if(this.tickable){
18090             this.okBtn.hide();
18091             this.cancelBtn.hide();
18092             this.trigger.show();
18093             
18094             if(this.editable){
18095                 this.tickableInputEl().dom.value = '';
18096                 this.tickableInputEl().blur();
18097             }
18098             
18099         }
18100         
18101         Roo.get(document).un('mousedown', this.collapseIf, this);
18102         Roo.get(document).un('mousewheel', this.collapseIf, this);
18103         if (!this.editable) {
18104             Roo.get(document).un('keydown', this.listKeyPress, this);
18105         }
18106         this.fireEvent('collapse', this);
18107         
18108         this.validate();
18109     },
18110
18111     // private
18112     collapseIf : function(e){
18113         var in_combo  = e.within(this.el);
18114         var in_list =  e.within(this.list);
18115         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
18116         
18117         if (in_combo || in_list || is_list) {
18118             //e.stopPropagation();
18119             return;
18120         }
18121         
18122         if(this.tickable){
18123             this.onTickableFooterButtonClick(e, false, false);
18124         }
18125
18126         this.collapse();
18127         
18128     },
18129
18130     /**
18131      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
18132      */
18133     expand : function(){
18134        
18135         if(this.isExpanded() || !this.hasFocus){
18136             return;
18137         }
18138         
18139         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
18140         this.list.setWidth(lw);
18141         
18142         Roo.log('expand');
18143         
18144         this.list.show();
18145         
18146         this.restrictHeight();
18147         
18148         if(this.tickable){
18149             
18150             this.tickItems = Roo.apply([], this.item);
18151             
18152             this.okBtn.show();
18153             this.cancelBtn.show();
18154             this.trigger.hide();
18155             
18156             if(this.editable){
18157                 this.tickableInputEl().focus();
18158             }
18159             
18160         }
18161         
18162         Roo.get(document).on('mousedown', this.collapseIf, this);
18163         Roo.get(document).on('mousewheel', this.collapseIf, this);
18164         if (!this.editable) {
18165             Roo.get(document).on('keydown', this.listKeyPress, this);
18166         }
18167         
18168         this.fireEvent('expand', this);
18169     },
18170
18171     // private
18172     // Implements the default empty TriggerField.onTriggerClick function
18173     onTriggerClick : function(e)
18174     {
18175         Roo.log('trigger click');
18176         
18177         if(this.disabled || !this.triggerList){
18178             return;
18179         }
18180         
18181         this.page = 0;
18182         this.loadNext = false;
18183         
18184         if(this.isExpanded()){
18185             this.collapse();
18186             if (!this.blockFocus) {
18187                 this.inputEl().focus();
18188             }
18189             
18190         }else {
18191             this.hasFocus = true;
18192             if(this.triggerAction == 'all') {
18193                 this.doQuery(this.allQuery, true);
18194             } else {
18195                 this.doQuery(this.getRawValue());
18196             }
18197             if (!this.blockFocus) {
18198                 this.inputEl().focus();
18199             }
18200         }
18201     },
18202     
18203     onTickableTriggerClick : function(e)
18204     {
18205         if(this.disabled){
18206             return;
18207         }
18208         
18209         this.page = 0;
18210         this.loadNext = false;
18211         this.hasFocus = true;
18212         
18213         if(this.triggerAction == 'all') {
18214             this.doQuery(this.allQuery, true);
18215         } else {
18216             this.doQuery(this.getRawValue());
18217         }
18218     },
18219     
18220     onSearchFieldClick : function(e)
18221     {
18222         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
18223             this.onTickableFooterButtonClick(e, false, false);
18224             return;
18225         }
18226         
18227         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
18228             return;
18229         }
18230         
18231         this.page = 0;
18232         this.loadNext = false;
18233         this.hasFocus = true;
18234         
18235         if(this.triggerAction == 'all') {
18236             this.doQuery(this.allQuery, true);
18237         } else {
18238             this.doQuery(this.getRawValue());
18239         }
18240     },
18241     
18242     listKeyPress : function(e)
18243     {
18244         //Roo.log('listkeypress');
18245         // scroll to first matching element based on key pres..
18246         if (e.isSpecialKey()) {
18247             return false;
18248         }
18249         var k = String.fromCharCode(e.getKey()).toUpperCase();
18250         //Roo.log(k);
18251         var match  = false;
18252         var csel = this.view.getSelectedNodes();
18253         var cselitem = false;
18254         if (csel.length) {
18255             var ix = this.view.indexOf(csel[0]);
18256             cselitem  = this.store.getAt(ix);
18257             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
18258                 cselitem = false;
18259             }
18260             
18261         }
18262         
18263         this.store.each(function(v) { 
18264             if (cselitem) {
18265                 // start at existing selection.
18266                 if (cselitem.id == v.id) {
18267                     cselitem = false;
18268                 }
18269                 return true;
18270             }
18271                 
18272             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
18273                 match = this.store.indexOf(v);
18274                 return false;
18275             }
18276             return true;
18277         }, this);
18278         
18279         if (match === false) {
18280             return true; // no more action?
18281         }
18282         // scroll to?
18283         this.view.select(match);
18284         var sn = Roo.get(this.view.getSelectedNodes()[0]);
18285         sn.scrollIntoView(sn.dom.parentNode, false);
18286     },
18287     
18288     onViewScroll : function(e, t){
18289         
18290         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
18291             return;
18292         }
18293         
18294         this.hasQuery = true;
18295         
18296         this.loading = this.list.select('.loading', true).first();
18297         
18298         if(this.loading === null){
18299             this.list.createChild({
18300                 tag: 'div',
18301                 cls: 'loading roo-select2-more-results roo-select2-active',
18302                 html: 'Loading more results...'
18303             });
18304             
18305             this.loading = this.list.select('.loading', true).first();
18306             
18307             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
18308             
18309             this.loading.hide();
18310         }
18311         
18312         this.loading.show();
18313         
18314         var _combo = this;
18315         
18316         this.page++;
18317         this.loadNext = true;
18318         
18319         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
18320         
18321         return;
18322     },
18323     
18324     addItem : function(o)
18325     {   
18326         var dv = ''; // display value
18327         
18328         if (this.displayField) {
18329             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
18330         } else {
18331             // this is an error condition!!!
18332             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
18333         }
18334         
18335         if(!dv.length){
18336             return;
18337         }
18338         
18339         var choice = this.choices.createChild({
18340             tag: 'li',
18341             cls: 'roo-select2-search-choice',
18342             cn: [
18343                 {
18344                     tag: 'div',
18345                     html: dv
18346                 },
18347                 {
18348                     tag: 'a',
18349                     href: '#',
18350                     cls: 'roo-select2-search-choice-close fa fa-times',
18351                     tabindex: '-1'
18352                 }
18353             ]
18354             
18355         }, this.searchField);
18356         
18357         var close = choice.select('a.roo-select2-search-choice-close', true).first();
18358         
18359         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
18360         
18361         this.item.push(o);
18362         
18363         this.lastData = o;
18364         
18365         this.syncValue();
18366         
18367         this.inputEl().dom.value = '';
18368         
18369         this.validate();
18370     },
18371     
18372     onRemoveItem : function(e, _self, o)
18373     {
18374         e.preventDefault();
18375         
18376         this.lastItem = Roo.apply([], this.item);
18377         
18378         var index = this.item.indexOf(o.data) * 1;
18379         
18380         if( index < 0){
18381             Roo.log('not this item?!');
18382             return;
18383         }
18384         
18385         this.item.splice(index, 1);
18386         o.item.remove();
18387         
18388         this.syncValue();
18389         
18390         this.fireEvent('remove', this, e);
18391         
18392         this.validate();
18393         
18394     },
18395     
18396     syncValue : function()
18397     {
18398         if(!this.item.length){
18399             this.clearValue();
18400             return;
18401         }
18402             
18403         var value = [];
18404         var _this = this;
18405         Roo.each(this.item, function(i){
18406             if(_this.valueField){
18407                 value.push(i[_this.valueField]);
18408                 return;
18409             }
18410
18411             value.push(i);
18412         });
18413
18414         this.value = value.join(',');
18415
18416         if(this.hiddenField){
18417             this.hiddenField.dom.value = this.value;
18418         }
18419         
18420         this.store.fireEvent("datachanged", this.store);
18421         
18422         this.validate();
18423     },
18424     
18425     clearItem : function()
18426     {
18427         if(!this.multiple){
18428             return;
18429         }
18430         
18431         this.item = [];
18432         
18433         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
18434            c.remove();
18435         });
18436         
18437         this.syncValue();
18438         
18439         this.validate();
18440         
18441         if(this.tickable && !Roo.isTouch){
18442             this.view.refresh();
18443         }
18444     },
18445     
18446     inputEl: function ()
18447     {
18448         if(Roo.isIOS && this.useNativeIOS){
18449             return this.el.select('select.roo-ios-select', true).first();
18450         }
18451         
18452         if(Roo.isTouch && this.mobileTouchView){
18453             return this.el.select('input.form-control',true).first();
18454         }
18455         
18456         if(this.tickable){
18457             return this.searchField;
18458         }
18459         
18460         return this.el.select('input.form-control',true).first();
18461     },
18462     
18463     onTickableFooterButtonClick : function(e, btn, el)
18464     {
18465         e.preventDefault();
18466         
18467         this.lastItem = Roo.apply([], this.item);
18468         
18469         if(btn && btn.name == 'cancel'){
18470             this.tickItems = Roo.apply([], this.item);
18471             this.collapse();
18472             return;
18473         }
18474         
18475         this.clearItem();
18476         
18477         var _this = this;
18478         
18479         Roo.each(this.tickItems, function(o){
18480             _this.addItem(o);
18481         });
18482         
18483         this.collapse();
18484         
18485     },
18486     
18487     validate : function()
18488     {
18489         if(this.getVisibilityEl().hasClass('hidden')){
18490             return true;
18491         }
18492         
18493         var v = this.getRawValue();
18494         
18495         if(this.multiple){
18496             v = this.getValue();
18497         }
18498         
18499         if(this.disabled || this.allowBlank || v.length){
18500             this.markValid();
18501             return true;
18502         }
18503         
18504         this.markInvalid();
18505         return false;
18506     },
18507     
18508     tickableInputEl : function()
18509     {
18510         if(!this.tickable || !this.editable){
18511             return this.inputEl();
18512         }
18513         
18514         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18515     },
18516     
18517     
18518     getAutoCreateTouchView : function()
18519     {
18520         var id = Roo.id();
18521         
18522         var cfg = {
18523             cls: 'form-group' //input-group
18524         };
18525         
18526         var input =  {
18527             tag: 'input',
18528             id : id,
18529             type : this.inputType,
18530             cls : 'form-control x-combo-noedit',
18531             autocomplete: 'new-password',
18532             placeholder : this.placeholder || '',
18533             readonly : true
18534         };
18535         
18536         if (this.name) {
18537             input.name = this.name;
18538         }
18539         
18540         if (this.size) {
18541             input.cls += ' input-' + this.size;
18542         }
18543         
18544         if (this.disabled) {
18545             input.disabled = true;
18546         }
18547         
18548         var inputblock = {
18549             cls : 'roo-combobox-wrap',
18550             cn : [
18551                 input
18552             ]
18553         };
18554         
18555         if(this.before){
18556             inputblock.cls += ' input-group';
18557             
18558             inputblock.cn.unshift({
18559                 tag :'span',
18560                 cls : 'input-group-addon input-group-prepend input-group-text',
18561                 html : this.before
18562             });
18563         }
18564         
18565         if(this.removable && !this.multiple){
18566             inputblock.cls += ' roo-removable';
18567             
18568             inputblock.cn.push({
18569                 tag: 'button',
18570                 html : 'x',
18571                 cls : 'roo-combo-removable-btn close'
18572             });
18573         }
18574
18575         if(this.hasFeedback && !this.allowBlank){
18576             
18577             inputblock.cls += ' has-feedback';
18578             
18579             inputblock.cn.push({
18580                 tag: 'span',
18581                 cls: 'glyphicon form-control-feedback'
18582             });
18583             
18584         }
18585         
18586         if (this.after) {
18587             
18588             inputblock.cls += (this.before) ? '' : ' input-group';
18589             
18590             inputblock.cn.push({
18591                 tag :'span',
18592                 cls : 'input-group-addon input-group-append input-group-text',
18593                 html : this.after
18594             });
18595         }
18596
18597         
18598         var ibwrap = inputblock;
18599         
18600         if(this.multiple){
18601             ibwrap = {
18602                 tag: 'ul',
18603                 cls: 'roo-select2-choices',
18604                 cn:[
18605                     {
18606                         tag: 'li',
18607                         cls: 'roo-select2-search-field',
18608                         cn: [
18609
18610                             inputblock
18611                         ]
18612                     }
18613                 ]
18614             };
18615         
18616             
18617         }
18618         
18619         var combobox = {
18620             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18621             cn: [
18622                 {
18623                     tag: 'input',
18624                     type : 'hidden',
18625                     cls: 'form-hidden-field'
18626                 },
18627                 ibwrap
18628             ]
18629         };
18630         
18631         if(!this.multiple && this.showToggleBtn){
18632             
18633             var caret = {
18634                 cls: 'caret'
18635             };
18636             
18637             if (this.caret != false) {
18638                 caret = {
18639                      tag: 'i',
18640                      cls: 'fa fa-' + this.caret
18641                 };
18642                 
18643             }
18644             
18645             combobox.cn.push({
18646                 tag :'span',
18647                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18648                 cn : [
18649                     Roo.bootstrap.version == 3 ? caret : '',
18650                     {
18651                         tag: 'span',
18652                         cls: 'combobox-clear',
18653                         cn  : [
18654                             {
18655                                 tag : 'i',
18656                                 cls: 'icon-remove'
18657                             }
18658                         ]
18659                     }
18660                 ]
18661
18662             })
18663         }
18664         
18665         if(this.multiple){
18666             combobox.cls += ' roo-select2-container-multi';
18667         }
18668         
18669         var required =  this.allowBlank ?  {
18670                     tag : 'i',
18671                     style: 'display: none'
18672                 } : {
18673                    tag : 'i',
18674                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18675                    tooltip : 'This field is required'
18676                 };
18677         
18678         var align = this.labelAlign || this.parentLabelAlign();
18679         
18680         if (align ==='left' && this.fieldLabel.length) {
18681
18682             cfg.cn = [
18683                 required,
18684                 {
18685                     tag: 'label',
18686                     cls : 'control-label col-form-label',
18687                     html : this.fieldLabel
18688
18689                 },
18690                 {
18691                     cls : 'roo-combobox-wrap ', 
18692                     cn: [
18693                         combobox
18694                     ]
18695                 }
18696             ];
18697             
18698             var labelCfg = cfg.cn[1];
18699             var contentCfg = cfg.cn[2];
18700             
18701
18702             if(this.indicatorpos == 'right'){
18703                 cfg.cn = [
18704                     {
18705                         tag: 'label',
18706                         'for' :  id,
18707                         cls : 'control-label col-form-label',
18708                         cn : [
18709                             {
18710                                 tag : 'span',
18711                                 html : this.fieldLabel
18712                             },
18713                             required
18714                         ]
18715                     },
18716                     {
18717                         cls : "roo-combobox-wrap ",
18718                         cn: [
18719                             combobox
18720                         ]
18721                     }
18722
18723                 ];
18724                 
18725                 labelCfg = cfg.cn[0];
18726                 contentCfg = cfg.cn[1];
18727             }
18728             
18729            
18730             
18731             if(this.labelWidth > 12){
18732                 labelCfg.style = "width: " + this.labelWidth + 'px';
18733             }
18734            
18735             if(this.labelWidth < 13 && this.labelmd == 0){
18736                 this.labelmd = this.labelWidth;
18737             }
18738             
18739             if(this.labellg > 0){
18740                 labelCfg.cls += ' col-lg-' + this.labellg;
18741                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18742             }
18743             
18744             if(this.labelmd > 0){
18745                 labelCfg.cls += ' col-md-' + this.labelmd;
18746                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18747             }
18748             
18749             if(this.labelsm > 0){
18750                 labelCfg.cls += ' col-sm-' + this.labelsm;
18751                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18752             }
18753             
18754             if(this.labelxs > 0){
18755                 labelCfg.cls += ' col-xs-' + this.labelxs;
18756                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18757             }
18758                 
18759                 
18760         } else if ( this.fieldLabel.length) {
18761             cfg.cn = [
18762                required,
18763                 {
18764                     tag: 'label',
18765                     cls : 'control-label',
18766                     html : this.fieldLabel
18767
18768                 },
18769                 {
18770                     cls : '', 
18771                     cn: [
18772                         combobox
18773                     ]
18774                 }
18775             ];
18776             
18777             if(this.indicatorpos == 'right'){
18778                 cfg.cn = [
18779                     {
18780                         tag: 'label',
18781                         cls : 'control-label',
18782                         html : this.fieldLabel,
18783                         cn : [
18784                             required
18785                         ]
18786                     },
18787                     {
18788                         cls : '', 
18789                         cn: [
18790                             combobox
18791                         ]
18792                     }
18793                 ];
18794             }
18795         } else {
18796             cfg.cn = combobox;    
18797         }
18798         
18799         
18800         var settings = this;
18801         
18802         ['xs','sm','md','lg'].map(function(size){
18803             if (settings[size]) {
18804                 cfg.cls += ' col-' + size + '-' + settings[size];
18805             }
18806         });
18807         
18808         return cfg;
18809     },
18810     
18811     initTouchView : function()
18812     {
18813         this.renderTouchView();
18814         
18815         this.touchViewEl.on('scroll', function(){
18816             this.el.dom.scrollTop = 0;
18817         }, this);
18818         
18819         this.originalValue = this.getValue();
18820         
18821         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18822         
18823         this.inputEl().on("click", this.showTouchView, this);
18824         if (this.triggerEl) {
18825             this.triggerEl.on("click", this.showTouchView, this);
18826         }
18827         
18828         
18829         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18830         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18831         
18832         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18833         
18834         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18835         this.store.on('load', this.onTouchViewLoad, this);
18836         this.store.on('loadexception', this.onTouchViewLoadException, this);
18837         
18838         if(this.hiddenName){
18839             
18840             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18841             
18842             this.hiddenField.dom.value =
18843                 this.hiddenValue !== undefined ? this.hiddenValue :
18844                 this.value !== undefined ? this.value : '';
18845         
18846             this.el.dom.removeAttribute('name');
18847             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18848         }
18849         
18850         if(this.multiple){
18851             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18852             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18853         }
18854         
18855         if(this.removable && !this.multiple){
18856             var close = this.closeTriggerEl();
18857             if(close){
18858                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18859                 close.on('click', this.removeBtnClick, this, close);
18860             }
18861         }
18862         /*
18863          * fix the bug in Safari iOS8
18864          */
18865         this.inputEl().on("focus", function(e){
18866             document.activeElement.blur();
18867         }, this);
18868         
18869         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18870         
18871         return;
18872         
18873         
18874     },
18875     
18876     renderTouchView : function()
18877     {
18878         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18879         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18880         
18881         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18882         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18883         
18884         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18885         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18886         this.touchViewBodyEl.setStyle('overflow', 'auto');
18887         
18888         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18889         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18890         
18891         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18892         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18893         
18894     },
18895     
18896     showTouchView : function()
18897     {
18898         if(this.disabled){
18899             return;
18900         }
18901         
18902         this.touchViewHeaderEl.hide();
18903
18904         if(this.modalTitle.length){
18905             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18906             this.touchViewHeaderEl.show();
18907         }
18908
18909         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18910         this.touchViewEl.show();
18911
18912         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18913         
18914         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18915         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18916
18917         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18918
18919         if(this.modalTitle.length){
18920             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18921         }
18922         
18923         this.touchViewBodyEl.setHeight(bodyHeight);
18924
18925         if(this.animate){
18926             var _this = this;
18927             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18928         }else{
18929             this.touchViewEl.addClass(['in','show']);
18930         }
18931         
18932         if(this._touchViewMask){
18933             Roo.get(document.body).addClass("x-body-masked");
18934             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18935             this._touchViewMask.setStyle('z-index', 10000);
18936             this._touchViewMask.addClass('show');
18937         }
18938         
18939         this.doTouchViewQuery();
18940         
18941     },
18942     
18943     hideTouchView : function()
18944     {
18945         this.touchViewEl.removeClass(['in','show']);
18946
18947         if(this.animate){
18948             var _this = this;
18949             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18950         }else{
18951             this.touchViewEl.setStyle('display', 'none');
18952         }
18953         
18954         if(this._touchViewMask){
18955             this._touchViewMask.removeClass('show');
18956             Roo.get(document.body).removeClass("x-body-masked");
18957         }
18958     },
18959     
18960     setTouchViewValue : function()
18961     {
18962         if(this.multiple){
18963             this.clearItem();
18964         
18965             var _this = this;
18966
18967             Roo.each(this.tickItems, function(o){
18968                 this.addItem(o);
18969             }, this);
18970         }
18971         
18972         this.hideTouchView();
18973     },
18974     
18975     doTouchViewQuery : function()
18976     {
18977         var qe = {
18978             query: '',
18979             forceAll: true,
18980             combo: this,
18981             cancel:false
18982         };
18983         
18984         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18985             return false;
18986         }
18987         
18988         if(!this.alwaysQuery || this.mode == 'local'){
18989             this.onTouchViewLoad();
18990             return;
18991         }
18992         
18993         this.store.load();
18994     },
18995     
18996     onTouchViewBeforeLoad : function(combo,opts)
18997     {
18998         return;
18999     },
19000
19001     // private
19002     onTouchViewLoad : function()
19003     {
19004         if(this.store.getCount() < 1){
19005             this.onTouchViewEmptyResults();
19006             return;
19007         }
19008         
19009         this.clearTouchView();
19010         
19011         var rawValue = this.getRawValue();
19012         
19013         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
19014         
19015         this.tickItems = [];
19016         
19017         this.store.data.each(function(d, rowIndex){
19018             var row = this.touchViewListGroup.createChild(template);
19019             
19020             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
19021                 row.addClass(d.data.cls);
19022             }
19023             
19024             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19025                 var cfg = {
19026                     data : d.data,
19027                     html : d.data[this.displayField]
19028                 };
19029                 
19030                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
19031                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
19032                 }
19033             }
19034             row.removeClass('selected');
19035             if(!this.multiple && this.valueField &&
19036                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
19037             {
19038                 // radio buttons..
19039                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19040                 row.addClass('selected');
19041             }
19042             
19043             if(this.multiple && this.valueField &&
19044                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
19045             {
19046                 
19047                 // checkboxes...
19048                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19049                 this.tickItems.push(d.data);
19050             }
19051             
19052             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
19053             
19054         }, this);
19055         
19056         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
19057         
19058         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
19059
19060         if(this.modalTitle.length){
19061             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
19062         }
19063
19064         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
19065         
19066         if(this.mobile_restrict_height && listHeight < bodyHeight){
19067             this.touchViewBodyEl.setHeight(listHeight);
19068         }
19069         
19070         var _this = this;
19071         
19072         if(firstChecked && listHeight > bodyHeight){
19073             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
19074         }
19075         
19076     },
19077     
19078     onTouchViewLoadException : function()
19079     {
19080         this.hideTouchView();
19081     },
19082     
19083     onTouchViewEmptyResults : function()
19084     {
19085         this.clearTouchView();
19086         
19087         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
19088         
19089         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
19090         
19091     },
19092     
19093     clearTouchView : function()
19094     {
19095         this.touchViewListGroup.dom.innerHTML = '';
19096     },
19097     
19098     onTouchViewClick : function(e, el, o)
19099     {
19100         e.preventDefault();
19101         
19102         var row = o.row;
19103         var rowIndex = o.rowIndex;
19104         
19105         var r = this.store.getAt(rowIndex);
19106         
19107         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
19108             
19109             if(!this.multiple){
19110                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
19111                     c.dom.removeAttribute('checked');
19112                 }, this);
19113
19114                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19115
19116                 this.setFromData(r.data);
19117
19118                 var close = this.closeTriggerEl();
19119
19120                 if(close){
19121                     close.show();
19122                 }
19123
19124                 this.hideTouchView();
19125
19126                 this.fireEvent('select', this, r, rowIndex);
19127
19128                 return;
19129             }
19130
19131             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
19132                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
19133                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
19134                 return;
19135             }
19136
19137             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
19138             this.addItem(r.data);
19139             this.tickItems.push(r.data);
19140         }
19141     },
19142     
19143     getAutoCreateNativeIOS : function()
19144     {
19145         var cfg = {
19146             cls: 'form-group' //input-group,
19147         };
19148         
19149         var combobox =  {
19150             tag: 'select',
19151             cls : 'roo-ios-select'
19152         };
19153         
19154         if (this.name) {
19155             combobox.name = this.name;
19156         }
19157         
19158         if (this.disabled) {
19159             combobox.disabled = true;
19160         }
19161         
19162         var settings = this;
19163         
19164         ['xs','sm','md','lg'].map(function(size){
19165             if (settings[size]) {
19166                 cfg.cls += ' col-' + size + '-' + settings[size];
19167             }
19168         });
19169         
19170         cfg.cn = combobox;
19171         
19172         return cfg;
19173         
19174     },
19175     
19176     initIOSView : function()
19177     {
19178         this.store.on('load', this.onIOSViewLoad, this);
19179         
19180         return;
19181     },
19182     
19183     onIOSViewLoad : function()
19184     {
19185         if(this.store.getCount() < 1){
19186             return;
19187         }
19188         
19189         this.clearIOSView();
19190         
19191         if(this.allowBlank) {
19192             
19193             var default_text = '-- SELECT --';
19194             
19195             if(this.placeholder.length){
19196                 default_text = this.placeholder;
19197             }
19198             
19199             if(this.emptyTitle.length){
19200                 default_text += ' - ' + this.emptyTitle + ' -';
19201             }
19202             
19203             var opt = this.inputEl().createChild({
19204                 tag: 'option',
19205                 value : 0,
19206                 html : default_text
19207             });
19208             
19209             var o = {};
19210             o[this.valueField] = 0;
19211             o[this.displayField] = default_text;
19212             
19213             this.ios_options.push({
19214                 data : o,
19215                 el : opt
19216             });
19217             
19218         }
19219         
19220         this.store.data.each(function(d, rowIndex){
19221             
19222             var html = '';
19223             
19224             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
19225                 html = d.data[this.displayField];
19226             }
19227             
19228             var value = '';
19229             
19230             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
19231                 value = d.data[this.valueField];
19232             }
19233             
19234             var option = {
19235                 tag: 'option',
19236                 value : value,
19237                 html : html
19238             };
19239             
19240             if(this.value == d.data[this.valueField]){
19241                 option['selected'] = true;
19242             }
19243             
19244             var opt = this.inputEl().createChild(option);
19245             
19246             this.ios_options.push({
19247                 data : d.data,
19248                 el : opt
19249             });
19250             
19251         }, this);
19252         
19253         this.inputEl().on('change', function(){
19254            this.fireEvent('select', this);
19255         }, this);
19256         
19257     },
19258     
19259     clearIOSView: function()
19260     {
19261         this.inputEl().dom.innerHTML = '';
19262         
19263         this.ios_options = [];
19264     },
19265     
19266     setIOSValue: function(v)
19267     {
19268         this.value = v;
19269         
19270         if(!this.ios_options){
19271             return;
19272         }
19273         
19274         Roo.each(this.ios_options, function(opts){
19275            
19276            opts.el.dom.removeAttribute('selected');
19277            
19278            if(opts.data[this.valueField] != v){
19279                return;
19280            }
19281            
19282            opts.el.dom.setAttribute('selected', true);
19283            
19284         }, this);
19285     }
19286
19287     /** 
19288     * @cfg {Boolean} grow 
19289     * @hide 
19290     */
19291     /** 
19292     * @cfg {Number} growMin 
19293     * @hide 
19294     */
19295     /** 
19296     * @cfg {Number} growMax 
19297     * @hide 
19298     */
19299     /**
19300      * @hide
19301      * @method autoSize
19302      */
19303 });
19304
19305 Roo.apply(Roo.bootstrap.ComboBox,  {
19306     
19307     header : {
19308         tag: 'div',
19309         cls: 'modal-header',
19310         cn: [
19311             {
19312                 tag: 'h4',
19313                 cls: 'modal-title'
19314             }
19315         ]
19316     },
19317     
19318     body : {
19319         tag: 'div',
19320         cls: 'modal-body',
19321         cn: [
19322             {
19323                 tag: 'ul',
19324                 cls: 'list-group'
19325             }
19326         ]
19327     },
19328     
19329     listItemRadio : {
19330         tag: 'li',
19331         cls: 'list-group-item',
19332         cn: [
19333             {
19334                 tag: 'span',
19335                 cls: 'roo-combobox-list-group-item-value'
19336             },
19337             {
19338                 tag: 'div',
19339                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
19340                 cn: [
19341                     {
19342                         tag: 'input',
19343                         type: 'radio'
19344                     },
19345                     {
19346                         tag: 'label'
19347                     }
19348                 ]
19349             }
19350         ]
19351     },
19352     
19353     listItemCheckbox : {
19354         tag: 'li',
19355         cls: 'list-group-item',
19356         cn: [
19357             {
19358                 tag: 'span',
19359                 cls: 'roo-combobox-list-group-item-value'
19360             },
19361             {
19362                 tag: 'div',
19363                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
19364                 cn: [
19365                     {
19366                         tag: 'input',
19367                         type: 'checkbox'
19368                     },
19369                     {
19370                         tag: 'label'
19371                     }
19372                 ]
19373             }
19374         ]
19375     },
19376     
19377     emptyResult : {
19378         tag: 'div',
19379         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
19380     },
19381     
19382     footer : {
19383         tag: 'div',
19384         cls: 'modal-footer',
19385         cn: [
19386             {
19387                 tag: 'div',
19388                 cls: 'row',
19389                 cn: [
19390                     {
19391                         tag: 'div',
19392                         cls: 'col-xs-6 text-left',
19393                         cn: {
19394                             tag: 'button',
19395                             cls: 'btn btn-danger roo-touch-view-cancel',
19396                             html: 'Cancel'
19397                         }
19398                     },
19399                     {
19400                         tag: 'div',
19401                         cls: 'col-xs-6 text-right',
19402                         cn: {
19403                             tag: 'button',
19404                             cls: 'btn btn-success roo-touch-view-ok',
19405                             html: 'OK'
19406                         }
19407                     }
19408                 ]
19409             }
19410         ]
19411         
19412     }
19413 });
19414
19415 Roo.apply(Roo.bootstrap.ComboBox,  {
19416     
19417     touchViewTemplate : {
19418         tag: 'div',
19419         cls: 'modal fade roo-combobox-touch-view',
19420         cn: [
19421             {
19422                 tag: 'div',
19423                 cls: 'modal-dialog',
19424                 style : 'position:fixed', // we have to fix position....
19425                 cn: [
19426                     {
19427                         tag: 'div',
19428                         cls: 'modal-content',
19429                         cn: [
19430                             Roo.bootstrap.ComboBox.header,
19431                             Roo.bootstrap.ComboBox.body,
19432                             Roo.bootstrap.ComboBox.footer
19433                         ]
19434                     }
19435                 ]
19436             }
19437         ]
19438     }
19439 });/*
19440  * Based on:
19441  * Ext JS Library 1.1.1
19442  * Copyright(c) 2006-2007, Ext JS, LLC.
19443  *
19444  * Originally Released Under LGPL - original licence link has changed is not relivant.
19445  *
19446  * Fork - LGPL
19447  * <script type="text/javascript">
19448  */
19449
19450 /**
19451  * @class Roo.View
19452  * @extends Roo.util.Observable
19453  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
19454  * This class also supports single and multi selection modes. <br>
19455  * Create a data model bound view:
19456  <pre><code>
19457  var store = new Roo.data.Store(...);
19458
19459  var view = new Roo.View({
19460     el : "my-element",
19461     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19462  
19463     singleSelect: true,
19464     selectedClass: "ydataview-selected",
19465     store: store
19466  });
19467
19468  // listen for node click?
19469  view.on("click", function(vw, index, node, e){
19470  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19471  });
19472
19473  // load XML data
19474  dataModel.load("foobar.xml");
19475  </code></pre>
19476  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19477  * <br><br>
19478  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19479  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19480  * 
19481  * Note: old style constructor is still suported (container, template, config)
19482  * 
19483  * @constructor
19484  * Create a new View
19485  * @param {Object} config The config object
19486  * 
19487  */
19488 Roo.View = function(config, depreciated_tpl, depreciated_config){
19489     
19490     this.parent = false;
19491     
19492     if (typeof(depreciated_tpl) == 'undefined') {
19493         // new way.. - universal constructor.
19494         Roo.apply(this, config);
19495         this.el  = Roo.get(this.el);
19496     } else {
19497         // old format..
19498         this.el  = Roo.get(config);
19499         this.tpl = depreciated_tpl;
19500         Roo.apply(this, depreciated_config);
19501     }
19502     this.wrapEl  = this.el.wrap().wrap();
19503     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19504     
19505     
19506     if(typeof(this.tpl) == "string"){
19507         this.tpl = new Roo.Template(this.tpl);
19508     } else {
19509         // support xtype ctors..
19510         this.tpl = new Roo.factory(this.tpl, Roo);
19511     }
19512     
19513     
19514     this.tpl.compile();
19515     
19516     /** @private */
19517     this.addEvents({
19518         /**
19519          * @event beforeclick
19520          * Fires before a click is processed. Returns false to cancel the default action.
19521          * @param {Roo.View} this
19522          * @param {Number} index The index of the target node
19523          * @param {HTMLElement} node The target node
19524          * @param {Roo.EventObject} e The raw event object
19525          */
19526             "beforeclick" : true,
19527         /**
19528          * @event click
19529          * Fires when a template node is clicked.
19530          * @param {Roo.View} this
19531          * @param {Number} index The index of the target node
19532          * @param {HTMLElement} node The target node
19533          * @param {Roo.EventObject} e The raw event object
19534          */
19535             "click" : true,
19536         /**
19537          * @event dblclick
19538          * Fires when a template node is double clicked.
19539          * @param {Roo.View} this
19540          * @param {Number} index The index of the target node
19541          * @param {HTMLElement} node The target node
19542          * @param {Roo.EventObject} e The raw event object
19543          */
19544             "dblclick" : true,
19545         /**
19546          * @event contextmenu
19547          * Fires when a template node is right clicked.
19548          * @param {Roo.View} this
19549          * @param {Number} index The index of the target node
19550          * @param {HTMLElement} node The target node
19551          * @param {Roo.EventObject} e The raw event object
19552          */
19553             "contextmenu" : true,
19554         /**
19555          * @event selectionchange
19556          * Fires when the selected nodes change.
19557          * @param {Roo.View} this
19558          * @param {Array} selections Array of the selected nodes
19559          */
19560             "selectionchange" : true,
19561     
19562         /**
19563          * @event beforeselect
19564          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19565          * @param {Roo.View} this
19566          * @param {HTMLElement} node The node to be selected
19567          * @param {Array} selections Array of currently selected nodes
19568          */
19569             "beforeselect" : true,
19570         /**
19571          * @event preparedata
19572          * Fires on every row to render, to allow you to change the data.
19573          * @param {Roo.View} this
19574          * @param {Object} data to be rendered (change this)
19575          */
19576           "preparedata" : true
19577           
19578           
19579         });
19580
19581
19582
19583     this.el.on({
19584         "click": this.onClick,
19585         "dblclick": this.onDblClick,
19586         "contextmenu": this.onContextMenu,
19587         scope:this
19588     });
19589
19590     this.selections = [];
19591     this.nodes = [];
19592     this.cmp = new Roo.CompositeElementLite([]);
19593     if(this.store){
19594         this.store = Roo.factory(this.store, Roo.data);
19595         this.setStore(this.store, true);
19596     }
19597     
19598     if ( this.footer && this.footer.xtype) {
19599            
19600          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19601         
19602         this.footer.dataSource = this.store;
19603         this.footer.container = fctr;
19604         this.footer = Roo.factory(this.footer, Roo);
19605         fctr.insertFirst(this.el);
19606         
19607         // this is a bit insane - as the paging toolbar seems to detach the el..
19608 //        dom.parentNode.parentNode.parentNode
19609          // they get detached?
19610     }
19611     
19612     
19613     Roo.View.superclass.constructor.call(this);
19614     
19615     
19616 };
19617
19618 Roo.extend(Roo.View, Roo.util.Observable, {
19619     
19620      /**
19621      * @cfg {Roo.data.Store} store Data store to load data from.
19622      */
19623     store : false,
19624     
19625     /**
19626      * @cfg {String|Roo.Element} el The container element.
19627      */
19628     el : '',
19629     
19630     /**
19631      * @cfg {String|Roo.Template} tpl The template used by this View 
19632      */
19633     tpl : false,
19634     /**
19635      * @cfg {String} dataName the named area of the template to use as the data area
19636      *                          Works with domtemplates roo-name="name"
19637      */
19638     dataName: false,
19639     /**
19640      * @cfg {String} selectedClass The css class to add to selected nodes
19641      */
19642     selectedClass : "x-view-selected",
19643      /**
19644      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19645      */
19646     emptyText : "",
19647     
19648     /**
19649      * @cfg {String} text to display on mask (default Loading)
19650      */
19651     mask : false,
19652     /**
19653      * @cfg {Boolean} multiSelect Allow multiple selection
19654      */
19655     multiSelect : false,
19656     /**
19657      * @cfg {Boolean} singleSelect Allow single selection
19658      */
19659     singleSelect:  false,
19660     
19661     /**
19662      * @cfg {Boolean} toggleSelect - selecting 
19663      */
19664     toggleSelect : false,
19665     
19666     /**
19667      * @cfg {Boolean} tickable - selecting 
19668      */
19669     tickable : false,
19670     
19671     /**
19672      * Returns the element this view is bound to.
19673      * @return {Roo.Element}
19674      */
19675     getEl : function(){
19676         return this.wrapEl;
19677     },
19678     
19679     
19680
19681     /**
19682      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19683      */
19684     refresh : function(){
19685         //Roo.log('refresh');
19686         var t = this.tpl;
19687         
19688         // if we are using something like 'domtemplate', then
19689         // the what gets used is:
19690         // t.applySubtemplate(NAME, data, wrapping data..)
19691         // the outer template then get' applied with
19692         //     the store 'extra data'
19693         // and the body get's added to the
19694         //      roo-name="data" node?
19695         //      <span class='roo-tpl-{name}'></span> ?????
19696         
19697         
19698         
19699         this.clearSelections();
19700         this.el.update("");
19701         var html = [];
19702         var records = this.store.getRange();
19703         if(records.length < 1) {
19704             
19705             // is this valid??  = should it render a template??
19706             
19707             this.el.update(this.emptyText);
19708             return;
19709         }
19710         var el = this.el;
19711         if (this.dataName) {
19712             this.el.update(t.apply(this.store.meta)); //????
19713             el = this.el.child('.roo-tpl-' + this.dataName);
19714         }
19715         
19716         for(var i = 0, len = records.length; i < len; i++){
19717             var data = this.prepareData(records[i].data, i, records[i]);
19718             this.fireEvent("preparedata", this, data, i, records[i]);
19719             
19720             var d = Roo.apply({}, data);
19721             
19722             if(this.tickable){
19723                 Roo.apply(d, {'roo-id' : Roo.id()});
19724                 
19725                 var _this = this;
19726             
19727                 Roo.each(this.parent.item, function(item){
19728                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19729                         return;
19730                     }
19731                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19732                 });
19733             }
19734             
19735             html[html.length] = Roo.util.Format.trim(
19736                 this.dataName ?
19737                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19738                     t.apply(d)
19739             );
19740         }
19741         
19742         
19743         
19744         el.update(html.join(""));
19745         this.nodes = el.dom.childNodes;
19746         this.updateIndexes(0);
19747     },
19748     
19749
19750     /**
19751      * Function to override to reformat the data that is sent to
19752      * the template for each node.
19753      * DEPRICATED - use the preparedata event handler.
19754      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19755      * a JSON object for an UpdateManager bound view).
19756      */
19757     prepareData : function(data, index, record)
19758     {
19759         this.fireEvent("preparedata", this, data, index, record);
19760         return data;
19761     },
19762
19763     onUpdate : function(ds, record){
19764         // Roo.log('on update');   
19765         this.clearSelections();
19766         var index = this.store.indexOf(record);
19767         var n = this.nodes[index];
19768         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19769         n.parentNode.removeChild(n);
19770         this.updateIndexes(index, index);
19771     },
19772
19773     
19774     
19775 // --------- FIXME     
19776     onAdd : function(ds, records, index)
19777     {
19778         //Roo.log(['on Add', ds, records, index] );        
19779         this.clearSelections();
19780         if(this.nodes.length == 0){
19781             this.refresh();
19782             return;
19783         }
19784         var n = this.nodes[index];
19785         for(var i = 0, len = records.length; i < len; i++){
19786             var d = this.prepareData(records[i].data, i, records[i]);
19787             if(n){
19788                 this.tpl.insertBefore(n, d);
19789             }else{
19790                 
19791                 this.tpl.append(this.el, d);
19792             }
19793         }
19794         this.updateIndexes(index);
19795     },
19796
19797     onRemove : function(ds, record, index){
19798        // Roo.log('onRemove');
19799         this.clearSelections();
19800         var el = this.dataName  ?
19801             this.el.child('.roo-tpl-' + this.dataName) :
19802             this.el; 
19803         
19804         el.dom.removeChild(this.nodes[index]);
19805         this.updateIndexes(index);
19806     },
19807
19808     /**
19809      * Refresh an individual node.
19810      * @param {Number} index
19811      */
19812     refreshNode : function(index){
19813         this.onUpdate(this.store, this.store.getAt(index));
19814     },
19815
19816     updateIndexes : function(startIndex, endIndex){
19817         var ns = this.nodes;
19818         startIndex = startIndex || 0;
19819         endIndex = endIndex || ns.length - 1;
19820         for(var i = startIndex; i <= endIndex; i++){
19821             ns[i].nodeIndex = i;
19822         }
19823     },
19824
19825     /**
19826      * Changes the data store this view uses and refresh the view.
19827      * @param {Store} store
19828      */
19829     setStore : function(store, initial){
19830         if(!initial && this.store){
19831             this.store.un("datachanged", this.refresh);
19832             this.store.un("add", this.onAdd);
19833             this.store.un("remove", this.onRemove);
19834             this.store.un("update", this.onUpdate);
19835             this.store.un("clear", this.refresh);
19836             this.store.un("beforeload", this.onBeforeLoad);
19837             this.store.un("load", this.onLoad);
19838             this.store.un("loadexception", this.onLoad);
19839         }
19840         if(store){
19841           
19842             store.on("datachanged", this.refresh, this);
19843             store.on("add", this.onAdd, this);
19844             store.on("remove", this.onRemove, this);
19845             store.on("update", this.onUpdate, this);
19846             store.on("clear", this.refresh, this);
19847             store.on("beforeload", this.onBeforeLoad, this);
19848             store.on("load", this.onLoad, this);
19849             store.on("loadexception", this.onLoad, this);
19850         }
19851         
19852         if(store){
19853             this.refresh();
19854         }
19855     },
19856     /**
19857      * onbeforeLoad - masks the loading area.
19858      *
19859      */
19860     onBeforeLoad : function(store,opts)
19861     {
19862          //Roo.log('onBeforeLoad');   
19863         if (!opts.add) {
19864             this.el.update("");
19865         }
19866         this.el.mask(this.mask ? this.mask : "Loading" ); 
19867     },
19868     onLoad : function ()
19869     {
19870         this.el.unmask();
19871     },
19872     
19873
19874     /**
19875      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19876      * @param {HTMLElement} node
19877      * @return {HTMLElement} The template node
19878      */
19879     findItemFromChild : function(node){
19880         var el = this.dataName  ?
19881             this.el.child('.roo-tpl-' + this.dataName,true) :
19882             this.el.dom; 
19883         
19884         if(!node || node.parentNode == el){
19885                     return node;
19886             }
19887             var p = node.parentNode;
19888             while(p && p != el){
19889             if(p.parentNode == el){
19890                 return p;
19891             }
19892             p = p.parentNode;
19893         }
19894             return null;
19895     },
19896
19897     /** @ignore */
19898     onClick : function(e){
19899         var item = this.findItemFromChild(e.getTarget());
19900         if(item){
19901             var index = this.indexOf(item);
19902             if(this.onItemClick(item, index, e) !== false){
19903                 this.fireEvent("click", this, index, item, e);
19904             }
19905         }else{
19906             this.clearSelections();
19907         }
19908     },
19909
19910     /** @ignore */
19911     onContextMenu : function(e){
19912         var item = this.findItemFromChild(e.getTarget());
19913         if(item){
19914             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19915         }
19916     },
19917
19918     /** @ignore */
19919     onDblClick : function(e){
19920         var item = this.findItemFromChild(e.getTarget());
19921         if(item){
19922             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19923         }
19924     },
19925
19926     onItemClick : function(item, index, e)
19927     {
19928         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19929             return false;
19930         }
19931         if (this.toggleSelect) {
19932             var m = this.isSelected(item) ? 'unselect' : 'select';
19933             //Roo.log(m);
19934             var _t = this;
19935             _t[m](item, true, false);
19936             return true;
19937         }
19938         if(this.multiSelect || this.singleSelect){
19939             if(this.multiSelect && e.shiftKey && this.lastSelection){
19940                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19941             }else{
19942                 this.select(item, this.multiSelect && e.ctrlKey);
19943                 this.lastSelection = item;
19944             }
19945             
19946             if(!this.tickable){
19947                 e.preventDefault();
19948             }
19949             
19950         }
19951         return true;
19952     },
19953
19954     /**
19955      * Get the number of selected nodes.
19956      * @return {Number}
19957      */
19958     getSelectionCount : function(){
19959         return this.selections.length;
19960     },
19961
19962     /**
19963      * Get the currently selected nodes.
19964      * @return {Array} An array of HTMLElements
19965      */
19966     getSelectedNodes : function(){
19967         return this.selections;
19968     },
19969
19970     /**
19971      * Get the indexes of the selected nodes.
19972      * @return {Array}
19973      */
19974     getSelectedIndexes : function(){
19975         var indexes = [], s = this.selections;
19976         for(var i = 0, len = s.length; i < len; i++){
19977             indexes.push(s[i].nodeIndex);
19978         }
19979         return indexes;
19980     },
19981
19982     /**
19983      * Clear all selections
19984      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19985      */
19986     clearSelections : function(suppressEvent){
19987         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19988             this.cmp.elements = this.selections;
19989             this.cmp.removeClass(this.selectedClass);
19990             this.selections = [];
19991             if(!suppressEvent){
19992                 this.fireEvent("selectionchange", this, this.selections);
19993             }
19994         }
19995     },
19996
19997     /**
19998      * Returns true if the passed node is selected
19999      * @param {HTMLElement/Number} node The node or node index
20000      * @return {Boolean}
20001      */
20002     isSelected : function(node){
20003         var s = this.selections;
20004         if(s.length < 1){
20005             return false;
20006         }
20007         node = this.getNode(node);
20008         return s.indexOf(node) !== -1;
20009     },
20010
20011     /**
20012      * Selects nodes.
20013      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20014      * @param {Boolean} keepExisting (optional) true to keep existing selections
20015      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20016      */
20017     select : function(nodeInfo, keepExisting, suppressEvent){
20018         if(nodeInfo instanceof Array){
20019             if(!keepExisting){
20020                 this.clearSelections(true);
20021             }
20022             for(var i = 0, len = nodeInfo.length; i < len; i++){
20023                 this.select(nodeInfo[i], true, true);
20024             }
20025             return;
20026         } 
20027         var node = this.getNode(nodeInfo);
20028         if(!node || this.isSelected(node)){
20029             return; // already selected.
20030         }
20031         if(!keepExisting){
20032             this.clearSelections(true);
20033         }
20034         
20035         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
20036             Roo.fly(node).addClass(this.selectedClass);
20037             this.selections.push(node);
20038             if(!suppressEvent){
20039                 this.fireEvent("selectionchange", this, this.selections);
20040             }
20041         }
20042         
20043         
20044     },
20045       /**
20046      * Unselects nodes.
20047      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
20048      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
20049      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
20050      */
20051     unselect : function(nodeInfo, keepExisting, suppressEvent)
20052     {
20053         if(nodeInfo instanceof Array){
20054             Roo.each(this.selections, function(s) {
20055                 this.unselect(s, nodeInfo);
20056             }, this);
20057             return;
20058         }
20059         var node = this.getNode(nodeInfo);
20060         if(!node || !this.isSelected(node)){
20061             //Roo.log("not selected");
20062             return; // not selected.
20063         }
20064         // fireevent???
20065         var ns = [];
20066         Roo.each(this.selections, function(s) {
20067             if (s == node ) {
20068                 Roo.fly(node).removeClass(this.selectedClass);
20069
20070                 return;
20071             }
20072             ns.push(s);
20073         },this);
20074         
20075         this.selections= ns;
20076         this.fireEvent("selectionchange", this, this.selections);
20077     },
20078
20079     /**
20080      * Gets a template node.
20081      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20082      * @return {HTMLElement} The node or null if it wasn't found
20083      */
20084     getNode : function(nodeInfo){
20085         if(typeof nodeInfo == "string"){
20086             return document.getElementById(nodeInfo);
20087         }else if(typeof nodeInfo == "number"){
20088             return this.nodes[nodeInfo];
20089         }
20090         return nodeInfo;
20091     },
20092
20093     /**
20094      * Gets a range template nodes.
20095      * @param {Number} startIndex
20096      * @param {Number} endIndex
20097      * @return {Array} An array of nodes
20098      */
20099     getNodes : function(start, end){
20100         var ns = this.nodes;
20101         start = start || 0;
20102         end = typeof end == "undefined" ? ns.length - 1 : end;
20103         var nodes = [];
20104         if(start <= end){
20105             for(var i = start; i <= end; i++){
20106                 nodes.push(ns[i]);
20107             }
20108         } else{
20109             for(var i = start; i >= end; i--){
20110                 nodes.push(ns[i]);
20111             }
20112         }
20113         return nodes;
20114     },
20115
20116     /**
20117      * Finds the index of the passed node
20118      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
20119      * @return {Number} The index of the node or -1
20120      */
20121     indexOf : function(node){
20122         node = this.getNode(node);
20123         if(typeof node.nodeIndex == "number"){
20124             return node.nodeIndex;
20125         }
20126         var ns = this.nodes;
20127         for(var i = 0, len = ns.length; i < len; i++){
20128             if(ns[i] == node){
20129                 return i;
20130             }
20131         }
20132         return -1;
20133     }
20134 });
20135 /*
20136  * - LGPL
20137  *
20138  * based on jquery fullcalendar
20139  * 
20140  */
20141
20142 Roo.bootstrap = Roo.bootstrap || {};
20143 /**
20144  * @class Roo.bootstrap.Calendar
20145  * @extends Roo.bootstrap.Component
20146  * Bootstrap Calendar class
20147  * @cfg {Boolean} loadMask (true|false) default false
20148  * @cfg {Object} header generate the user specific header of the calendar, default false
20149
20150  * @constructor
20151  * Create a new Container
20152  * @param {Object} config The config object
20153  */
20154
20155
20156
20157 Roo.bootstrap.Calendar = function(config){
20158     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
20159      this.addEvents({
20160         /**
20161              * @event select
20162              * Fires when a date is selected
20163              * @param {DatePicker} this
20164              * @param {Date} date The selected date
20165              */
20166         'select': true,
20167         /**
20168              * @event monthchange
20169              * Fires when the displayed month changes 
20170              * @param {DatePicker} this
20171              * @param {Date} date The selected month
20172              */
20173         'monthchange': true,
20174         /**
20175              * @event evententer
20176              * Fires when mouse over an event
20177              * @param {Calendar} this
20178              * @param {event} Event
20179              */
20180         'evententer': true,
20181         /**
20182              * @event eventleave
20183              * Fires when the mouse leaves an
20184              * @param {Calendar} this
20185              * @param {event}
20186              */
20187         'eventleave': true,
20188         /**
20189              * @event eventclick
20190              * Fires when the mouse click an
20191              * @param {Calendar} this
20192              * @param {event}
20193              */
20194         'eventclick': true
20195         
20196     });
20197
20198 };
20199
20200 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
20201     
20202           /**
20203      * @cfg {Roo.data.Store} store
20204      * The data source for the calendar
20205      */
20206         store : false,
20207      /**
20208      * @cfg {Number} startDay
20209      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
20210      */
20211     startDay : 0,
20212     
20213     loadMask : false,
20214     
20215     header : false,
20216       
20217     getAutoCreate : function(){
20218         
20219         
20220         var fc_button = function(name, corner, style, content ) {
20221             return Roo.apply({},{
20222                 tag : 'span',
20223                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
20224                          (corner.length ?
20225                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
20226                             ''
20227                         ),
20228                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
20229                 unselectable: 'on'
20230             });
20231         };
20232         
20233         var header = {};
20234         
20235         if(!this.header){
20236             header = {
20237                 tag : 'table',
20238                 cls : 'fc-header',
20239                 style : 'width:100%',
20240                 cn : [
20241                     {
20242                         tag: 'tr',
20243                         cn : [
20244                             {
20245                                 tag : 'td',
20246                                 cls : 'fc-header-left',
20247                                 cn : [
20248                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
20249                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
20250                                     { tag: 'span', cls: 'fc-header-space' },
20251                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
20252
20253
20254                                 ]
20255                             },
20256
20257                             {
20258                                 tag : 'td',
20259                                 cls : 'fc-header-center',
20260                                 cn : [
20261                                     {
20262                                         tag: 'span',
20263                                         cls: 'fc-header-title',
20264                                         cn : {
20265                                             tag: 'H2',
20266                                             html : 'month / year'
20267                                         }
20268                                     }
20269
20270                                 ]
20271                             },
20272                             {
20273                                 tag : 'td',
20274                                 cls : 'fc-header-right',
20275                                 cn : [
20276                               /*      fc_button('month', 'left', '', 'month' ),
20277                                     fc_button('week', '', '', 'week' ),
20278                                     fc_button('day', 'right', '', 'day' )
20279                                 */    
20280
20281                                 ]
20282                             }
20283
20284                         ]
20285                     }
20286                 ]
20287             };
20288         }
20289         
20290         header = this.header;
20291         
20292        
20293         var cal_heads = function() {
20294             var ret = [];
20295             // fixme - handle this.
20296             
20297             for (var i =0; i < Date.dayNames.length; i++) {
20298                 var d = Date.dayNames[i];
20299                 ret.push({
20300                     tag: 'th',
20301                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
20302                     html : d.substring(0,3)
20303                 });
20304                 
20305             }
20306             ret[0].cls += ' fc-first';
20307             ret[6].cls += ' fc-last';
20308             return ret;
20309         };
20310         var cal_cell = function(n) {
20311             return  {
20312                 tag: 'td',
20313                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
20314                 cn : [
20315                     {
20316                         cn : [
20317                             {
20318                                 cls: 'fc-day-number',
20319                                 html: 'D'
20320                             },
20321                             {
20322                                 cls: 'fc-day-content',
20323                              
20324                                 cn : [
20325                                      {
20326                                         style: 'position: relative;' // height: 17px;
20327                                     }
20328                                 ]
20329                             }
20330                             
20331                             
20332                         ]
20333                     }
20334                 ]
20335                 
20336             }
20337         };
20338         var cal_rows = function() {
20339             
20340             var ret = [];
20341             for (var r = 0; r < 6; r++) {
20342                 var row= {
20343                     tag : 'tr',
20344                     cls : 'fc-week',
20345                     cn : []
20346                 };
20347                 
20348                 for (var i =0; i < Date.dayNames.length; i++) {
20349                     var d = Date.dayNames[i];
20350                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
20351
20352                 }
20353                 row.cn[0].cls+=' fc-first';
20354                 row.cn[0].cn[0].style = 'min-height:90px';
20355                 row.cn[6].cls+=' fc-last';
20356                 ret.push(row);
20357                 
20358             }
20359             ret[0].cls += ' fc-first';
20360             ret[4].cls += ' fc-prev-last';
20361             ret[5].cls += ' fc-last';
20362             return ret;
20363             
20364         };
20365         
20366         var cal_table = {
20367             tag: 'table',
20368             cls: 'fc-border-separate',
20369             style : 'width:100%',
20370             cellspacing  : 0,
20371             cn : [
20372                 { 
20373                     tag: 'thead',
20374                     cn : [
20375                         { 
20376                             tag: 'tr',
20377                             cls : 'fc-first fc-last',
20378                             cn : cal_heads()
20379                         }
20380                     ]
20381                 },
20382                 { 
20383                     tag: 'tbody',
20384                     cn : cal_rows()
20385                 }
20386                   
20387             ]
20388         };
20389          
20390          var cfg = {
20391             cls : 'fc fc-ltr',
20392             cn : [
20393                 header,
20394                 {
20395                     cls : 'fc-content',
20396                     style : "position: relative;",
20397                     cn : [
20398                         {
20399                             cls : 'fc-view fc-view-month fc-grid',
20400                             style : 'position: relative',
20401                             unselectable : 'on',
20402                             cn : [
20403                                 {
20404                                     cls : 'fc-event-container',
20405                                     style : 'position:absolute;z-index:8;top:0;left:0;'
20406                                 },
20407                                 cal_table
20408                             ]
20409                         }
20410                     ]
20411     
20412                 }
20413            ] 
20414             
20415         };
20416         
20417          
20418         
20419         return cfg;
20420     },
20421     
20422     
20423     initEvents : function()
20424     {
20425         if(!this.store){
20426             throw "can not find store for calendar";
20427         }
20428         
20429         var mark = {
20430             tag: "div",
20431             cls:"x-dlg-mask",
20432             style: "text-align:center",
20433             cn: [
20434                 {
20435                     tag: "div",
20436                     style: "background-color:white;width:50%;margin:250 auto",
20437                     cn: [
20438                         {
20439                             tag: "img",
20440                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
20441                         },
20442                         {
20443                             tag: "span",
20444                             html: "Loading"
20445                         }
20446                         
20447                     ]
20448                 }
20449             ]
20450         };
20451         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
20452         
20453         var size = this.el.select('.fc-content', true).first().getSize();
20454         this.maskEl.setSize(size.width, size.height);
20455         this.maskEl.enableDisplayMode("block");
20456         if(!this.loadMask){
20457             this.maskEl.hide();
20458         }
20459         
20460         this.store = Roo.factory(this.store, Roo.data);
20461         this.store.on('load', this.onLoad, this);
20462         this.store.on('beforeload', this.onBeforeLoad, this);
20463         
20464         this.resize();
20465         
20466         this.cells = this.el.select('.fc-day',true);
20467         //Roo.log(this.cells);
20468         this.textNodes = this.el.query('.fc-day-number');
20469         this.cells.addClassOnOver('fc-state-hover');
20470         
20471         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20472         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20473         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20474         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20475         
20476         this.on('monthchange', this.onMonthChange, this);
20477         
20478         this.update(new Date().clearTime());
20479     },
20480     
20481     resize : function() {
20482         var sz  = this.el.getSize();
20483         
20484         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20485         this.el.select('.fc-day-content div',true).setHeight(34);
20486     },
20487     
20488     
20489     // private
20490     showPrevMonth : function(e){
20491         this.update(this.activeDate.add("mo", -1));
20492     },
20493     showToday : function(e){
20494         this.update(new Date().clearTime());
20495     },
20496     // private
20497     showNextMonth : function(e){
20498         this.update(this.activeDate.add("mo", 1));
20499     },
20500
20501     // private
20502     showPrevYear : function(){
20503         this.update(this.activeDate.add("y", -1));
20504     },
20505
20506     // private
20507     showNextYear : function(){
20508         this.update(this.activeDate.add("y", 1));
20509     },
20510
20511     
20512    // private
20513     update : function(date)
20514     {
20515         var vd = this.activeDate;
20516         this.activeDate = date;
20517 //        if(vd && this.el){
20518 //            var t = date.getTime();
20519 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20520 //                Roo.log('using add remove');
20521 //                
20522 //                this.fireEvent('monthchange', this, date);
20523 //                
20524 //                this.cells.removeClass("fc-state-highlight");
20525 //                this.cells.each(function(c){
20526 //                   if(c.dateValue == t){
20527 //                       c.addClass("fc-state-highlight");
20528 //                       setTimeout(function(){
20529 //                            try{c.dom.firstChild.focus();}catch(e){}
20530 //                       }, 50);
20531 //                       return false;
20532 //                   }
20533 //                   return true;
20534 //                });
20535 //                return;
20536 //            }
20537 //        }
20538         
20539         var days = date.getDaysInMonth();
20540         
20541         var firstOfMonth = date.getFirstDateOfMonth();
20542         var startingPos = firstOfMonth.getDay()-this.startDay;
20543         
20544         if(startingPos < this.startDay){
20545             startingPos += 7;
20546         }
20547         
20548         var pm = date.add(Date.MONTH, -1);
20549         var prevStart = pm.getDaysInMonth()-startingPos;
20550 //        
20551         this.cells = this.el.select('.fc-day',true);
20552         this.textNodes = this.el.query('.fc-day-number');
20553         this.cells.addClassOnOver('fc-state-hover');
20554         
20555         var cells = this.cells.elements;
20556         var textEls = this.textNodes;
20557         
20558         Roo.each(cells, function(cell){
20559             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20560         });
20561         
20562         days += startingPos;
20563
20564         // convert everything to numbers so it's fast
20565         var day = 86400000;
20566         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20567         //Roo.log(d);
20568         //Roo.log(pm);
20569         //Roo.log(prevStart);
20570         
20571         var today = new Date().clearTime().getTime();
20572         var sel = date.clearTime().getTime();
20573         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20574         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20575         var ddMatch = this.disabledDatesRE;
20576         var ddText = this.disabledDatesText;
20577         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20578         var ddaysText = this.disabledDaysText;
20579         var format = this.format;
20580         
20581         var setCellClass = function(cal, cell){
20582             cell.row = 0;
20583             cell.events = [];
20584             cell.more = [];
20585             //Roo.log('set Cell Class');
20586             cell.title = "";
20587             var t = d.getTime();
20588             
20589             //Roo.log(d);
20590             
20591             cell.dateValue = t;
20592             if(t == today){
20593                 cell.className += " fc-today";
20594                 cell.className += " fc-state-highlight";
20595                 cell.title = cal.todayText;
20596             }
20597             if(t == sel){
20598                 // disable highlight in other month..
20599                 //cell.className += " fc-state-highlight";
20600                 
20601             }
20602             // disabling
20603             if(t < min) {
20604                 cell.className = " fc-state-disabled";
20605                 cell.title = cal.minText;
20606                 return;
20607             }
20608             if(t > max) {
20609                 cell.className = " fc-state-disabled";
20610                 cell.title = cal.maxText;
20611                 return;
20612             }
20613             if(ddays){
20614                 if(ddays.indexOf(d.getDay()) != -1){
20615                     cell.title = ddaysText;
20616                     cell.className = " fc-state-disabled";
20617                 }
20618             }
20619             if(ddMatch && format){
20620                 var fvalue = d.dateFormat(format);
20621                 if(ddMatch.test(fvalue)){
20622                     cell.title = ddText.replace("%0", fvalue);
20623                     cell.className = " fc-state-disabled";
20624                 }
20625             }
20626             
20627             if (!cell.initialClassName) {
20628                 cell.initialClassName = cell.dom.className;
20629             }
20630             
20631             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20632         };
20633
20634         var i = 0;
20635         
20636         for(; i < startingPos; i++) {
20637             textEls[i].innerHTML = (++prevStart);
20638             d.setDate(d.getDate()+1);
20639             
20640             cells[i].className = "fc-past fc-other-month";
20641             setCellClass(this, cells[i]);
20642         }
20643         
20644         var intDay = 0;
20645         
20646         for(; i < days; i++){
20647             intDay = i - startingPos + 1;
20648             textEls[i].innerHTML = (intDay);
20649             d.setDate(d.getDate()+1);
20650             
20651             cells[i].className = ''; // "x-date-active";
20652             setCellClass(this, cells[i]);
20653         }
20654         var extraDays = 0;
20655         
20656         for(; i < 42; i++) {
20657             textEls[i].innerHTML = (++extraDays);
20658             d.setDate(d.getDate()+1);
20659             
20660             cells[i].className = "fc-future fc-other-month";
20661             setCellClass(this, cells[i]);
20662         }
20663         
20664         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20665         
20666         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20667         
20668         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20669         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20670         
20671         if(totalRows != 6){
20672             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20673             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20674         }
20675         
20676         this.fireEvent('monthchange', this, date);
20677         
20678         
20679         /*
20680         if(!this.internalRender){
20681             var main = this.el.dom.firstChild;
20682             var w = main.offsetWidth;
20683             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20684             Roo.fly(main).setWidth(w);
20685             this.internalRender = true;
20686             // opera does not respect the auto grow header center column
20687             // then, after it gets a width opera refuses to recalculate
20688             // without a second pass
20689             if(Roo.isOpera && !this.secondPass){
20690                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20691                 this.secondPass = true;
20692                 this.update.defer(10, this, [date]);
20693             }
20694         }
20695         */
20696         
20697     },
20698     
20699     findCell : function(dt) {
20700         dt = dt.clearTime().getTime();
20701         var ret = false;
20702         this.cells.each(function(c){
20703             //Roo.log("check " +c.dateValue + '?=' + dt);
20704             if(c.dateValue == dt){
20705                 ret = c;
20706                 return false;
20707             }
20708             return true;
20709         });
20710         
20711         return ret;
20712     },
20713     
20714     findCells : function(ev) {
20715         var s = ev.start.clone().clearTime().getTime();
20716        // Roo.log(s);
20717         var e= ev.end.clone().clearTime().getTime();
20718        // Roo.log(e);
20719         var ret = [];
20720         this.cells.each(function(c){
20721              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20722             
20723             if(c.dateValue > e){
20724                 return ;
20725             }
20726             if(c.dateValue < s){
20727                 return ;
20728             }
20729             ret.push(c);
20730         });
20731         
20732         return ret;    
20733     },
20734     
20735 //    findBestRow: function(cells)
20736 //    {
20737 //        var ret = 0;
20738 //        
20739 //        for (var i =0 ; i < cells.length;i++) {
20740 //            ret  = Math.max(cells[i].rows || 0,ret);
20741 //        }
20742 //        return ret;
20743 //        
20744 //    },
20745     
20746     
20747     addItem : function(ev)
20748     {
20749         // look for vertical location slot in
20750         var cells = this.findCells(ev);
20751         
20752 //        ev.row = this.findBestRow(cells);
20753         
20754         // work out the location.
20755         
20756         var crow = false;
20757         var rows = [];
20758         for(var i =0; i < cells.length; i++) {
20759             
20760             cells[i].row = cells[0].row;
20761             
20762             if(i == 0){
20763                 cells[i].row = cells[i].row + 1;
20764             }
20765             
20766             if (!crow) {
20767                 crow = {
20768                     start : cells[i],
20769                     end :  cells[i]
20770                 };
20771                 continue;
20772             }
20773             if (crow.start.getY() == cells[i].getY()) {
20774                 // on same row.
20775                 crow.end = cells[i];
20776                 continue;
20777             }
20778             // different row.
20779             rows.push(crow);
20780             crow = {
20781                 start: cells[i],
20782                 end : cells[i]
20783             };
20784             
20785         }
20786         
20787         rows.push(crow);
20788         ev.els = [];
20789         ev.rows = rows;
20790         ev.cells = cells;
20791         
20792         cells[0].events.push(ev);
20793         
20794         this.calevents.push(ev);
20795     },
20796     
20797     clearEvents: function() {
20798         
20799         if(!this.calevents){
20800             return;
20801         }
20802         
20803         Roo.each(this.cells.elements, function(c){
20804             c.row = 0;
20805             c.events = [];
20806             c.more = [];
20807         });
20808         
20809         Roo.each(this.calevents, function(e) {
20810             Roo.each(e.els, function(el) {
20811                 el.un('mouseenter' ,this.onEventEnter, this);
20812                 el.un('mouseleave' ,this.onEventLeave, this);
20813                 el.remove();
20814             },this);
20815         },this);
20816         
20817         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20818             e.remove();
20819         });
20820         
20821     },
20822     
20823     renderEvents: function()
20824     {   
20825         var _this = this;
20826         
20827         this.cells.each(function(c) {
20828             
20829             if(c.row < 5){
20830                 return;
20831             }
20832             
20833             var ev = c.events;
20834             
20835             var r = 4;
20836             if(c.row != c.events.length){
20837                 r = 4 - (4 - (c.row - c.events.length));
20838             }
20839             
20840             c.events = ev.slice(0, r);
20841             c.more = ev.slice(r);
20842             
20843             if(c.more.length && c.more.length == 1){
20844                 c.events.push(c.more.pop());
20845             }
20846             
20847             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20848             
20849         });
20850             
20851         this.cells.each(function(c) {
20852             
20853             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20854             
20855             
20856             for (var e = 0; e < c.events.length; e++){
20857                 var ev = c.events[e];
20858                 var rows = ev.rows;
20859                 
20860                 for(var i = 0; i < rows.length; i++) {
20861                 
20862                     // how many rows should it span..
20863
20864                     var  cfg = {
20865                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20866                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20867
20868                         unselectable : "on",
20869                         cn : [
20870                             {
20871                                 cls: 'fc-event-inner',
20872                                 cn : [
20873     //                                {
20874     //                                  tag:'span',
20875     //                                  cls: 'fc-event-time',
20876     //                                  html : cells.length > 1 ? '' : ev.time
20877     //                                },
20878                                     {
20879                                       tag:'span',
20880                                       cls: 'fc-event-title',
20881                                       html : String.format('{0}', ev.title)
20882                                     }
20883
20884
20885                                 ]
20886                             },
20887                             {
20888                                 cls: 'ui-resizable-handle ui-resizable-e',
20889                                 html : '&nbsp;&nbsp;&nbsp'
20890                             }
20891
20892                         ]
20893                     };
20894
20895                     if (i == 0) {
20896                         cfg.cls += ' fc-event-start';
20897                     }
20898                     if ((i+1) == rows.length) {
20899                         cfg.cls += ' fc-event-end';
20900                     }
20901
20902                     var ctr = _this.el.select('.fc-event-container',true).first();
20903                     var cg = ctr.createChild(cfg);
20904
20905                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20906                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20907
20908                     var r = (c.more.length) ? 1 : 0;
20909                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20910                     cg.setWidth(ebox.right - sbox.x -2);
20911
20912                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20913                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20914                     cg.on('click', _this.onEventClick, _this, ev);
20915
20916                     ev.els.push(cg);
20917                     
20918                 }
20919                 
20920             }
20921             
20922             
20923             if(c.more.length){
20924                 var  cfg = {
20925                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20926                     style : 'position: absolute',
20927                     unselectable : "on",
20928                     cn : [
20929                         {
20930                             cls: 'fc-event-inner',
20931                             cn : [
20932                                 {
20933                                   tag:'span',
20934                                   cls: 'fc-event-title',
20935                                   html : 'More'
20936                                 }
20937
20938
20939                             ]
20940                         },
20941                         {
20942                             cls: 'ui-resizable-handle ui-resizable-e',
20943                             html : '&nbsp;&nbsp;&nbsp'
20944                         }
20945
20946                     ]
20947                 };
20948
20949                 var ctr = _this.el.select('.fc-event-container',true).first();
20950                 var cg = ctr.createChild(cfg);
20951
20952                 var sbox = c.select('.fc-day-content',true).first().getBox();
20953                 var ebox = c.select('.fc-day-content',true).first().getBox();
20954                 //Roo.log(cg);
20955                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20956                 cg.setWidth(ebox.right - sbox.x -2);
20957
20958                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20959                 
20960             }
20961             
20962         });
20963         
20964         
20965         
20966     },
20967     
20968     onEventEnter: function (e, el,event,d) {
20969         this.fireEvent('evententer', this, el, event);
20970     },
20971     
20972     onEventLeave: function (e, el,event,d) {
20973         this.fireEvent('eventleave', this, el, event);
20974     },
20975     
20976     onEventClick: function (e, el,event,d) {
20977         this.fireEvent('eventclick', this, el, event);
20978     },
20979     
20980     onMonthChange: function () {
20981         this.store.load();
20982     },
20983     
20984     onMoreEventClick: function(e, el, more)
20985     {
20986         var _this = this;
20987         
20988         this.calpopover.placement = 'right';
20989         this.calpopover.setTitle('More');
20990         
20991         this.calpopover.setContent('');
20992         
20993         var ctr = this.calpopover.el.select('.popover-content', true).first();
20994         
20995         Roo.each(more, function(m){
20996             var cfg = {
20997                 cls : 'fc-event-hori fc-event-draggable',
20998                 html : m.title
20999             };
21000             var cg = ctr.createChild(cfg);
21001             
21002             cg.on('click', _this.onEventClick, _this, m);
21003         });
21004         
21005         this.calpopover.show(el);
21006         
21007         
21008     },
21009     
21010     onLoad: function () 
21011     {   
21012         this.calevents = [];
21013         var cal = this;
21014         
21015         if(this.store.getCount() > 0){
21016             this.store.data.each(function(d){
21017                cal.addItem({
21018                     id : d.data.id,
21019                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
21020                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
21021                     time : d.data.start_time,
21022                     title : d.data.title,
21023                     description : d.data.description,
21024                     venue : d.data.venue
21025                 });
21026             });
21027         }
21028         
21029         this.renderEvents();
21030         
21031         if(this.calevents.length && this.loadMask){
21032             this.maskEl.hide();
21033         }
21034     },
21035     
21036     onBeforeLoad: function()
21037     {
21038         this.clearEvents();
21039         if(this.loadMask){
21040             this.maskEl.show();
21041         }
21042     }
21043 });
21044
21045  
21046  /*
21047  * - LGPL
21048  *
21049  * element
21050  * 
21051  */
21052
21053 /**
21054  * @class Roo.bootstrap.Popover
21055  * @extends Roo.bootstrap.Component
21056  * Bootstrap Popover class
21057  * @cfg {String} html contents of the popover   (or false to use children..)
21058  * @cfg {String} title of popover (or false to hide)
21059  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
21060  * @cfg {String} trigger click || hover (or false to trigger manually)
21061  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
21062  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
21063  *      - if false and it has a 'parent' then it will be automatically added to that element
21064  *      - if string - Roo.get  will be called 
21065  * @cfg {Number} delay - delay before showing
21066  
21067  * @constructor
21068  * Create a new Popover
21069  * @param {Object} config The config object
21070  */
21071
21072 Roo.bootstrap.Popover = function(config){
21073     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
21074     
21075     this.addEvents({
21076         // raw events
21077          /**
21078          * @event show
21079          * After the popover show
21080          * 
21081          * @param {Roo.bootstrap.Popover} this
21082          */
21083         "show" : true,
21084         /**
21085          * @event hide
21086          * After the popover hide
21087          * 
21088          * @param {Roo.bootstrap.Popover} this
21089          */
21090         "hide" : true
21091     });
21092 };
21093
21094 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
21095     
21096     title: false,
21097     html: false,
21098     
21099     placement : 'right',
21100     trigger : 'hover', // hover
21101     modal : false,
21102     delay : 0,
21103     
21104     over: false,
21105     
21106     can_build_overlaid : false,
21107     
21108     maskEl : false, // the mask element
21109     headerEl : false,
21110     contentEl : false,
21111     alignEl : false, // when show is called with an element - this get's stored.
21112     
21113     getChildContainer : function()
21114     {
21115         return this.contentEl;
21116         
21117     },
21118     getPopoverHeader : function()
21119     {
21120         this.title = true; // flag not to hide it..
21121         this.headerEl.addClass('p-0');
21122         return this.headerEl
21123     },
21124     
21125     
21126     getAutoCreate : function(){
21127          
21128         var cfg = {
21129            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
21130            style: 'display:block',
21131            cn : [
21132                 {
21133                     cls : 'arrow'
21134                 },
21135                 {
21136                     cls : 'popover-inner ',
21137                     cn : [
21138                         {
21139                             tag: 'h3',
21140                             cls: 'popover-title popover-header',
21141                             html : this.title === false ? '' : this.title
21142                         },
21143                         {
21144                             cls : 'popover-content popover-body '  + (this.cls || ''),
21145                             html : this.html || ''
21146                         }
21147                     ]
21148                     
21149                 }
21150            ]
21151         };
21152         
21153         return cfg;
21154     },
21155     /**
21156      * @param {string} the title
21157      */
21158     setTitle: function(str)
21159     {
21160         this.title = str;
21161         if (this.el) {
21162             this.headerEl.dom.innerHTML = str;
21163         }
21164         
21165     },
21166     /**
21167      * @param {string} the body content
21168      */
21169     setContent: function(str)
21170     {
21171         this.html = str;
21172         if (this.contentEl) {
21173             this.contentEl.dom.innerHTML = str;
21174         }
21175         
21176     },
21177     // as it get's added to the bottom of the page.
21178     onRender : function(ct, position)
21179     {
21180         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
21181         
21182         
21183         
21184         if(!this.el){
21185             var cfg = Roo.apply({},  this.getAutoCreate());
21186             cfg.id = Roo.id();
21187             
21188             if (this.cls) {
21189                 cfg.cls += ' ' + this.cls;
21190             }
21191             if (this.style) {
21192                 cfg.style = this.style;
21193             }
21194             //Roo.log("adding to ");
21195             this.el = Roo.get(document.body).createChild(cfg, position);
21196 //            Roo.log(this.el);
21197         }
21198         
21199         this.contentEl = this.el.select('.popover-content',true).first();
21200         this.headerEl =  this.el.select('.popover-title',true).first();
21201         
21202         var nitems = [];
21203         if(typeof(this.items) != 'undefined'){
21204             var items = this.items;
21205             delete this.items;
21206
21207             for(var i =0;i < items.length;i++) {
21208                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
21209             }
21210         }
21211
21212         this.items = nitems;
21213         
21214         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
21215         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
21216         
21217         
21218         
21219         this.initEvents();
21220     },
21221     
21222     resizeMask : function()
21223     {
21224         this.maskEl.setSize(
21225             Roo.lib.Dom.getViewWidth(true),
21226             Roo.lib.Dom.getViewHeight(true)
21227         );
21228     },
21229     
21230     initEvents : function()
21231     {
21232         
21233         if (!this.modal) { 
21234             Roo.bootstrap.Popover.register(this);
21235         }
21236          
21237         this.arrowEl = this.el.select('.arrow',true).first();
21238         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
21239         this.el.enableDisplayMode('block');
21240         this.el.hide();
21241  
21242         
21243         if (this.over === false && !this.parent()) {
21244             return; 
21245         }
21246         if (this.triggers === false) {
21247             return;
21248         }
21249          
21250         // support parent
21251         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
21252         var triggers = this.trigger ? this.trigger.split(' ') : [];
21253         Roo.each(triggers, function(trigger) {
21254         
21255             if (trigger == 'click') {
21256                 on_el.on('click', this.toggle, this);
21257             } else if (trigger != 'manual') {
21258                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
21259                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
21260       
21261                 on_el.on(eventIn  ,this.enter, this);
21262                 on_el.on(eventOut, this.leave, this);
21263             }
21264         }, this);
21265     },
21266     
21267     
21268     // private
21269     timeout : null,
21270     hoverState : null,
21271     
21272     toggle : function () {
21273         this.hoverState == 'in' ? this.leave() : this.enter();
21274     },
21275     
21276     enter : function () {
21277         
21278         clearTimeout(this.timeout);
21279     
21280         this.hoverState = 'in';
21281     
21282         if (!this.delay || !this.delay.show) {
21283             this.show();
21284             return;
21285         }
21286         var _t = this;
21287         this.timeout = setTimeout(function () {
21288             if (_t.hoverState == 'in') {
21289                 _t.show();
21290             }
21291         }, this.delay.show)
21292     },
21293     
21294     leave : function() {
21295         clearTimeout(this.timeout);
21296     
21297         this.hoverState = 'out';
21298     
21299         if (!this.delay || !this.delay.hide) {
21300             this.hide();
21301             return;
21302         }
21303         var _t = this;
21304         this.timeout = setTimeout(function () {
21305             if (_t.hoverState == 'out') {
21306                 _t.hide();
21307             }
21308         }, this.delay.hide)
21309     },
21310     /**
21311      * Show the popover
21312      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
21313      * @param {string} (left|right|top|bottom) position
21314      */
21315     show : function (on_el, placement)
21316     {
21317         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
21318         on_el = on_el || false; // default to false
21319          
21320         if (!on_el) {
21321             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
21322                 on_el = this.parent().el;
21323             } else if (this.over) {
21324                 on_el = Roo.get(this.over);
21325             }
21326             
21327         }
21328         
21329         this.alignEl = Roo.get( on_el );
21330
21331         if (!this.el) {
21332             this.render(document.body);
21333         }
21334         
21335         
21336          
21337         
21338         if (this.title === false) {
21339             this.headerEl.hide();
21340         }
21341         
21342        
21343         this.el.show();
21344         this.el.dom.style.display = 'block';
21345          
21346  
21347         if (this.alignEl) {
21348             this.updatePosition(this.placement, true);
21349              
21350         } else {
21351             // this is usually just done by the builder = to show the popoup in the middle of the scren.
21352             var es = this.el.getSize();
21353             var x = Roo.lib.Dom.getViewWidth()/2;
21354             var y = Roo.lib.Dom.getViewHeight()/2;
21355             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
21356             
21357         }
21358
21359         
21360         //var arrow = this.el.select('.arrow',true).first();
21361         //arrow.set(align[2], 
21362         
21363         this.el.addClass('in');
21364         
21365          
21366         
21367         this.hoverState = 'in';
21368         
21369         if (this.modal) {
21370             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
21371             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21372             this.maskEl.dom.style.display = 'block';
21373             this.maskEl.addClass('show');
21374         }
21375         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
21376  
21377         this.fireEvent('show', this);
21378         
21379     },
21380     /**
21381      * fire this manually after loading a grid in the table for example
21382      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
21383      * @param {Boolean} try and move it if we cant get right position.
21384      */
21385     updatePosition : function(placement, try_move)
21386     {
21387         // allow for calling with no parameters
21388         placement = placement   ? placement :  this.placement;
21389         try_move = typeof(try_move) == 'undefined' ? true : try_move;
21390         
21391         this.el.removeClass([
21392             'fade','top','bottom', 'left', 'right','in',
21393             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
21394         ]);
21395         this.el.addClass(placement + ' bs-popover-' + placement);
21396         
21397         if (!this.alignEl ) {
21398             return false;
21399         }
21400         
21401         switch (placement) {
21402             case 'right':
21403                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
21404                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
21405                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21406                     //normal display... or moved up/down.
21407                     this.el.setXY(offset);
21408                     var xy = this.alignEl.getAnchorXY('tr', false);
21409                     xy[0]+=2;xy[1]+=5;
21410                     this.arrowEl.setXY(xy);
21411                     return true;
21412                 }
21413                 // continue through...
21414                 return this.updatePosition('left', false);
21415                 
21416             
21417             case 'left':
21418                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
21419                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
21420                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
21421                     //normal display... or moved up/down.
21422                     this.el.setXY(offset);
21423                     var xy = this.alignEl.getAnchorXY('tl', false);
21424                     xy[0]-=10;xy[1]+=5; // << fix me
21425                     this.arrowEl.setXY(xy);
21426                     return true;
21427                 }
21428                 // call self...
21429                 return this.updatePosition('right', false);
21430             
21431             case 'top':
21432                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
21433                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
21434                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21435                     //normal display... or moved up/down.
21436                     this.el.setXY(offset);
21437                     var xy = this.alignEl.getAnchorXY('t', false);
21438                     xy[1]-=10; // << fix me
21439                     this.arrowEl.setXY(xy);
21440                     return true;
21441                 }
21442                 // fall through
21443                return this.updatePosition('bottom', false);
21444             
21445             case 'bottom':
21446                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
21447                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
21448                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
21449                     //normal display... or moved up/down.
21450                     this.el.setXY(offset);
21451                     var xy = this.alignEl.getAnchorXY('b', false);
21452                      xy[1]+=2; // << fix me
21453                     this.arrowEl.setXY(xy);
21454                     return true;
21455                 }
21456                 // fall through
21457                 return this.updatePosition('top', false);
21458                 
21459             
21460         }
21461         
21462         
21463         return false;
21464     },
21465     
21466     hide : function()
21467     {
21468         this.el.setXY([0,0]);
21469         this.el.removeClass('in');
21470         this.el.hide();
21471         this.hoverState = null;
21472         this.maskEl.hide(); // always..
21473         this.fireEvent('hide', this);
21474     }
21475     
21476 });
21477
21478
21479 Roo.apply(Roo.bootstrap.Popover, {
21480
21481     alignment : {
21482         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21483         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21484         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21485         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21486     },
21487     
21488     zIndex : 20001,
21489
21490     clickHander : false,
21491     
21492     
21493
21494     onMouseDown : function(e)
21495     {
21496         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21497             /// what is nothing is showing..
21498             this.hideAll();
21499         }
21500          
21501     },
21502     
21503     
21504     popups : [],
21505     
21506     register : function(popup)
21507     {
21508         if (!Roo.bootstrap.Popover.clickHandler) {
21509             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21510         }
21511         // hide other popups.
21512         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21513         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21514         this.hideAll(); //<< why?
21515         //this.popups.push(popup);
21516     },
21517     hideAll : function()
21518     {
21519         this.popups.forEach(function(p) {
21520             p.hide();
21521         });
21522     },
21523     onShow : function() {
21524         Roo.bootstrap.Popover.popups.push(this);
21525     },
21526     onHide : function() {
21527         Roo.bootstrap.Popover.popups.remove(this);
21528     } 
21529
21530 });/*
21531  * - LGPL
21532  *
21533  * Card header - holder for the card header elements.
21534  * 
21535  */
21536
21537 /**
21538  * @class Roo.bootstrap.PopoverNav
21539  * @extends Roo.bootstrap.NavGroup
21540  * Bootstrap Popover header navigation class
21541  * @constructor
21542  * Create a new Popover Header Navigation 
21543  * @param {Object} config The config object
21544  */
21545
21546 Roo.bootstrap.PopoverNav = function(config){
21547     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21548 };
21549
21550 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21551     
21552     
21553     container_method : 'getPopoverHeader' 
21554     
21555      
21556     
21557     
21558    
21559 });
21560
21561  
21562
21563  /*
21564  * - LGPL
21565  *
21566  * Progress
21567  * 
21568  */
21569
21570 /**
21571  * @class Roo.bootstrap.Progress
21572  * @extends Roo.bootstrap.Component
21573  * Bootstrap Progress class
21574  * @cfg {Boolean} striped striped of the progress bar
21575  * @cfg {Boolean} active animated of the progress bar
21576  * 
21577  * 
21578  * @constructor
21579  * Create a new Progress
21580  * @param {Object} config The config object
21581  */
21582
21583 Roo.bootstrap.Progress = function(config){
21584     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21585 };
21586
21587 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21588     
21589     striped : false,
21590     active: false,
21591     
21592     getAutoCreate : function(){
21593         var cfg = {
21594             tag: 'div',
21595             cls: 'progress'
21596         };
21597         
21598         
21599         if(this.striped){
21600             cfg.cls += ' progress-striped';
21601         }
21602       
21603         if(this.active){
21604             cfg.cls += ' active';
21605         }
21606         
21607         
21608         return cfg;
21609     }
21610    
21611 });
21612
21613  
21614
21615  /*
21616  * - LGPL
21617  *
21618  * ProgressBar
21619  * 
21620  */
21621
21622 /**
21623  * @class Roo.bootstrap.ProgressBar
21624  * @extends Roo.bootstrap.Component
21625  * Bootstrap ProgressBar class
21626  * @cfg {Number} aria_valuenow aria-value now
21627  * @cfg {Number} aria_valuemin aria-value min
21628  * @cfg {Number} aria_valuemax aria-value max
21629  * @cfg {String} label label for the progress bar
21630  * @cfg {String} panel (success | info | warning | danger )
21631  * @cfg {String} role role of the progress bar
21632  * @cfg {String} sr_only text
21633  * 
21634  * 
21635  * @constructor
21636  * Create a new ProgressBar
21637  * @param {Object} config The config object
21638  */
21639
21640 Roo.bootstrap.ProgressBar = function(config){
21641     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21642 };
21643
21644 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21645     
21646     aria_valuenow : 0,
21647     aria_valuemin : 0,
21648     aria_valuemax : 100,
21649     label : false,
21650     panel : false,
21651     role : false,
21652     sr_only: false,
21653     
21654     getAutoCreate : function()
21655     {
21656         
21657         var cfg = {
21658             tag: 'div',
21659             cls: 'progress-bar',
21660             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21661         };
21662         
21663         if(this.sr_only){
21664             cfg.cn = {
21665                 tag: 'span',
21666                 cls: 'sr-only',
21667                 html: this.sr_only
21668             }
21669         }
21670         
21671         if(this.role){
21672             cfg.role = this.role;
21673         }
21674         
21675         if(this.aria_valuenow){
21676             cfg['aria-valuenow'] = this.aria_valuenow;
21677         }
21678         
21679         if(this.aria_valuemin){
21680             cfg['aria-valuemin'] = this.aria_valuemin;
21681         }
21682         
21683         if(this.aria_valuemax){
21684             cfg['aria-valuemax'] = this.aria_valuemax;
21685         }
21686         
21687         if(this.label && !this.sr_only){
21688             cfg.html = this.label;
21689         }
21690         
21691         if(this.panel){
21692             cfg.cls += ' progress-bar-' + this.panel;
21693         }
21694         
21695         return cfg;
21696     },
21697     
21698     update : function(aria_valuenow)
21699     {
21700         this.aria_valuenow = aria_valuenow;
21701         
21702         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21703     }
21704    
21705 });
21706
21707  
21708
21709  /*
21710  * - LGPL
21711  *
21712  * column
21713  * 
21714  */
21715
21716 /**
21717  * @class Roo.bootstrap.TabGroup
21718  * @extends Roo.bootstrap.Column
21719  * Bootstrap Column class
21720  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21721  * @cfg {Boolean} carousel true to make the group behave like a carousel
21722  * @cfg {Boolean} bullets show bullets for the panels
21723  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21724  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21725  * @cfg {Boolean} showarrow (true|false) show arrow default true
21726  * 
21727  * @constructor
21728  * Create a new TabGroup
21729  * @param {Object} config The config object
21730  */
21731
21732 Roo.bootstrap.TabGroup = function(config){
21733     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21734     if (!this.navId) {
21735         this.navId = Roo.id();
21736     }
21737     this.tabs = [];
21738     Roo.bootstrap.TabGroup.register(this);
21739     
21740 };
21741
21742 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21743     
21744     carousel : false,
21745     transition : false,
21746     bullets : 0,
21747     timer : 0,
21748     autoslide : false,
21749     slideFn : false,
21750     slideOnTouch : false,
21751     showarrow : true,
21752     
21753     getAutoCreate : function()
21754     {
21755         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21756         
21757         cfg.cls += ' tab-content';
21758         
21759         if (this.carousel) {
21760             cfg.cls += ' carousel slide';
21761             
21762             cfg.cn = [{
21763                cls : 'carousel-inner',
21764                cn : []
21765             }];
21766         
21767             if(this.bullets  && !Roo.isTouch){
21768                 
21769                 var bullets = {
21770                     cls : 'carousel-bullets',
21771                     cn : []
21772                 };
21773                
21774                 if(this.bullets_cls){
21775                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21776                 }
21777                 
21778                 bullets.cn.push({
21779                     cls : 'clear'
21780                 });
21781                 
21782                 cfg.cn[0].cn.push(bullets);
21783             }
21784             
21785             if(this.showarrow){
21786                 cfg.cn[0].cn.push({
21787                     tag : 'div',
21788                     class : 'carousel-arrow',
21789                     cn : [
21790                         {
21791                             tag : 'div',
21792                             class : 'carousel-prev',
21793                             cn : [
21794                                 {
21795                                     tag : 'i',
21796                                     class : 'fa fa-chevron-left'
21797                                 }
21798                             ]
21799                         },
21800                         {
21801                             tag : 'div',
21802                             class : 'carousel-next',
21803                             cn : [
21804                                 {
21805                                     tag : 'i',
21806                                     class : 'fa fa-chevron-right'
21807                                 }
21808                             ]
21809                         }
21810                     ]
21811                 });
21812             }
21813             
21814         }
21815         
21816         return cfg;
21817     },
21818     
21819     initEvents:  function()
21820     {
21821 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21822 //            this.el.on("touchstart", this.onTouchStart, this);
21823 //        }
21824         
21825         if(this.autoslide){
21826             var _this = this;
21827             
21828             this.slideFn = window.setInterval(function() {
21829                 _this.showPanelNext();
21830             }, this.timer);
21831         }
21832         
21833         if(this.showarrow){
21834             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21835             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21836         }
21837         
21838         
21839     },
21840     
21841 //    onTouchStart : function(e, el, o)
21842 //    {
21843 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21844 //            return;
21845 //        }
21846 //        
21847 //        this.showPanelNext();
21848 //    },
21849     
21850     
21851     getChildContainer : function()
21852     {
21853         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21854     },
21855     
21856     /**
21857     * register a Navigation item
21858     * @param {Roo.bootstrap.NavItem} the navitem to add
21859     */
21860     register : function(item)
21861     {
21862         this.tabs.push( item);
21863         item.navId = this.navId; // not really needed..
21864         this.addBullet();
21865     
21866     },
21867     
21868     getActivePanel : function()
21869     {
21870         var r = false;
21871         Roo.each(this.tabs, function(t) {
21872             if (t.active) {
21873                 r = t;
21874                 return false;
21875             }
21876             return null;
21877         });
21878         return r;
21879         
21880     },
21881     getPanelByName : function(n)
21882     {
21883         var r = false;
21884         Roo.each(this.tabs, function(t) {
21885             if (t.tabId == n) {
21886                 r = t;
21887                 return false;
21888             }
21889             return null;
21890         });
21891         return r;
21892     },
21893     indexOfPanel : function(p)
21894     {
21895         var r = false;
21896         Roo.each(this.tabs, function(t,i) {
21897             if (t.tabId == p.tabId) {
21898                 r = i;
21899                 return false;
21900             }
21901             return null;
21902         });
21903         return r;
21904     },
21905     /**
21906      * show a specific panel
21907      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21908      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21909      */
21910     showPanel : function (pan)
21911     {
21912         if(this.transition || typeof(pan) == 'undefined'){
21913             Roo.log("waiting for the transitionend");
21914             return false;
21915         }
21916         
21917         if (typeof(pan) == 'number') {
21918             pan = this.tabs[pan];
21919         }
21920         
21921         if (typeof(pan) == 'string') {
21922             pan = this.getPanelByName(pan);
21923         }
21924         
21925         var cur = this.getActivePanel();
21926         
21927         if(!pan || !cur){
21928             Roo.log('pan or acitve pan is undefined');
21929             return false;
21930         }
21931         
21932         if (pan.tabId == this.getActivePanel().tabId) {
21933             return true;
21934         }
21935         
21936         if (false === cur.fireEvent('beforedeactivate')) {
21937             return false;
21938         }
21939         
21940         if(this.bullets > 0 && !Roo.isTouch){
21941             this.setActiveBullet(this.indexOfPanel(pan));
21942         }
21943         
21944         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21945             
21946             //class="carousel-item carousel-item-next carousel-item-left"
21947             
21948             this.transition = true;
21949             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21950             var lr = dir == 'next' ? 'left' : 'right';
21951             pan.el.addClass(dir); // or prev
21952             pan.el.addClass('carousel-item-' + dir); // or prev
21953             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21954             cur.el.addClass(lr); // or right
21955             pan.el.addClass(lr);
21956             cur.el.addClass('carousel-item-' +lr); // or right
21957             pan.el.addClass('carousel-item-' +lr);
21958             
21959             
21960             var _this = this;
21961             cur.el.on('transitionend', function() {
21962                 Roo.log("trans end?");
21963                 
21964                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21965                 pan.setActive(true);
21966                 
21967                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21968                 cur.setActive(false);
21969                 
21970                 _this.transition = false;
21971                 
21972             }, this, { single:  true } );
21973             
21974             return true;
21975         }
21976         
21977         cur.setActive(false);
21978         pan.setActive(true);
21979         
21980         return true;
21981         
21982     },
21983     showPanelNext : function()
21984     {
21985         var i = this.indexOfPanel(this.getActivePanel());
21986         
21987         if (i >= this.tabs.length - 1 && !this.autoslide) {
21988             return;
21989         }
21990         
21991         if (i >= this.tabs.length - 1 && this.autoslide) {
21992             i = -1;
21993         }
21994         
21995         this.showPanel(this.tabs[i+1]);
21996     },
21997     
21998     showPanelPrev : function()
21999     {
22000         var i = this.indexOfPanel(this.getActivePanel());
22001         
22002         if (i  < 1 && !this.autoslide) {
22003             return;
22004         }
22005         
22006         if (i < 1 && this.autoslide) {
22007             i = this.tabs.length;
22008         }
22009         
22010         this.showPanel(this.tabs[i-1]);
22011     },
22012     
22013     
22014     addBullet: function()
22015     {
22016         if(!this.bullets || Roo.isTouch){
22017             return;
22018         }
22019         var ctr = this.el.select('.carousel-bullets',true).first();
22020         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
22021         var bullet = ctr.createChild({
22022             cls : 'bullet bullet-' + i
22023         },ctr.dom.lastChild);
22024         
22025         
22026         var _this = this;
22027         
22028         bullet.on('click', (function(e, el, o, ii, t){
22029
22030             e.preventDefault();
22031
22032             this.showPanel(ii);
22033
22034             if(this.autoslide && this.slideFn){
22035                 clearInterval(this.slideFn);
22036                 this.slideFn = window.setInterval(function() {
22037                     _this.showPanelNext();
22038                 }, this.timer);
22039             }
22040
22041         }).createDelegate(this, [i, bullet], true));
22042                 
22043         
22044     },
22045      
22046     setActiveBullet : function(i)
22047     {
22048         if(Roo.isTouch){
22049             return;
22050         }
22051         
22052         Roo.each(this.el.select('.bullet', true).elements, function(el){
22053             el.removeClass('selected');
22054         });
22055
22056         var bullet = this.el.select('.bullet-' + i, true).first();
22057         
22058         if(!bullet){
22059             return;
22060         }
22061         
22062         bullet.addClass('selected');
22063     }
22064     
22065     
22066   
22067 });
22068
22069  
22070
22071  
22072  
22073 Roo.apply(Roo.bootstrap.TabGroup, {
22074     
22075     groups: {},
22076      /**
22077     * register a Navigation Group
22078     * @param {Roo.bootstrap.NavGroup} the navgroup to add
22079     */
22080     register : function(navgrp)
22081     {
22082         this.groups[navgrp.navId] = navgrp;
22083         
22084     },
22085     /**
22086     * fetch a Navigation Group based on the navigation ID
22087     * if one does not exist , it will get created.
22088     * @param {string} the navgroup to add
22089     * @returns {Roo.bootstrap.NavGroup} the navgroup 
22090     */
22091     get: function(navId) {
22092         if (typeof(this.groups[navId]) == 'undefined') {
22093             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
22094         }
22095         return this.groups[navId] ;
22096     }
22097     
22098     
22099     
22100 });
22101
22102  /*
22103  * - LGPL
22104  *
22105  * TabPanel
22106  * 
22107  */
22108
22109 /**
22110  * @class Roo.bootstrap.TabPanel
22111  * @extends Roo.bootstrap.Component
22112  * Bootstrap TabPanel class
22113  * @cfg {Boolean} active panel active
22114  * @cfg {String} html panel content
22115  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
22116  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
22117  * @cfg {String} href click to link..
22118  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
22119  * 
22120  * 
22121  * @constructor
22122  * Create a new TabPanel
22123  * @param {Object} config The config object
22124  */
22125
22126 Roo.bootstrap.TabPanel = function(config){
22127     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
22128     this.addEvents({
22129         /**
22130              * @event changed
22131              * Fires when the active status changes
22132              * @param {Roo.bootstrap.TabPanel} this
22133              * @param {Boolean} state the new state
22134             
22135          */
22136         'changed': true,
22137         /**
22138              * @event beforedeactivate
22139              * Fires before a tab is de-activated - can be used to do validation on a form.
22140              * @param {Roo.bootstrap.TabPanel} this
22141              * @return {Boolean} false if there is an error
22142             
22143          */
22144         'beforedeactivate': true
22145      });
22146     
22147     this.tabId = this.tabId || Roo.id();
22148   
22149 };
22150
22151 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
22152     
22153     active: false,
22154     html: false,
22155     tabId: false,
22156     navId : false,
22157     href : '',
22158     touchSlide : false,
22159     getAutoCreate : function(){
22160         
22161         
22162         var cfg = {
22163             tag: 'div',
22164             // item is needed for carousel - not sure if it has any effect otherwise
22165             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
22166             html: this.html || ''
22167         };
22168         
22169         if(this.active){
22170             cfg.cls += ' active';
22171         }
22172         
22173         if(this.tabId){
22174             cfg.tabId = this.tabId;
22175         }
22176         
22177         
22178         
22179         return cfg;
22180     },
22181     
22182     initEvents:  function()
22183     {
22184         var p = this.parent();
22185         
22186         this.navId = this.navId || p.navId;
22187         
22188         if (typeof(this.navId) != 'undefined') {
22189             // not really needed.. but just in case.. parent should be a NavGroup.
22190             var tg = Roo.bootstrap.TabGroup.get(this.navId);
22191             
22192             tg.register(this);
22193             
22194             var i = tg.tabs.length - 1;
22195             
22196             if(this.active && tg.bullets > 0 && i < tg.bullets){
22197                 tg.setActiveBullet(i);
22198             }
22199         }
22200         
22201         this.el.on('click', this.onClick, this);
22202         
22203         if(Roo.isTouch && this.touchSlide){
22204             this.el.on("touchstart", this.onTouchStart, this);
22205             this.el.on("touchmove", this.onTouchMove, this);
22206             this.el.on("touchend", this.onTouchEnd, this);
22207         }
22208         
22209     },
22210     
22211     onRender : function(ct, position)
22212     {
22213         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
22214     },
22215     
22216     setActive : function(state)
22217     {
22218         Roo.log("panel - set active " + this.tabId + "=" + state);
22219         
22220         this.active = state;
22221         if (!state) {
22222             this.el.removeClass('active');
22223             
22224         } else  if (!this.el.hasClass('active')) {
22225             this.el.addClass('active');
22226         }
22227         
22228         this.fireEvent('changed', this, state);
22229     },
22230     
22231     onClick : function(e)
22232     {
22233         e.preventDefault();
22234         
22235         if(!this.href.length){
22236             return;
22237         }
22238         
22239         window.location.href = this.href;
22240     },
22241     
22242     startX : 0,
22243     startY : 0,
22244     endX : 0,
22245     endY : 0,
22246     swiping : false,
22247     
22248     onTouchStart : function(e)
22249     {
22250         this.swiping = false;
22251         
22252         this.startX = e.browserEvent.touches[0].clientX;
22253         this.startY = e.browserEvent.touches[0].clientY;
22254     },
22255     
22256     onTouchMove : function(e)
22257     {
22258         this.swiping = true;
22259         
22260         this.endX = e.browserEvent.touches[0].clientX;
22261         this.endY = e.browserEvent.touches[0].clientY;
22262     },
22263     
22264     onTouchEnd : function(e)
22265     {
22266         if(!this.swiping){
22267             this.onClick(e);
22268             return;
22269         }
22270         
22271         var tabGroup = this.parent();
22272         
22273         if(this.endX > this.startX){ // swiping right
22274             tabGroup.showPanelPrev();
22275             return;
22276         }
22277         
22278         if(this.startX > this.endX){ // swiping left
22279             tabGroup.showPanelNext();
22280             return;
22281         }
22282     }
22283     
22284     
22285 });
22286  
22287
22288  
22289
22290  /*
22291  * - LGPL
22292  *
22293  * DateField
22294  * 
22295  */
22296
22297 /**
22298  * @class Roo.bootstrap.DateField
22299  * @extends Roo.bootstrap.Input
22300  * Bootstrap DateField class
22301  * @cfg {Number} weekStart default 0
22302  * @cfg {String} viewMode default empty, (months|years)
22303  * @cfg {String} minViewMode default empty, (months|years)
22304  * @cfg {Number} startDate default -Infinity
22305  * @cfg {Number} endDate default Infinity
22306  * @cfg {Boolean} todayHighlight default false
22307  * @cfg {Boolean} todayBtn default false
22308  * @cfg {Boolean} calendarWeeks default false
22309  * @cfg {Object} daysOfWeekDisabled default empty
22310  * @cfg {Boolean} singleMode default false (true | false)
22311  * 
22312  * @cfg {Boolean} keyboardNavigation default true
22313  * @cfg {String} language default en
22314  * 
22315  * @constructor
22316  * Create a new DateField
22317  * @param {Object} config The config object
22318  */
22319
22320 Roo.bootstrap.DateField = function(config){
22321     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
22322      this.addEvents({
22323             /**
22324              * @event show
22325              * Fires when this field show.
22326              * @param {Roo.bootstrap.DateField} this
22327              * @param {Mixed} date The date value
22328              */
22329             show : true,
22330             /**
22331              * @event show
22332              * Fires when this field hide.
22333              * @param {Roo.bootstrap.DateField} this
22334              * @param {Mixed} date The date value
22335              */
22336             hide : true,
22337             /**
22338              * @event select
22339              * Fires when select a date.
22340              * @param {Roo.bootstrap.DateField} this
22341              * @param {Mixed} date The date value
22342              */
22343             select : true,
22344             /**
22345              * @event beforeselect
22346              * Fires when before select a date.
22347              * @param {Roo.bootstrap.DateField} this
22348              * @param {Mixed} date The date value
22349              */
22350             beforeselect : true
22351         });
22352 };
22353
22354 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
22355     
22356     /**
22357      * @cfg {String} format
22358      * The default date format string which can be overriden for localization support.  The format must be
22359      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
22360      */
22361     format : "m/d/y",
22362     /**
22363      * @cfg {String} altFormats
22364      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
22365      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
22366      */
22367     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
22368     
22369     weekStart : 0,
22370     
22371     viewMode : '',
22372     
22373     minViewMode : '',
22374     
22375     todayHighlight : false,
22376     
22377     todayBtn: false,
22378     
22379     language: 'en',
22380     
22381     keyboardNavigation: true,
22382     
22383     calendarWeeks: false,
22384     
22385     startDate: -Infinity,
22386     
22387     endDate: Infinity,
22388     
22389     daysOfWeekDisabled: [],
22390     
22391     _events: [],
22392     
22393     singleMode : false,
22394     
22395     UTCDate: function()
22396     {
22397         return new Date(Date.UTC.apply(Date, arguments));
22398     },
22399     
22400     UTCToday: function()
22401     {
22402         var today = new Date();
22403         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
22404     },
22405     
22406     getDate: function() {
22407             var d = this.getUTCDate();
22408             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
22409     },
22410     
22411     getUTCDate: function() {
22412             return this.date;
22413     },
22414     
22415     setDate: function(d) {
22416             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
22417     },
22418     
22419     setUTCDate: function(d) {
22420             this.date = d;
22421             this.setValue(this.formatDate(this.date));
22422     },
22423         
22424     onRender: function(ct, position)
22425     {
22426         
22427         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
22428         
22429         this.language = this.language || 'en';
22430         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
22431         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
22432         
22433         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
22434         this.format = this.format || 'm/d/y';
22435         this.isInline = false;
22436         this.isInput = true;
22437         this.component = this.el.select('.add-on', true).first() || false;
22438         this.component = (this.component && this.component.length === 0) ? false : this.component;
22439         this.hasInput = this.component && this.inputEl().length;
22440         
22441         if (typeof(this.minViewMode === 'string')) {
22442             switch (this.minViewMode) {
22443                 case 'months':
22444                     this.minViewMode = 1;
22445                     break;
22446                 case 'years':
22447                     this.minViewMode = 2;
22448                     break;
22449                 default:
22450                     this.minViewMode = 0;
22451                     break;
22452             }
22453         }
22454         
22455         if (typeof(this.viewMode === 'string')) {
22456             switch (this.viewMode) {
22457                 case 'months':
22458                     this.viewMode = 1;
22459                     break;
22460                 case 'years':
22461                     this.viewMode = 2;
22462                     break;
22463                 default:
22464                     this.viewMode = 0;
22465                     break;
22466             }
22467         }
22468                 
22469         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22470         
22471 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22472         
22473         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22474         
22475         this.picker().on('mousedown', this.onMousedown, this);
22476         this.picker().on('click', this.onClick, this);
22477         
22478         this.picker().addClass('datepicker-dropdown');
22479         
22480         this.startViewMode = this.viewMode;
22481         
22482         if(this.singleMode){
22483             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22484                 v.setVisibilityMode(Roo.Element.DISPLAY);
22485                 v.hide();
22486             });
22487             
22488             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22489                 v.setStyle('width', '189px');
22490             });
22491         }
22492         
22493         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22494             if(!this.calendarWeeks){
22495                 v.remove();
22496                 return;
22497             }
22498             
22499             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22500             v.attr('colspan', function(i, val){
22501                 return parseInt(val) + 1;
22502             });
22503         });
22504                         
22505         
22506         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22507         
22508         this.setStartDate(this.startDate);
22509         this.setEndDate(this.endDate);
22510         
22511         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22512         
22513         this.fillDow();
22514         this.fillMonths();
22515         this.update();
22516         this.showMode();
22517         
22518         if(this.isInline) {
22519             this.showPopup();
22520         }
22521     },
22522     
22523     picker : function()
22524     {
22525         return this.pickerEl;
22526 //        return this.el.select('.datepicker', true).first();
22527     },
22528     
22529     fillDow: function()
22530     {
22531         var dowCnt = this.weekStart;
22532         
22533         var dow = {
22534             tag: 'tr',
22535             cn: [
22536                 
22537             ]
22538         };
22539         
22540         if(this.calendarWeeks){
22541             dow.cn.push({
22542                 tag: 'th',
22543                 cls: 'cw',
22544                 html: '&nbsp;'
22545             })
22546         }
22547         
22548         while (dowCnt < this.weekStart + 7) {
22549             dow.cn.push({
22550                 tag: 'th',
22551                 cls: 'dow',
22552                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22553             });
22554         }
22555         
22556         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22557     },
22558     
22559     fillMonths: function()
22560     {    
22561         var i = 0;
22562         var months = this.picker().select('>.datepicker-months td', true).first();
22563         
22564         months.dom.innerHTML = '';
22565         
22566         while (i < 12) {
22567             var month = {
22568                 tag: 'span',
22569                 cls: 'month',
22570                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22571             };
22572             
22573             months.createChild(month);
22574         }
22575         
22576     },
22577     
22578     update: function()
22579     {
22580         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;
22581         
22582         if (this.date < this.startDate) {
22583             this.viewDate = new Date(this.startDate);
22584         } else if (this.date > this.endDate) {
22585             this.viewDate = new Date(this.endDate);
22586         } else {
22587             this.viewDate = new Date(this.date);
22588         }
22589         
22590         this.fill();
22591     },
22592     
22593     fill: function() 
22594     {
22595         var d = new Date(this.viewDate),
22596                 year = d.getUTCFullYear(),
22597                 month = d.getUTCMonth(),
22598                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22599                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22600                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22601                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22602                 currentDate = this.date && this.date.valueOf(),
22603                 today = this.UTCToday();
22604         
22605         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22606         
22607 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22608         
22609 //        this.picker.select('>tfoot th.today').
22610 //                                              .text(dates[this.language].today)
22611 //                                              .toggle(this.todayBtn !== false);
22612     
22613         this.updateNavArrows();
22614         this.fillMonths();
22615                                                 
22616         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22617         
22618         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22619          
22620         prevMonth.setUTCDate(day);
22621         
22622         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22623         
22624         var nextMonth = new Date(prevMonth);
22625         
22626         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22627         
22628         nextMonth = nextMonth.valueOf();
22629         
22630         var fillMonths = false;
22631         
22632         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22633         
22634         while(prevMonth.valueOf() <= nextMonth) {
22635             var clsName = '';
22636             
22637             if (prevMonth.getUTCDay() === this.weekStart) {
22638                 if(fillMonths){
22639                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22640                 }
22641                     
22642                 fillMonths = {
22643                     tag: 'tr',
22644                     cn: []
22645                 };
22646                 
22647                 if(this.calendarWeeks){
22648                     // ISO 8601: First week contains first thursday.
22649                     // ISO also states week starts on Monday, but we can be more abstract here.
22650                     var
22651                     // Start of current week: based on weekstart/current date
22652                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22653                     // Thursday of this week
22654                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22655                     // First Thursday of year, year from thursday
22656                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22657                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22658                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22659                     
22660                     fillMonths.cn.push({
22661                         tag: 'td',
22662                         cls: 'cw',
22663                         html: calWeek
22664                     });
22665                 }
22666             }
22667             
22668             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22669                 clsName += ' old';
22670             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22671                 clsName += ' new';
22672             }
22673             if (this.todayHighlight &&
22674                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22675                 prevMonth.getUTCMonth() == today.getMonth() &&
22676                 prevMonth.getUTCDate() == today.getDate()) {
22677                 clsName += ' today';
22678             }
22679             
22680             if (currentDate && prevMonth.valueOf() === currentDate) {
22681                 clsName += ' active';
22682             }
22683             
22684             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22685                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22686                     clsName += ' disabled';
22687             }
22688             
22689             fillMonths.cn.push({
22690                 tag: 'td',
22691                 cls: 'day ' + clsName,
22692                 html: prevMonth.getDate()
22693             });
22694             
22695             prevMonth.setDate(prevMonth.getDate()+1);
22696         }
22697           
22698         var currentYear = this.date && this.date.getUTCFullYear();
22699         var currentMonth = this.date && this.date.getUTCMonth();
22700         
22701         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22702         
22703         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22704             v.removeClass('active');
22705             
22706             if(currentYear === year && k === currentMonth){
22707                 v.addClass('active');
22708             }
22709             
22710             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22711                 v.addClass('disabled');
22712             }
22713             
22714         });
22715         
22716         
22717         year = parseInt(year/10, 10) * 10;
22718         
22719         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22720         
22721         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22722         
22723         year -= 1;
22724         for (var i = -1; i < 11; i++) {
22725             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22726                 tag: 'span',
22727                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22728                 html: year
22729             });
22730             
22731             year += 1;
22732         }
22733     },
22734     
22735     showMode: function(dir) 
22736     {
22737         if (dir) {
22738             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22739         }
22740         
22741         Roo.each(this.picker().select('>div',true).elements, function(v){
22742             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22743             v.hide();
22744         });
22745         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22746     },
22747     
22748     place: function()
22749     {
22750         if(this.isInline) {
22751             return;
22752         }
22753         
22754         this.picker().removeClass(['bottom', 'top']);
22755         
22756         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22757             /*
22758              * place to the top of element!
22759              *
22760              */
22761             
22762             this.picker().addClass('top');
22763             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22764             
22765             return;
22766         }
22767         
22768         this.picker().addClass('bottom');
22769         
22770         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22771     },
22772     
22773     parseDate : function(value)
22774     {
22775         if(!value || value instanceof Date){
22776             return value;
22777         }
22778         var v = Date.parseDate(value, this.format);
22779         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22780             v = Date.parseDate(value, 'Y-m-d');
22781         }
22782         if(!v && this.altFormats){
22783             if(!this.altFormatsArray){
22784                 this.altFormatsArray = this.altFormats.split("|");
22785             }
22786             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22787                 v = Date.parseDate(value, this.altFormatsArray[i]);
22788             }
22789         }
22790         return v;
22791     },
22792     
22793     formatDate : function(date, fmt)
22794     {   
22795         return (!date || !(date instanceof Date)) ?
22796         date : date.dateFormat(fmt || this.format);
22797     },
22798     
22799     onFocus : function()
22800     {
22801         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22802         this.showPopup();
22803     },
22804     
22805     onBlur : function()
22806     {
22807         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22808         
22809         var d = this.inputEl().getValue();
22810         
22811         this.setValue(d);
22812                 
22813         this.hidePopup();
22814     },
22815     
22816     showPopup : function()
22817     {
22818         this.picker().show();
22819         this.update();
22820         this.place();
22821         
22822         this.fireEvent('showpopup', this, this.date);
22823     },
22824     
22825     hidePopup : function()
22826     {
22827         if(this.isInline) {
22828             return;
22829         }
22830         this.picker().hide();
22831         this.viewMode = this.startViewMode;
22832         this.showMode();
22833         
22834         this.fireEvent('hidepopup', this, this.date);
22835         
22836     },
22837     
22838     onMousedown: function(e)
22839     {
22840         e.stopPropagation();
22841         e.preventDefault();
22842     },
22843     
22844     keyup: function(e)
22845     {
22846         Roo.bootstrap.DateField.superclass.keyup.call(this);
22847         this.update();
22848     },
22849
22850     setValue: function(v)
22851     {
22852         if(this.fireEvent('beforeselect', this, v) !== false){
22853             var d = new Date(this.parseDate(v) ).clearTime();
22854         
22855             if(isNaN(d.getTime())){
22856                 this.date = this.viewDate = '';
22857                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22858                 return;
22859             }
22860
22861             v = this.formatDate(d);
22862
22863             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22864
22865             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22866
22867             this.update();
22868
22869             this.fireEvent('select', this, this.date);
22870         }
22871     },
22872     
22873     getValue: function()
22874     {
22875         return this.formatDate(this.date);
22876     },
22877     
22878     fireKey: function(e)
22879     {
22880         if (!this.picker().isVisible()){
22881             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22882                 this.showPopup();
22883             }
22884             return;
22885         }
22886         
22887         var dateChanged = false,
22888         dir, day, month,
22889         newDate, newViewDate;
22890         
22891         switch(e.keyCode){
22892             case 27: // escape
22893                 this.hidePopup();
22894                 e.preventDefault();
22895                 break;
22896             case 37: // left
22897             case 39: // right
22898                 if (!this.keyboardNavigation) {
22899                     break;
22900                 }
22901                 dir = e.keyCode == 37 ? -1 : 1;
22902                 
22903                 if (e.ctrlKey){
22904                     newDate = this.moveYear(this.date, dir);
22905                     newViewDate = this.moveYear(this.viewDate, dir);
22906                 } else if (e.shiftKey){
22907                     newDate = this.moveMonth(this.date, dir);
22908                     newViewDate = this.moveMonth(this.viewDate, dir);
22909                 } else {
22910                     newDate = new Date(this.date);
22911                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22912                     newViewDate = new Date(this.viewDate);
22913                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22914                 }
22915                 if (this.dateWithinRange(newDate)){
22916                     this.date = newDate;
22917                     this.viewDate = newViewDate;
22918                     this.setValue(this.formatDate(this.date));
22919 //                    this.update();
22920                     e.preventDefault();
22921                     dateChanged = true;
22922                 }
22923                 break;
22924             case 38: // up
22925             case 40: // down
22926                 if (!this.keyboardNavigation) {
22927                     break;
22928                 }
22929                 dir = e.keyCode == 38 ? -1 : 1;
22930                 if (e.ctrlKey){
22931                     newDate = this.moveYear(this.date, dir);
22932                     newViewDate = this.moveYear(this.viewDate, dir);
22933                 } else if (e.shiftKey){
22934                     newDate = this.moveMonth(this.date, dir);
22935                     newViewDate = this.moveMonth(this.viewDate, dir);
22936                 } else {
22937                     newDate = new Date(this.date);
22938                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22939                     newViewDate = new Date(this.viewDate);
22940                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22941                 }
22942                 if (this.dateWithinRange(newDate)){
22943                     this.date = newDate;
22944                     this.viewDate = newViewDate;
22945                     this.setValue(this.formatDate(this.date));
22946 //                    this.update();
22947                     e.preventDefault();
22948                     dateChanged = true;
22949                 }
22950                 break;
22951             case 13: // enter
22952                 this.setValue(this.formatDate(this.date));
22953                 this.hidePopup();
22954                 e.preventDefault();
22955                 break;
22956             case 9: // tab
22957                 this.setValue(this.formatDate(this.date));
22958                 this.hidePopup();
22959                 break;
22960             case 16: // shift
22961             case 17: // ctrl
22962             case 18: // alt
22963                 break;
22964             default :
22965                 this.hidePopup();
22966                 
22967         }
22968     },
22969     
22970     
22971     onClick: function(e) 
22972     {
22973         e.stopPropagation();
22974         e.preventDefault();
22975         
22976         var target = e.getTarget();
22977         
22978         if(target.nodeName.toLowerCase() === 'i'){
22979             target = Roo.get(target).dom.parentNode;
22980         }
22981         
22982         var nodeName = target.nodeName;
22983         var className = target.className;
22984         var html = target.innerHTML;
22985         //Roo.log(nodeName);
22986         
22987         switch(nodeName.toLowerCase()) {
22988             case 'th':
22989                 switch(className) {
22990                     case 'switch':
22991                         this.showMode(1);
22992                         break;
22993                     case 'prev':
22994                     case 'next':
22995                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22996                         switch(this.viewMode){
22997                                 case 0:
22998                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22999                                         break;
23000                                 case 1:
23001                                 case 2:
23002                                         this.viewDate = this.moveYear(this.viewDate, dir);
23003                                         break;
23004                         }
23005                         this.fill();
23006                         break;
23007                     case 'today':
23008                         var date = new Date();
23009                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
23010 //                        this.fill()
23011                         this.setValue(this.formatDate(this.date));
23012                         
23013                         this.hidePopup();
23014                         break;
23015                 }
23016                 break;
23017             case 'span':
23018                 if (className.indexOf('disabled') < 0) {
23019                 if (!this.viewDate) {
23020                     this.viewDate = new Date();
23021                 }
23022                 this.viewDate.setUTCDate(1);
23023                     if (className.indexOf('month') > -1) {
23024                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
23025                     } else {
23026                         var year = parseInt(html, 10) || 0;
23027                         this.viewDate.setUTCFullYear(year);
23028                         
23029                     }
23030                     
23031                     if(this.singleMode){
23032                         this.setValue(this.formatDate(this.viewDate));
23033                         this.hidePopup();
23034                         return;
23035                     }
23036                     
23037                     this.showMode(-1);
23038                     this.fill();
23039                 }
23040                 break;
23041                 
23042             case 'td':
23043                 //Roo.log(className);
23044                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
23045                     var day = parseInt(html, 10) || 1;
23046                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
23047                         month = (this.viewDate || new Date()).getUTCMonth();
23048
23049                     if (className.indexOf('old') > -1) {
23050                         if(month === 0 ){
23051                             month = 11;
23052                             year -= 1;
23053                         }else{
23054                             month -= 1;
23055                         }
23056                     } else if (className.indexOf('new') > -1) {
23057                         if (month == 11) {
23058                             month = 0;
23059                             year += 1;
23060                         } else {
23061                             month += 1;
23062                         }
23063                     }
23064                     //Roo.log([year,month,day]);
23065                     this.date = this.UTCDate(year, month, day,0,0,0,0);
23066                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
23067 //                    this.fill();
23068                     //Roo.log(this.formatDate(this.date));
23069                     this.setValue(this.formatDate(this.date));
23070                     this.hidePopup();
23071                 }
23072                 break;
23073         }
23074     },
23075     
23076     setStartDate: function(startDate)
23077     {
23078         this.startDate = startDate || -Infinity;
23079         if (this.startDate !== -Infinity) {
23080             this.startDate = this.parseDate(this.startDate);
23081         }
23082         this.update();
23083         this.updateNavArrows();
23084     },
23085
23086     setEndDate: function(endDate)
23087     {
23088         this.endDate = endDate || Infinity;
23089         if (this.endDate !== Infinity) {
23090             this.endDate = this.parseDate(this.endDate);
23091         }
23092         this.update();
23093         this.updateNavArrows();
23094     },
23095     
23096     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
23097     {
23098         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
23099         if (typeof(this.daysOfWeekDisabled) !== 'object') {
23100             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
23101         }
23102         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
23103             return parseInt(d, 10);
23104         });
23105         this.update();
23106         this.updateNavArrows();
23107     },
23108     
23109     updateNavArrows: function() 
23110     {
23111         if(this.singleMode){
23112             return;
23113         }
23114         
23115         var d = new Date(this.viewDate),
23116         year = d.getUTCFullYear(),
23117         month = d.getUTCMonth();
23118         
23119         Roo.each(this.picker().select('.prev', true).elements, function(v){
23120             v.show();
23121             switch (this.viewMode) {
23122                 case 0:
23123
23124                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
23125                         v.hide();
23126                     }
23127                     break;
23128                 case 1:
23129                 case 2:
23130                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
23131                         v.hide();
23132                     }
23133                     break;
23134             }
23135         });
23136         
23137         Roo.each(this.picker().select('.next', true).elements, function(v){
23138             v.show();
23139             switch (this.viewMode) {
23140                 case 0:
23141
23142                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
23143                         v.hide();
23144                     }
23145                     break;
23146                 case 1:
23147                 case 2:
23148                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
23149                         v.hide();
23150                     }
23151                     break;
23152             }
23153         })
23154     },
23155     
23156     moveMonth: function(date, dir)
23157     {
23158         if (!dir) {
23159             return date;
23160         }
23161         var new_date = new Date(date.valueOf()),
23162         day = new_date.getUTCDate(),
23163         month = new_date.getUTCMonth(),
23164         mag = Math.abs(dir),
23165         new_month, test;
23166         dir = dir > 0 ? 1 : -1;
23167         if (mag == 1){
23168             test = dir == -1
23169             // If going back one month, make sure month is not current month
23170             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
23171             ? function(){
23172                 return new_date.getUTCMonth() == month;
23173             }
23174             // If going forward one month, make sure month is as expected
23175             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
23176             : function(){
23177                 return new_date.getUTCMonth() != new_month;
23178             };
23179             new_month = month + dir;
23180             new_date.setUTCMonth(new_month);
23181             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
23182             if (new_month < 0 || new_month > 11) {
23183                 new_month = (new_month + 12) % 12;
23184             }
23185         } else {
23186             // For magnitudes >1, move one month at a time...
23187             for (var i=0; i<mag; i++) {
23188                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
23189                 new_date = this.moveMonth(new_date, dir);
23190             }
23191             // ...then reset the day, keeping it in the new month
23192             new_month = new_date.getUTCMonth();
23193             new_date.setUTCDate(day);
23194             test = function(){
23195                 return new_month != new_date.getUTCMonth();
23196             };
23197         }
23198         // Common date-resetting loop -- if date is beyond end of month, make it
23199         // end of month
23200         while (test()){
23201             new_date.setUTCDate(--day);
23202             new_date.setUTCMonth(new_month);
23203         }
23204         return new_date;
23205     },
23206
23207     moveYear: function(date, dir)
23208     {
23209         return this.moveMonth(date, dir*12);
23210     },
23211
23212     dateWithinRange: function(date)
23213     {
23214         return date >= this.startDate && date <= this.endDate;
23215     },
23216
23217     
23218     remove: function() 
23219     {
23220         this.picker().remove();
23221     },
23222     
23223     validateValue : function(value)
23224     {
23225         if(this.getVisibilityEl().hasClass('hidden')){
23226             return true;
23227         }
23228         
23229         if(value.length < 1)  {
23230             if(this.allowBlank){
23231                 return true;
23232             }
23233             return false;
23234         }
23235         
23236         if(value.length < this.minLength){
23237             return false;
23238         }
23239         if(value.length > this.maxLength){
23240             return false;
23241         }
23242         if(this.vtype){
23243             var vt = Roo.form.VTypes;
23244             if(!vt[this.vtype](value, this)){
23245                 return false;
23246             }
23247         }
23248         if(typeof this.validator == "function"){
23249             var msg = this.validator(value);
23250             if(msg !== true){
23251                 return false;
23252             }
23253         }
23254         
23255         if(this.regex && !this.regex.test(value)){
23256             return false;
23257         }
23258         
23259         if(typeof(this.parseDate(value)) == 'undefined'){
23260             return false;
23261         }
23262         
23263         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
23264             return false;
23265         }      
23266         
23267         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
23268             return false;
23269         } 
23270         
23271         
23272         return true;
23273     },
23274     
23275     reset : function()
23276     {
23277         this.date = this.viewDate = '';
23278         
23279         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
23280     }
23281    
23282 });
23283
23284 Roo.apply(Roo.bootstrap.DateField,  {
23285     
23286     head : {
23287         tag: 'thead',
23288         cn: [
23289         {
23290             tag: 'tr',
23291             cn: [
23292             {
23293                 tag: 'th',
23294                 cls: 'prev',
23295                 html: '<i class="fa fa-arrow-left"/>'
23296             },
23297             {
23298                 tag: 'th',
23299                 cls: 'switch',
23300                 colspan: '5'
23301             },
23302             {
23303                 tag: 'th',
23304                 cls: 'next',
23305                 html: '<i class="fa fa-arrow-right"/>'
23306             }
23307
23308             ]
23309         }
23310         ]
23311     },
23312     
23313     content : {
23314         tag: 'tbody',
23315         cn: [
23316         {
23317             tag: 'tr',
23318             cn: [
23319             {
23320                 tag: 'td',
23321                 colspan: '7'
23322             }
23323             ]
23324         }
23325         ]
23326     },
23327     
23328     footer : {
23329         tag: 'tfoot',
23330         cn: [
23331         {
23332             tag: 'tr',
23333             cn: [
23334             {
23335                 tag: 'th',
23336                 colspan: '7',
23337                 cls: 'today'
23338             }
23339                     
23340             ]
23341         }
23342         ]
23343     },
23344     
23345     dates:{
23346         en: {
23347             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
23348             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
23349             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
23350             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23351             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
23352             today: "Today"
23353         }
23354     },
23355     
23356     modes: [
23357     {
23358         clsName: 'days',
23359         navFnc: 'Month',
23360         navStep: 1
23361     },
23362     {
23363         clsName: 'months',
23364         navFnc: 'FullYear',
23365         navStep: 1
23366     },
23367     {
23368         clsName: 'years',
23369         navFnc: 'FullYear',
23370         navStep: 10
23371     }]
23372 });
23373
23374 Roo.apply(Roo.bootstrap.DateField,  {
23375   
23376     template : {
23377         tag: 'div',
23378         cls: 'datepicker dropdown-menu roo-dynamic shadow',
23379         cn: [
23380         {
23381             tag: 'div',
23382             cls: 'datepicker-days',
23383             cn: [
23384             {
23385                 tag: 'table',
23386                 cls: 'table-condensed',
23387                 cn:[
23388                 Roo.bootstrap.DateField.head,
23389                 {
23390                     tag: 'tbody'
23391                 },
23392                 Roo.bootstrap.DateField.footer
23393                 ]
23394             }
23395             ]
23396         },
23397         {
23398             tag: 'div',
23399             cls: 'datepicker-months',
23400             cn: [
23401             {
23402                 tag: 'table',
23403                 cls: 'table-condensed',
23404                 cn:[
23405                 Roo.bootstrap.DateField.head,
23406                 Roo.bootstrap.DateField.content,
23407                 Roo.bootstrap.DateField.footer
23408                 ]
23409             }
23410             ]
23411         },
23412         {
23413             tag: 'div',
23414             cls: 'datepicker-years',
23415             cn: [
23416             {
23417                 tag: 'table',
23418                 cls: 'table-condensed',
23419                 cn:[
23420                 Roo.bootstrap.DateField.head,
23421                 Roo.bootstrap.DateField.content,
23422                 Roo.bootstrap.DateField.footer
23423                 ]
23424             }
23425             ]
23426         }
23427         ]
23428     }
23429 });
23430
23431  
23432
23433  /*
23434  * - LGPL
23435  *
23436  * TimeField
23437  * 
23438  */
23439
23440 /**
23441  * @class Roo.bootstrap.TimeField
23442  * @extends Roo.bootstrap.Input
23443  * Bootstrap DateField class
23444  * 
23445  * 
23446  * @constructor
23447  * Create a new TimeField
23448  * @param {Object} config The config object
23449  */
23450
23451 Roo.bootstrap.TimeField = function(config){
23452     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
23453     this.addEvents({
23454             /**
23455              * @event show
23456              * Fires when this field show.
23457              * @param {Roo.bootstrap.DateField} thisthis
23458              * @param {Mixed} date The date value
23459              */
23460             show : true,
23461             /**
23462              * @event show
23463              * Fires when this field hide.
23464              * @param {Roo.bootstrap.DateField} this
23465              * @param {Mixed} date The date value
23466              */
23467             hide : true,
23468             /**
23469              * @event select
23470              * Fires when select a date.
23471              * @param {Roo.bootstrap.DateField} this
23472              * @param {Mixed} date The date value
23473              */
23474             select : true
23475         });
23476 };
23477
23478 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23479     
23480     /**
23481      * @cfg {String} format
23482      * The default time format string which can be overriden for localization support.  The format must be
23483      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23484      */
23485     format : "H:i",
23486
23487     getAutoCreate : function()
23488     {
23489         this.after = '<i class="fa far fa-clock"></i>';
23490         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23491         
23492          
23493     },
23494     onRender: function(ct, position)
23495     {
23496         
23497         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23498                 
23499         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23500         
23501         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23502         
23503         this.pop = this.picker().select('>.datepicker-time',true).first();
23504         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23505         
23506         this.picker().on('mousedown', this.onMousedown, this);
23507         this.picker().on('click', this.onClick, this);
23508         
23509         this.picker().addClass('datepicker-dropdown');
23510     
23511         this.fillTime();
23512         this.update();
23513             
23514         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23515         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23516         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23517         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23518         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23519         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23520
23521     },
23522     
23523     fireKey: function(e){
23524         if (!this.picker().isVisible()){
23525             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23526                 this.show();
23527             }
23528             return;
23529         }
23530
23531         e.preventDefault();
23532         
23533         switch(e.keyCode){
23534             case 27: // escape
23535                 this.hide();
23536                 break;
23537             case 37: // left
23538             case 39: // right
23539                 this.onTogglePeriod();
23540                 break;
23541             case 38: // up
23542                 this.onIncrementMinutes();
23543                 break;
23544             case 40: // down
23545                 this.onDecrementMinutes();
23546                 break;
23547             case 13: // enter
23548             case 9: // tab
23549                 this.setTime();
23550                 break;
23551         }
23552     },
23553     
23554     onClick: function(e) {
23555         e.stopPropagation();
23556         e.preventDefault();
23557     },
23558     
23559     picker : function()
23560     {
23561         return this.pickerEl;
23562     },
23563     
23564     fillTime: function()
23565     {    
23566         var time = this.pop.select('tbody', true).first();
23567         
23568         time.dom.innerHTML = '';
23569         
23570         time.createChild({
23571             tag: 'tr',
23572             cn: [
23573                 {
23574                     tag: 'td',
23575                     cn: [
23576                         {
23577                             tag: 'a',
23578                             href: '#',
23579                             cls: 'btn',
23580                             cn: [
23581                                 {
23582                                     tag: 'i',
23583                                     cls: 'hours-up fa fas fa-chevron-up'
23584                                 }
23585                             ]
23586                         } 
23587                     ]
23588                 },
23589                 {
23590                     tag: 'td',
23591                     cls: 'separator'
23592                 },
23593                 {
23594                     tag: 'td',
23595                     cn: [
23596                         {
23597                             tag: 'a',
23598                             href: '#',
23599                             cls: 'btn',
23600                             cn: [
23601                                 {
23602                                     tag: 'i',
23603                                     cls: 'minutes-up fa fas fa-chevron-up'
23604                                 }
23605                             ]
23606                         }
23607                     ]
23608                 },
23609                 {
23610                     tag: 'td',
23611                     cls: 'separator'
23612                 }
23613             ]
23614         });
23615         
23616         time.createChild({
23617             tag: 'tr',
23618             cn: [
23619                 {
23620                     tag: 'td',
23621                     cn: [
23622                         {
23623                             tag: 'span',
23624                             cls: 'timepicker-hour',
23625                             html: '00'
23626                         }  
23627                     ]
23628                 },
23629                 {
23630                     tag: 'td',
23631                     cls: 'separator',
23632                     html: ':'
23633                 },
23634                 {
23635                     tag: 'td',
23636                     cn: [
23637                         {
23638                             tag: 'span',
23639                             cls: 'timepicker-minute',
23640                             html: '00'
23641                         }  
23642                     ]
23643                 },
23644                 {
23645                     tag: 'td',
23646                     cls: 'separator'
23647                 },
23648                 {
23649                     tag: 'td',
23650                     cn: [
23651                         {
23652                             tag: 'button',
23653                             type: 'button',
23654                             cls: 'btn btn-primary period',
23655                             html: 'AM'
23656                             
23657                         }
23658                     ]
23659                 }
23660             ]
23661         });
23662         
23663         time.createChild({
23664             tag: 'tr',
23665             cn: [
23666                 {
23667                     tag: 'td',
23668                     cn: [
23669                         {
23670                             tag: 'a',
23671                             href: '#',
23672                             cls: 'btn',
23673                             cn: [
23674                                 {
23675                                     tag: 'span',
23676                                     cls: 'hours-down fa fas fa-chevron-down'
23677                                 }
23678                             ]
23679                         }
23680                     ]
23681                 },
23682                 {
23683                     tag: 'td',
23684                     cls: 'separator'
23685                 },
23686                 {
23687                     tag: 'td',
23688                     cn: [
23689                         {
23690                             tag: 'a',
23691                             href: '#',
23692                             cls: 'btn',
23693                             cn: [
23694                                 {
23695                                     tag: 'span',
23696                                     cls: 'minutes-down fa fas fa-chevron-down'
23697                                 }
23698                             ]
23699                         }
23700                     ]
23701                 },
23702                 {
23703                     tag: 'td',
23704                     cls: 'separator'
23705                 }
23706             ]
23707         });
23708         
23709     },
23710     
23711     update: function()
23712     {
23713         
23714         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23715         
23716         this.fill();
23717     },
23718     
23719     fill: function() 
23720     {
23721         var hours = this.time.getHours();
23722         var minutes = this.time.getMinutes();
23723         var period = 'AM';
23724         
23725         if(hours > 11){
23726             period = 'PM';
23727         }
23728         
23729         if(hours == 0){
23730             hours = 12;
23731         }
23732         
23733         
23734         if(hours > 12){
23735             hours = hours - 12;
23736         }
23737         
23738         if(hours < 10){
23739             hours = '0' + hours;
23740         }
23741         
23742         if(minutes < 10){
23743             minutes = '0' + minutes;
23744         }
23745         
23746         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23747         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23748         this.pop.select('button', true).first().dom.innerHTML = period;
23749         
23750     },
23751     
23752     place: function()
23753     {   
23754         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23755         
23756         var cls = ['bottom'];
23757         
23758         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23759             cls.pop();
23760             cls.push('top');
23761         }
23762         
23763         cls.push('right');
23764         
23765         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23766             cls.pop();
23767             cls.push('left');
23768         }
23769         //this.picker().setXY(20000,20000);
23770         this.picker().addClass(cls.join('-'));
23771         
23772         var _this = this;
23773         
23774         Roo.each(cls, function(c){
23775             if(c == 'bottom'){
23776                 (function() {
23777                  //  
23778                 }).defer(200);
23779                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23780                 //_this.picker().setTop(_this.inputEl().getHeight());
23781                 return;
23782             }
23783             if(c == 'top'){
23784                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23785                 
23786                 //_this.picker().setTop(0 - _this.picker().getHeight());
23787                 return;
23788             }
23789             /*
23790             if(c == 'left'){
23791                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23792                 return;
23793             }
23794             if(c == 'right'){
23795                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23796                 return;
23797             }
23798             */
23799         });
23800         
23801     },
23802   
23803     onFocus : function()
23804     {
23805         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23806         this.show();
23807     },
23808     
23809     onBlur : function()
23810     {
23811         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23812         this.hide();
23813     },
23814     
23815     show : function()
23816     {
23817         this.picker().show();
23818         this.pop.show();
23819         this.update();
23820         this.place();
23821         
23822         this.fireEvent('show', this, this.date);
23823     },
23824     
23825     hide : function()
23826     {
23827         this.picker().hide();
23828         this.pop.hide();
23829         
23830         this.fireEvent('hide', this, this.date);
23831     },
23832     
23833     setTime : function()
23834     {
23835         this.hide();
23836         this.setValue(this.time.format(this.format));
23837         
23838         this.fireEvent('select', this, this.date);
23839         
23840         
23841     },
23842     
23843     onMousedown: function(e){
23844         e.stopPropagation();
23845         e.preventDefault();
23846     },
23847     
23848     onIncrementHours: function()
23849     {
23850         Roo.log('onIncrementHours');
23851         this.time = this.time.add(Date.HOUR, 1);
23852         this.update();
23853         
23854     },
23855     
23856     onDecrementHours: function()
23857     {
23858         Roo.log('onDecrementHours');
23859         this.time = this.time.add(Date.HOUR, -1);
23860         this.update();
23861     },
23862     
23863     onIncrementMinutes: function()
23864     {
23865         Roo.log('onIncrementMinutes');
23866         this.time = this.time.add(Date.MINUTE, 1);
23867         this.update();
23868     },
23869     
23870     onDecrementMinutes: function()
23871     {
23872         Roo.log('onDecrementMinutes');
23873         this.time = this.time.add(Date.MINUTE, -1);
23874         this.update();
23875     },
23876     
23877     onTogglePeriod: function()
23878     {
23879         Roo.log('onTogglePeriod');
23880         this.time = this.time.add(Date.HOUR, 12);
23881         this.update();
23882     }
23883     
23884    
23885 });
23886  
23887
23888 Roo.apply(Roo.bootstrap.TimeField,  {
23889   
23890     template : {
23891         tag: 'div',
23892         cls: 'datepicker dropdown-menu',
23893         cn: [
23894             {
23895                 tag: 'div',
23896                 cls: 'datepicker-time',
23897                 cn: [
23898                 {
23899                     tag: 'table',
23900                     cls: 'table-condensed',
23901                     cn:[
23902                         {
23903                             tag: 'tbody',
23904                             cn: [
23905                                 {
23906                                     tag: 'tr',
23907                                     cn: [
23908                                     {
23909                                         tag: 'td',
23910                                         colspan: '7'
23911                                     }
23912                                     ]
23913                                 }
23914                             ]
23915                         },
23916                         {
23917                             tag: 'tfoot',
23918                             cn: [
23919                                 {
23920                                     tag: 'tr',
23921                                     cn: [
23922                                     {
23923                                         tag: 'th',
23924                                         colspan: '7',
23925                                         cls: '',
23926                                         cn: [
23927                                             {
23928                                                 tag: 'button',
23929                                                 cls: 'btn btn-info ok',
23930                                                 html: 'OK'
23931                                             }
23932                                         ]
23933                                     }
23934                     
23935                                     ]
23936                                 }
23937                             ]
23938                         }
23939                     ]
23940                 }
23941                 ]
23942             }
23943         ]
23944     }
23945 });
23946
23947  
23948
23949  /*
23950  * - LGPL
23951  *
23952  * MonthField
23953  * 
23954  */
23955
23956 /**
23957  * @class Roo.bootstrap.MonthField
23958  * @extends Roo.bootstrap.Input
23959  * Bootstrap MonthField class
23960  * 
23961  * @cfg {String} language default en
23962  * 
23963  * @constructor
23964  * Create a new MonthField
23965  * @param {Object} config The config object
23966  */
23967
23968 Roo.bootstrap.MonthField = function(config){
23969     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23970     
23971     this.addEvents({
23972         /**
23973          * @event show
23974          * Fires when this field show.
23975          * @param {Roo.bootstrap.MonthField} this
23976          * @param {Mixed} date The date value
23977          */
23978         show : true,
23979         /**
23980          * @event show
23981          * Fires when this field hide.
23982          * @param {Roo.bootstrap.MonthField} this
23983          * @param {Mixed} date The date value
23984          */
23985         hide : true,
23986         /**
23987          * @event select
23988          * Fires when select a date.
23989          * @param {Roo.bootstrap.MonthField} this
23990          * @param {String} oldvalue The old value
23991          * @param {String} newvalue The new value
23992          */
23993         select : true
23994     });
23995 };
23996
23997 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23998     
23999     onRender: function(ct, position)
24000     {
24001         
24002         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
24003         
24004         this.language = this.language || 'en';
24005         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
24006         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
24007         
24008         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
24009         this.isInline = false;
24010         this.isInput = true;
24011         this.component = this.el.select('.add-on', true).first() || false;
24012         this.component = (this.component && this.component.length === 0) ? false : this.component;
24013         this.hasInput = this.component && this.inputEL().length;
24014         
24015         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
24016         
24017         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
24018         
24019         this.picker().on('mousedown', this.onMousedown, this);
24020         this.picker().on('click', this.onClick, this);
24021         
24022         this.picker().addClass('datepicker-dropdown');
24023         
24024         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
24025             v.setStyle('width', '189px');
24026         });
24027         
24028         this.fillMonths();
24029         
24030         this.update();
24031         
24032         if(this.isInline) {
24033             this.show();
24034         }
24035         
24036     },
24037     
24038     setValue: function(v, suppressEvent)
24039     {   
24040         var o = this.getValue();
24041         
24042         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
24043         
24044         this.update();
24045
24046         if(suppressEvent !== true){
24047             this.fireEvent('select', this, o, v);
24048         }
24049         
24050     },
24051     
24052     getValue: function()
24053     {
24054         return this.value;
24055     },
24056     
24057     onClick: function(e) 
24058     {
24059         e.stopPropagation();
24060         e.preventDefault();
24061         
24062         var target = e.getTarget();
24063         
24064         if(target.nodeName.toLowerCase() === 'i'){
24065             target = Roo.get(target).dom.parentNode;
24066         }
24067         
24068         var nodeName = target.nodeName;
24069         var className = target.className;
24070         var html = target.innerHTML;
24071         
24072         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
24073             return;
24074         }
24075         
24076         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
24077         
24078         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24079         
24080         this.hide();
24081                         
24082     },
24083     
24084     picker : function()
24085     {
24086         return this.pickerEl;
24087     },
24088     
24089     fillMonths: function()
24090     {    
24091         var i = 0;
24092         var months = this.picker().select('>.datepicker-months td', true).first();
24093         
24094         months.dom.innerHTML = '';
24095         
24096         while (i < 12) {
24097             var month = {
24098                 tag: 'span',
24099                 cls: 'month',
24100                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
24101             };
24102             
24103             months.createChild(month);
24104         }
24105         
24106     },
24107     
24108     update: function()
24109     {
24110         var _this = this;
24111         
24112         if(typeof(this.vIndex) == 'undefined' && this.value.length){
24113             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
24114         }
24115         
24116         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
24117             e.removeClass('active');
24118             
24119             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
24120                 e.addClass('active');
24121             }
24122         })
24123     },
24124     
24125     place: function()
24126     {
24127         if(this.isInline) {
24128             return;
24129         }
24130         
24131         this.picker().removeClass(['bottom', 'top']);
24132         
24133         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
24134             /*
24135              * place to the top of element!
24136              *
24137              */
24138             
24139             this.picker().addClass('top');
24140             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
24141             
24142             return;
24143         }
24144         
24145         this.picker().addClass('bottom');
24146         
24147         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
24148     },
24149     
24150     onFocus : function()
24151     {
24152         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
24153         this.show();
24154     },
24155     
24156     onBlur : function()
24157     {
24158         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
24159         
24160         var d = this.inputEl().getValue();
24161         
24162         this.setValue(d);
24163                 
24164         this.hide();
24165     },
24166     
24167     show : function()
24168     {
24169         this.picker().show();
24170         this.picker().select('>.datepicker-months', true).first().show();
24171         this.update();
24172         this.place();
24173         
24174         this.fireEvent('show', this, this.date);
24175     },
24176     
24177     hide : function()
24178     {
24179         if(this.isInline) {
24180             return;
24181         }
24182         this.picker().hide();
24183         this.fireEvent('hide', this, this.date);
24184         
24185     },
24186     
24187     onMousedown: function(e)
24188     {
24189         e.stopPropagation();
24190         e.preventDefault();
24191     },
24192     
24193     keyup: function(e)
24194     {
24195         Roo.bootstrap.MonthField.superclass.keyup.call(this);
24196         this.update();
24197     },
24198
24199     fireKey: function(e)
24200     {
24201         if (!this.picker().isVisible()){
24202             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
24203                 this.show();
24204             }
24205             return;
24206         }
24207         
24208         var dir;
24209         
24210         switch(e.keyCode){
24211             case 27: // escape
24212                 this.hide();
24213                 e.preventDefault();
24214                 break;
24215             case 37: // left
24216             case 39: // right
24217                 dir = e.keyCode == 37 ? -1 : 1;
24218                 
24219                 this.vIndex = this.vIndex + dir;
24220                 
24221                 if(this.vIndex < 0){
24222                     this.vIndex = 0;
24223                 }
24224                 
24225                 if(this.vIndex > 11){
24226                     this.vIndex = 11;
24227                 }
24228                 
24229                 if(isNaN(this.vIndex)){
24230                     this.vIndex = 0;
24231                 }
24232                 
24233                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24234                 
24235                 break;
24236             case 38: // up
24237             case 40: // down
24238                 
24239                 dir = e.keyCode == 38 ? -1 : 1;
24240                 
24241                 this.vIndex = this.vIndex + dir * 4;
24242                 
24243                 if(this.vIndex < 0){
24244                     this.vIndex = 0;
24245                 }
24246                 
24247                 if(this.vIndex > 11){
24248                     this.vIndex = 11;
24249                 }
24250                 
24251                 if(isNaN(this.vIndex)){
24252                     this.vIndex = 0;
24253                 }
24254                 
24255                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24256                 break;
24257                 
24258             case 13: // enter
24259                 
24260                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24261                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24262                 }
24263                 
24264                 this.hide();
24265                 e.preventDefault();
24266                 break;
24267             case 9: // tab
24268                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
24269                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
24270                 }
24271                 this.hide();
24272                 break;
24273             case 16: // shift
24274             case 17: // ctrl
24275             case 18: // alt
24276                 break;
24277             default :
24278                 this.hide();
24279                 
24280         }
24281     },
24282     
24283     remove: function() 
24284     {
24285         this.picker().remove();
24286     }
24287    
24288 });
24289
24290 Roo.apply(Roo.bootstrap.MonthField,  {
24291     
24292     content : {
24293         tag: 'tbody',
24294         cn: [
24295         {
24296             tag: 'tr',
24297             cn: [
24298             {
24299                 tag: 'td',
24300                 colspan: '7'
24301             }
24302             ]
24303         }
24304         ]
24305     },
24306     
24307     dates:{
24308         en: {
24309             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
24310             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
24311         }
24312     }
24313 });
24314
24315 Roo.apply(Roo.bootstrap.MonthField,  {
24316   
24317     template : {
24318         tag: 'div',
24319         cls: 'datepicker dropdown-menu roo-dynamic',
24320         cn: [
24321             {
24322                 tag: 'div',
24323                 cls: 'datepicker-months',
24324                 cn: [
24325                 {
24326                     tag: 'table',
24327                     cls: 'table-condensed',
24328                     cn:[
24329                         Roo.bootstrap.DateField.content
24330                     ]
24331                 }
24332                 ]
24333             }
24334         ]
24335     }
24336 });
24337
24338  
24339
24340  
24341  /*
24342  * - LGPL
24343  *
24344  * CheckBox
24345  * 
24346  */
24347
24348 /**
24349  * @class Roo.bootstrap.CheckBox
24350  * @extends Roo.bootstrap.Input
24351  * Bootstrap CheckBox class
24352  * 
24353  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
24354  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
24355  * @cfg {String} boxLabel The text that appears beside the checkbox
24356  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
24357  * @cfg {Boolean} checked initnal the element
24358  * @cfg {Boolean} inline inline the element (default false)
24359  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
24360  * @cfg {String} tooltip label tooltip
24361  * 
24362  * @constructor
24363  * Create a new CheckBox
24364  * @param {Object} config The config object
24365  */
24366
24367 Roo.bootstrap.CheckBox = function(config){
24368     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
24369    
24370     this.addEvents({
24371         /**
24372         * @event check
24373         * Fires when the element is checked or unchecked.
24374         * @param {Roo.bootstrap.CheckBox} this This input
24375         * @param {Boolean} checked The new checked value
24376         */
24377        check : true,
24378        /**
24379         * @event click
24380         * Fires when the element is click.
24381         * @param {Roo.bootstrap.CheckBox} this This input
24382         */
24383        click : true
24384     });
24385     
24386 };
24387
24388 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
24389   
24390     inputType: 'checkbox',
24391     inputValue: 1,
24392     valueOff: 0,
24393     boxLabel: false,
24394     checked: false,
24395     weight : false,
24396     inline: false,
24397     tooltip : '',
24398     
24399     // checkbox success does not make any sense really.. 
24400     invalidClass : "",
24401     validClass : "",
24402     
24403     
24404     getAutoCreate : function()
24405     {
24406         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
24407         
24408         var id = Roo.id();
24409         
24410         var cfg = {};
24411         
24412         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
24413         
24414         if(this.inline){
24415             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
24416         }
24417         
24418         var input =  {
24419             tag: 'input',
24420             id : id,
24421             type : this.inputType,
24422             value : this.inputValue,
24423             cls : 'roo-' + this.inputType, //'form-box',
24424             placeholder : this.placeholder || ''
24425             
24426         };
24427         
24428         if(this.inputType != 'radio'){
24429             var hidden =  {
24430                 tag: 'input',
24431                 type : 'hidden',
24432                 cls : 'roo-hidden-value',
24433                 value : this.checked ? this.inputValue : this.valueOff
24434             };
24435         }
24436         
24437             
24438         if (this.weight) { // Validity check?
24439             cfg.cls += " " + this.inputType + "-" + this.weight;
24440         }
24441         
24442         if (this.disabled) {
24443             input.disabled=true;
24444         }
24445         
24446         if(this.checked){
24447             input.checked = this.checked;
24448         }
24449         
24450         if (this.name) {
24451             
24452             input.name = this.name;
24453             
24454             if(this.inputType != 'radio'){
24455                 hidden.name = this.name;
24456                 input.name = '_hidden_' + this.name;
24457             }
24458         }
24459         
24460         if (this.size) {
24461             input.cls += ' input-' + this.size;
24462         }
24463         
24464         var settings=this;
24465         
24466         ['xs','sm','md','lg'].map(function(size){
24467             if (settings[size]) {
24468                 cfg.cls += ' col-' + size + '-' + settings[size];
24469             }
24470         });
24471         
24472         var inputblock = input;
24473          
24474         if (this.before || this.after) {
24475             
24476             inputblock = {
24477                 cls : 'input-group',
24478                 cn :  [] 
24479             };
24480             
24481             if (this.before) {
24482                 inputblock.cn.push({
24483                     tag :'span',
24484                     cls : 'input-group-addon',
24485                     html : this.before
24486                 });
24487             }
24488             
24489             inputblock.cn.push(input);
24490             
24491             if(this.inputType != 'radio'){
24492                 inputblock.cn.push(hidden);
24493             }
24494             
24495             if (this.after) {
24496                 inputblock.cn.push({
24497                     tag :'span',
24498                     cls : 'input-group-addon',
24499                     html : this.after
24500                 });
24501             }
24502             
24503         }
24504         var boxLabelCfg = false;
24505         
24506         if(this.boxLabel){
24507            
24508             boxLabelCfg = {
24509                 tag: 'label',
24510                 //'for': id, // box label is handled by onclick - so no for...
24511                 cls: 'box-label',
24512                 html: this.boxLabel
24513             };
24514             if(this.tooltip){
24515                 boxLabelCfg.tooltip = this.tooltip;
24516             }
24517              
24518         }
24519         
24520         
24521         if (align ==='left' && this.fieldLabel.length) {
24522 //                Roo.log("left and has label");
24523             cfg.cn = [
24524                 {
24525                     tag: 'label',
24526                     'for' :  id,
24527                     cls : 'control-label',
24528                     html : this.fieldLabel
24529                 },
24530                 {
24531                     cls : "", 
24532                     cn: [
24533                         inputblock
24534                     ]
24535                 }
24536             ];
24537             
24538             if (boxLabelCfg) {
24539                 cfg.cn[1].cn.push(boxLabelCfg);
24540             }
24541             
24542             if(this.labelWidth > 12){
24543                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24544             }
24545             
24546             if(this.labelWidth < 13 && this.labelmd == 0){
24547                 this.labelmd = this.labelWidth;
24548             }
24549             
24550             if(this.labellg > 0){
24551                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24552                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24553             }
24554             
24555             if(this.labelmd > 0){
24556                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24557                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24558             }
24559             
24560             if(this.labelsm > 0){
24561                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24562                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24563             }
24564             
24565             if(this.labelxs > 0){
24566                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24567                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24568             }
24569             
24570         } else if ( this.fieldLabel.length) {
24571 //                Roo.log(" label");
24572                 cfg.cn = [
24573                    
24574                     {
24575                         tag: this.boxLabel ? 'span' : 'label',
24576                         'for': id,
24577                         cls: 'control-label box-input-label',
24578                         //cls : 'input-group-addon',
24579                         html : this.fieldLabel
24580                     },
24581                     
24582                     inputblock
24583                     
24584                 ];
24585                 if (boxLabelCfg) {
24586                     cfg.cn.push(boxLabelCfg);
24587                 }
24588
24589         } else {
24590             
24591 //                Roo.log(" no label && no align");
24592                 cfg.cn = [  inputblock ] ;
24593                 if (boxLabelCfg) {
24594                     cfg.cn.push(boxLabelCfg);
24595                 }
24596
24597                 
24598         }
24599         
24600        
24601         
24602         if(this.inputType != 'radio'){
24603             cfg.cn.push(hidden);
24604         }
24605         
24606         return cfg;
24607         
24608     },
24609     
24610     /**
24611      * return the real input element.
24612      */
24613     inputEl: function ()
24614     {
24615         return this.el.select('input.roo-' + this.inputType,true).first();
24616     },
24617     hiddenEl: function ()
24618     {
24619         return this.el.select('input.roo-hidden-value',true).first();
24620     },
24621     
24622     labelEl: function()
24623     {
24624         return this.el.select('label.control-label',true).first();
24625     },
24626     /* depricated... */
24627     
24628     label: function()
24629     {
24630         return this.labelEl();
24631     },
24632     
24633     boxLabelEl: function()
24634     {
24635         return this.el.select('label.box-label',true).first();
24636     },
24637     
24638     initEvents : function()
24639     {
24640 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24641         
24642         this.inputEl().on('click', this.onClick,  this);
24643         
24644         if (this.boxLabel) { 
24645             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24646         }
24647         
24648         this.startValue = this.getValue();
24649         
24650         if(this.groupId){
24651             Roo.bootstrap.CheckBox.register(this);
24652         }
24653     },
24654     
24655     onClick : function(e)
24656     {   
24657         if(this.fireEvent('click', this, e) !== false){
24658             this.setChecked(!this.checked);
24659         }
24660         
24661     },
24662     
24663     setChecked : function(state,suppressEvent)
24664     {
24665         this.startValue = this.getValue();
24666
24667         if(this.inputType == 'radio'){
24668             
24669             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24670                 e.dom.checked = false;
24671             });
24672             
24673             this.inputEl().dom.checked = true;
24674             
24675             this.inputEl().dom.value = this.inputValue;
24676             
24677             if(suppressEvent !== true){
24678                 this.fireEvent('check', this, true);
24679             }
24680             
24681             this.validate();
24682             
24683             return;
24684         }
24685         
24686         this.checked = state;
24687         
24688         this.inputEl().dom.checked = state;
24689         
24690         
24691         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24692         
24693         if(suppressEvent !== true){
24694             this.fireEvent('check', this, state);
24695         }
24696         
24697         this.validate();
24698     },
24699     
24700     getValue : function()
24701     {
24702         if(this.inputType == 'radio'){
24703             return this.getGroupValue();
24704         }
24705         
24706         return this.hiddenEl().dom.value;
24707         
24708     },
24709     
24710     getGroupValue : function()
24711     {
24712         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24713             return '';
24714         }
24715         
24716         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24717     },
24718     
24719     setValue : function(v,suppressEvent)
24720     {
24721         if(this.inputType == 'radio'){
24722             this.setGroupValue(v, suppressEvent);
24723             return;
24724         }
24725         
24726         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24727         
24728         this.validate();
24729     },
24730     
24731     setGroupValue : function(v, suppressEvent)
24732     {
24733         this.startValue = this.getValue();
24734         
24735         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24736             e.dom.checked = false;
24737             
24738             if(e.dom.value == v){
24739                 e.dom.checked = true;
24740             }
24741         });
24742         
24743         if(suppressEvent !== true){
24744             this.fireEvent('check', this, true);
24745         }
24746
24747         this.validate();
24748         
24749         return;
24750     },
24751     
24752     validate : function()
24753     {
24754         if(this.getVisibilityEl().hasClass('hidden')){
24755             return true;
24756         }
24757         
24758         if(
24759                 this.disabled || 
24760                 (this.inputType == 'radio' && this.validateRadio()) ||
24761                 (this.inputType == 'checkbox' && this.validateCheckbox())
24762         ){
24763             this.markValid();
24764             return true;
24765         }
24766         
24767         this.markInvalid();
24768         return false;
24769     },
24770     
24771     validateRadio : function()
24772     {
24773         if(this.getVisibilityEl().hasClass('hidden')){
24774             return true;
24775         }
24776         
24777         if(this.allowBlank){
24778             return true;
24779         }
24780         
24781         var valid = false;
24782         
24783         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24784             if(!e.dom.checked){
24785                 return;
24786             }
24787             
24788             valid = true;
24789             
24790             return false;
24791         });
24792         
24793         return valid;
24794     },
24795     
24796     validateCheckbox : function()
24797     {
24798         if(!this.groupId){
24799             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24800             //return (this.getValue() == this.inputValue) ? true : false;
24801         }
24802         
24803         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24804         
24805         if(!group){
24806             return false;
24807         }
24808         
24809         var r = false;
24810         
24811         for(var i in group){
24812             if(group[i].el.isVisible(true)){
24813                 r = false;
24814                 break;
24815             }
24816             
24817             r = true;
24818         }
24819         
24820         for(var i in group){
24821             if(r){
24822                 break;
24823             }
24824             
24825             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24826         }
24827         
24828         return r;
24829     },
24830     
24831     /**
24832      * Mark this field as valid
24833      */
24834     markValid : function()
24835     {
24836         var _this = this;
24837         
24838         this.fireEvent('valid', this);
24839         
24840         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24841         
24842         if(this.groupId){
24843             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24844         }
24845         
24846         if(label){
24847             label.markValid();
24848         }
24849
24850         if(this.inputType == 'radio'){
24851             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24852                 var fg = e.findParent('.form-group', false, true);
24853                 if (Roo.bootstrap.version == 3) {
24854                     fg.removeClass([_this.invalidClass, _this.validClass]);
24855                     fg.addClass(_this.validClass);
24856                 } else {
24857                     fg.removeClass(['is-valid', 'is-invalid']);
24858                     fg.addClass('is-valid');
24859                 }
24860             });
24861             
24862             return;
24863         }
24864
24865         if(!this.groupId){
24866             var fg = this.el.findParent('.form-group', false, true);
24867             if (Roo.bootstrap.version == 3) {
24868                 fg.removeClass([this.invalidClass, this.validClass]);
24869                 fg.addClass(this.validClass);
24870             } else {
24871                 fg.removeClass(['is-valid', 'is-invalid']);
24872                 fg.addClass('is-valid');
24873             }
24874             return;
24875         }
24876         
24877         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24878         
24879         if(!group){
24880             return;
24881         }
24882         
24883         for(var i in group){
24884             var fg = group[i].el.findParent('.form-group', false, true);
24885             if (Roo.bootstrap.version == 3) {
24886                 fg.removeClass([this.invalidClass, this.validClass]);
24887                 fg.addClass(this.validClass);
24888             } else {
24889                 fg.removeClass(['is-valid', 'is-invalid']);
24890                 fg.addClass('is-valid');
24891             }
24892         }
24893     },
24894     
24895      /**
24896      * Mark this field as invalid
24897      * @param {String} msg The validation message
24898      */
24899     markInvalid : function(msg)
24900     {
24901         if(this.allowBlank){
24902             return;
24903         }
24904         
24905         var _this = this;
24906         
24907         this.fireEvent('invalid', this, msg);
24908         
24909         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24910         
24911         if(this.groupId){
24912             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24913         }
24914         
24915         if(label){
24916             label.markInvalid();
24917         }
24918             
24919         if(this.inputType == 'radio'){
24920             
24921             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24922                 var fg = e.findParent('.form-group', false, true);
24923                 if (Roo.bootstrap.version == 3) {
24924                     fg.removeClass([_this.invalidClass, _this.validClass]);
24925                     fg.addClass(_this.invalidClass);
24926                 } else {
24927                     fg.removeClass(['is-invalid', 'is-valid']);
24928                     fg.addClass('is-invalid');
24929                 }
24930             });
24931             
24932             return;
24933         }
24934         
24935         if(!this.groupId){
24936             var fg = this.el.findParent('.form-group', false, true);
24937             if (Roo.bootstrap.version == 3) {
24938                 fg.removeClass([_this.invalidClass, _this.validClass]);
24939                 fg.addClass(_this.invalidClass);
24940             } else {
24941                 fg.removeClass(['is-invalid', 'is-valid']);
24942                 fg.addClass('is-invalid');
24943             }
24944             return;
24945         }
24946         
24947         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24948         
24949         if(!group){
24950             return;
24951         }
24952         
24953         for(var i in group){
24954             var fg = group[i].el.findParent('.form-group', false, true);
24955             if (Roo.bootstrap.version == 3) {
24956                 fg.removeClass([_this.invalidClass, _this.validClass]);
24957                 fg.addClass(_this.invalidClass);
24958             } else {
24959                 fg.removeClass(['is-invalid', 'is-valid']);
24960                 fg.addClass('is-invalid');
24961             }
24962         }
24963         
24964     },
24965     
24966     clearInvalid : function()
24967     {
24968         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24969         
24970         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24971         
24972         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24973         
24974         if (label && label.iconEl) {
24975             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24976             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24977         }
24978     },
24979     
24980     disable : function()
24981     {
24982         if(this.inputType != 'radio'){
24983             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24984             return;
24985         }
24986         
24987         var _this = this;
24988         
24989         if(this.rendered){
24990             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24991                 _this.getActionEl().addClass(this.disabledClass);
24992                 e.dom.disabled = true;
24993             });
24994         }
24995         
24996         this.disabled = true;
24997         this.fireEvent("disable", this);
24998         return this;
24999     },
25000
25001     enable : function()
25002     {
25003         if(this.inputType != 'radio'){
25004             Roo.bootstrap.CheckBox.superclass.enable.call(this);
25005             return;
25006         }
25007         
25008         var _this = this;
25009         
25010         if(this.rendered){
25011             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
25012                 _this.getActionEl().removeClass(this.disabledClass);
25013                 e.dom.disabled = false;
25014             });
25015         }
25016         
25017         this.disabled = false;
25018         this.fireEvent("enable", this);
25019         return this;
25020     },
25021     
25022     setBoxLabel : function(v)
25023     {
25024         this.boxLabel = v;
25025         
25026         if(this.rendered){
25027             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25028         }
25029     }
25030
25031 });
25032
25033 Roo.apply(Roo.bootstrap.CheckBox, {
25034     
25035     groups: {},
25036     
25037      /**
25038     * register a CheckBox Group
25039     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
25040     */
25041     register : function(checkbox)
25042     {
25043         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
25044             this.groups[checkbox.groupId] = {};
25045         }
25046         
25047         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
25048             return;
25049         }
25050         
25051         this.groups[checkbox.groupId][checkbox.name] = checkbox;
25052         
25053     },
25054     /**
25055     * fetch a CheckBox Group based on the group ID
25056     * @param {string} the group ID
25057     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
25058     */
25059     get: function(groupId) {
25060         if (typeof(this.groups[groupId]) == 'undefined') {
25061             return false;
25062         }
25063         
25064         return this.groups[groupId] ;
25065     }
25066     
25067     
25068 });
25069 /*
25070  * - LGPL
25071  *
25072  * RadioItem
25073  * 
25074  */
25075
25076 /**
25077  * @class Roo.bootstrap.Radio
25078  * @extends Roo.bootstrap.Component
25079  * Bootstrap Radio class
25080  * @cfg {String} boxLabel - the label associated
25081  * @cfg {String} value - the value of radio
25082  * 
25083  * @constructor
25084  * Create a new Radio
25085  * @param {Object} config The config object
25086  */
25087 Roo.bootstrap.Radio = function(config){
25088     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
25089     
25090 };
25091
25092 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
25093     
25094     boxLabel : '',
25095     
25096     value : '',
25097     
25098     getAutoCreate : function()
25099     {
25100         var cfg = {
25101             tag : 'div',
25102             cls : 'form-group radio',
25103             cn : [
25104                 {
25105                     tag : 'label',
25106                     cls : 'box-label',
25107                     html : this.boxLabel
25108                 }
25109             ]
25110         };
25111         
25112         return cfg;
25113     },
25114     
25115     initEvents : function() 
25116     {
25117         this.parent().register(this);
25118         
25119         this.el.on('click', this.onClick, this);
25120         
25121     },
25122     
25123     onClick : function(e)
25124     {
25125         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
25126             this.setChecked(true);
25127         }
25128     },
25129     
25130     setChecked : function(state, suppressEvent)
25131     {
25132         this.parent().setValue(this.value, suppressEvent);
25133         
25134     },
25135     
25136     setBoxLabel : function(v)
25137     {
25138         this.boxLabel = v;
25139         
25140         if(this.rendered){
25141             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
25142         }
25143     }
25144     
25145 });
25146  
25147
25148  /*
25149  * - LGPL
25150  *
25151  * Input
25152  * 
25153  */
25154
25155 /**
25156  * @class Roo.bootstrap.SecurePass
25157  * @extends Roo.bootstrap.Input
25158  * Bootstrap SecurePass class
25159  *
25160  * 
25161  * @constructor
25162  * Create a new SecurePass
25163  * @param {Object} config The config object
25164  */
25165  
25166 Roo.bootstrap.SecurePass = function (config) {
25167     // these go here, so the translation tool can replace them..
25168     this.errors = {
25169         PwdEmpty: "Please type a password, and then retype it to confirm.",
25170         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25171         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25172         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25173         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25174         FNInPwd: "Your password can't contain your first name. Please type a different password.",
25175         LNInPwd: "Your password can't contain your last name. Please type a different password.",
25176         TooWeak: "Your password is Too Weak."
25177     },
25178     this.meterLabel = "Password strength:";
25179     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
25180     this.meterClass = [
25181         "roo-password-meter-tooweak", 
25182         "roo-password-meter-weak", 
25183         "roo-password-meter-medium", 
25184         "roo-password-meter-strong", 
25185         "roo-password-meter-grey"
25186     ];
25187     
25188     this.errors = {};
25189     
25190     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
25191 }
25192
25193 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
25194     /**
25195      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
25196      * {
25197      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
25198      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
25199      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
25200      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
25201      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
25202      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
25203      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
25204      * })
25205      */
25206     // private
25207     
25208     meterWidth: 300,
25209     errorMsg :'',    
25210     errors: false,
25211     imageRoot: '/',
25212     /**
25213      * @cfg {String/Object} Label for the strength meter (defaults to
25214      * 'Password strength:')
25215      */
25216     // private
25217     meterLabel: '',
25218     /**
25219      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
25220      * ['Weak', 'Medium', 'Strong'])
25221      */
25222     // private    
25223     pwdStrengths: false,    
25224     // private
25225     strength: 0,
25226     // private
25227     _lastPwd: null,
25228     // private
25229     kCapitalLetter: 0,
25230     kSmallLetter: 1,
25231     kDigit: 2,
25232     kPunctuation: 3,
25233     
25234     insecure: false,
25235     // private
25236     initEvents: function ()
25237     {
25238         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
25239
25240         if (this.el.is('input[type=password]') && Roo.isSafari) {
25241             this.el.on('keydown', this.SafariOnKeyDown, this);
25242         }
25243
25244         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
25245     },
25246     // private
25247     onRender: function (ct, position)
25248     {
25249         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
25250         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
25251         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
25252
25253         this.trigger.createChild({
25254                    cn: [
25255                     {
25256                     //id: 'PwdMeter',
25257                     tag: 'div',
25258                     cls: 'roo-password-meter-grey col-xs-12',
25259                     style: {
25260                         //width: 0,
25261                         //width: this.meterWidth + 'px'                                                
25262                         }
25263                     },
25264                     {                            
25265                          cls: 'roo-password-meter-text'                          
25266                     }
25267                 ]            
25268         });
25269
25270          
25271         if (this.hideTrigger) {
25272             this.trigger.setDisplayed(false);
25273         }
25274         this.setSize(this.width || '', this.height || '');
25275     },
25276     // private
25277     onDestroy: function ()
25278     {
25279         if (this.trigger) {
25280             this.trigger.removeAllListeners();
25281             this.trigger.remove();
25282         }
25283         if (this.wrap) {
25284             this.wrap.remove();
25285         }
25286         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
25287     },
25288     // private
25289     checkStrength: function ()
25290     {
25291         var pwd = this.inputEl().getValue();
25292         if (pwd == this._lastPwd) {
25293             return;
25294         }
25295
25296         var strength;
25297         if (this.ClientSideStrongPassword(pwd)) {
25298             strength = 3;
25299         } else if (this.ClientSideMediumPassword(pwd)) {
25300             strength = 2;
25301         } else if (this.ClientSideWeakPassword(pwd)) {
25302             strength = 1;
25303         } else {
25304             strength = 0;
25305         }
25306         
25307         Roo.log('strength1: ' + strength);
25308         
25309         //var pm = this.trigger.child('div/div/div').dom;
25310         var pm = this.trigger.child('div/div');
25311         pm.removeClass(this.meterClass);
25312         pm.addClass(this.meterClass[strength]);
25313                 
25314         
25315         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25316                 
25317         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25318         
25319         this._lastPwd = pwd;
25320     },
25321     reset: function ()
25322     {
25323         Roo.bootstrap.SecurePass.superclass.reset.call(this);
25324         
25325         this._lastPwd = '';
25326         
25327         var pm = this.trigger.child('div/div');
25328         pm.removeClass(this.meterClass);
25329         pm.addClass('roo-password-meter-grey');        
25330         
25331         
25332         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25333         
25334         pt.innerHTML = '';
25335         this.inputEl().dom.type='password';
25336     },
25337     // private
25338     validateValue: function (value)
25339     {
25340         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
25341             return false;
25342         }
25343         if (value.length == 0) {
25344             if (this.allowBlank) {
25345                 this.clearInvalid();
25346                 return true;
25347             }
25348
25349             this.markInvalid(this.errors.PwdEmpty);
25350             this.errorMsg = this.errors.PwdEmpty;
25351             return false;
25352         }
25353         
25354         if(this.insecure){
25355             return true;
25356         }
25357         
25358         if (!value.match(/[\x21-\x7e]+/)) {
25359             this.markInvalid(this.errors.PwdBadChar);
25360             this.errorMsg = this.errors.PwdBadChar;
25361             return false;
25362         }
25363         if (value.length < 6) {
25364             this.markInvalid(this.errors.PwdShort);
25365             this.errorMsg = this.errors.PwdShort;
25366             return false;
25367         }
25368         if (value.length > 16) {
25369             this.markInvalid(this.errors.PwdLong);
25370             this.errorMsg = this.errors.PwdLong;
25371             return false;
25372         }
25373         var strength;
25374         if (this.ClientSideStrongPassword(value)) {
25375             strength = 3;
25376         } else if (this.ClientSideMediumPassword(value)) {
25377             strength = 2;
25378         } else if (this.ClientSideWeakPassword(value)) {
25379             strength = 1;
25380         } else {
25381             strength = 0;
25382         }
25383
25384         
25385         if (strength < 2) {
25386             //this.markInvalid(this.errors.TooWeak);
25387             this.errorMsg = this.errors.TooWeak;
25388             //return false;
25389         }
25390         
25391         
25392         console.log('strength2: ' + strength);
25393         
25394         //var pm = this.trigger.child('div/div/div').dom;
25395         
25396         var pm = this.trigger.child('div/div');
25397         pm.removeClass(this.meterClass);
25398         pm.addClass(this.meterClass[strength]);
25399                 
25400         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
25401                 
25402         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
25403         
25404         this.errorMsg = ''; 
25405         return true;
25406     },
25407     // private
25408     CharacterSetChecks: function (type)
25409     {
25410         this.type = type;
25411         this.fResult = false;
25412     },
25413     // private
25414     isctype: function (character, type)
25415     {
25416         switch (type) {  
25417             case this.kCapitalLetter:
25418                 if (character >= 'A' && character <= 'Z') {
25419                     return true;
25420                 }
25421                 break;
25422             
25423             case this.kSmallLetter:
25424                 if (character >= 'a' && character <= 'z') {
25425                     return true;
25426                 }
25427                 break;
25428             
25429             case this.kDigit:
25430                 if (character >= '0' && character <= '9') {
25431                     return true;
25432                 }
25433                 break;
25434             
25435             case this.kPunctuation:
25436                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
25437                     return true;
25438                 }
25439                 break;
25440             
25441             default:
25442                 return false;
25443         }
25444
25445     },
25446     // private
25447     IsLongEnough: function (pwd, size)
25448     {
25449         return !(pwd == null || isNaN(size) || pwd.length < size);
25450     },
25451     // private
25452     SpansEnoughCharacterSets: function (word, nb)
25453     {
25454         if (!this.IsLongEnough(word, nb))
25455         {
25456             return false;
25457         }
25458
25459         var characterSetChecks = new Array(
25460             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25461             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25462         );
25463         
25464         for (var index = 0; index < word.length; ++index) {
25465             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25466                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25467                     characterSetChecks[nCharSet].fResult = true;
25468                     break;
25469                 }
25470             }
25471         }
25472
25473         var nCharSets = 0;
25474         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25475             if (characterSetChecks[nCharSet].fResult) {
25476                 ++nCharSets;
25477             }
25478         }
25479
25480         if (nCharSets < nb) {
25481             return false;
25482         }
25483         return true;
25484     },
25485     // private
25486     ClientSideStrongPassword: function (pwd)
25487     {
25488         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25489     },
25490     // private
25491     ClientSideMediumPassword: function (pwd)
25492     {
25493         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25494     },
25495     // private
25496     ClientSideWeakPassword: function (pwd)
25497     {
25498         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25499     }
25500           
25501 })//<script type="text/javascript">
25502
25503 /*
25504  * Based  Ext JS Library 1.1.1
25505  * Copyright(c) 2006-2007, Ext JS, LLC.
25506  * LGPL
25507  *
25508  */
25509  
25510 /**
25511  * @class Roo.HtmlEditorCore
25512  * @extends Roo.Component
25513  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25514  *
25515  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25516  */
25517
25518 Roo.HtmlEditorCore = function(config){
25519     
25520     
25521     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25522     
25523     
25524     this.addEvents({
25525         /**
25526          * @event initialize
25527          * Fires when the editor is fully initialized (including the iframe)
25528          * @param {Roo.HtmlEditorCore} this
25529          */
25530         initialize: true,
25531         /**
25532          * @event activate
25533          * Fires when the editor is first receives the focus. Any insertion must wait
25534          * until after this event.
25535          * @param {Roo.HtmlEditorCore} this
25536          */
25537         activate: true,
25538          /**
25539          * @event beforesync
25540          * Fires before the textarea is updated with content from the editor iframe. Return false
25541          * to cancel the sync.
25542          * @param {Roo.HtmlEditorCore} this
25543          * @param {String} html
25544          */
25545         beforesync: true,
25546          /**
25547          * @event beforepush
25548          * Fires before the iframe editor is updated with content from the textarea. Return false
25549          * to cancel the push.
25550          * @param {Roo.HtmlEditorCore} this
25551          * @param {String} html
25552          */
25553         beforepush: true,
25554          /**
25555          * @event sync
25556          * Fires when the textarea is updated with content from the editor iframe.
25557          * @param {Roo.HtmlEditorCore} this
25558          * @param {String} html
25559          */
25560         sync: true,
25561          /**
25562          * @event push
25563          * Fires when the iframe editor is updated with content from the textarea.
25564          * @param {Roo.HtmlEditorCore} this
25565          * @param {String} html
25566          */
25567         push: true,
25568         
25569         /**
25570          * @event editorevent
25571          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25572          * @param {Roo.HtmlEditorCore} this
25573          */
25574         editorevent: true
25575         
25576     });
25577     
25578     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25579     
25580     // defaults : white / black...
25581     this.applyBlacklists();
25582     
25583     
25584     
25585 };
25586
25587
25588 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25589
25590
25591      /**
25592      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25593      */
25594     
25595     owner : false,
25596     
25597      /**
25598      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25599      *                        Roo.resizable.
25600      */
25601     resizable : false,
25602      /**
25603      * @cfg {Number} height (in pixels)
25604      */   
25605     height: 300,
25606    /**
25607      * @cfg {Number} width (in pixels)
25608      */   
25609     width: 500,
25610     
25611     /**
25612      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25613      * 
25614      */
25615     stylesheets: false,
25616     
25617     /**
25618      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
25619      */
25620     allowComments: false,
25621     // id of frame..
25622     frameId: false,
25623     
25624     // private properties
25625     validationEvent : false,
25626     deferHeight: true,
25627     initialized : false,
25628     activated : false,
25629     sourceEditMode : false,
25630     onFocus : Roo.emptyFn,
25631     iframePad:3,
25632     hideMode:'offsets',
25633     
25634     clearUp: true,
25635     
25636     // blacklist + whitelisted elements..
25637     black: false,
25638     white: false,
25639      
25640     bodyCls : '',
25641
25642     /**
25643      * Protected method that will not generally be called directly. It
25644      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25645      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25646      */
25647     getDocMarkup : function(){
25648         // body styles..
25649         var st = '';
25650         
25651         // inherit styels from page...?? 
25652         if (this.stylesheets === false) {
25653             
25654             Roo.get(document.head).select('style').each(function(node) {
25655                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25656             });
25657             
25658             Roo.get(document.head).select('link').each(function(node) { 
25659                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25660             });
25661             
25662         } else if (!this.stylesheets.length) {
25663                 // simple..
25664                 st = '<style type="text/css">' +
25665                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25666                    '</style>';
25667         } else {
25668             for (var i in this.stylesheets) { 
25669                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25670             }
25671             
25672         }
25673         
25674         st +=  '<style type="text/css">' +
25675             'IMG { cursor: pointer } ' +
25676         '</style>';
25677
25678         var cls = 'roo-htmleditor-body';
25679         
25680         if(this.bodyCls.length){
25681             cls += ' ' + this.bodyCls;
25682         }
25683         
25684         return '<html><head>' + st  +
25685             //<style type="text/css">' +
25686             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25687             //'</style>' +
25688             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25689     },
25690
25691     // private
25692     onRender : function(ct, position)
25693     {
25694         var _t = this;
25695         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25696         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25697         
25698         
25699         this.el.dom.style.border = '0 none';
25700         this.el.dom.setAttribute('tabIndex', -1);
25701         this.el.addClass('x-hidden hide');
25702         
25703         
25704         
25705         if(Roo.isIE){ // fix IE 1px bogus margin
25706             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25707         }
25708        
25709         
25710         this.frameId = Roo.id();
25711         
25712          
25713         
25714         var iframe = this.owner.wrap.createChild({
25715             tag: 'iframe',
25716             cls: 'form-control', // bootstrap..
25717             id: this.frameId,
25718             name: this.frameId,
25719             frameBorder : 'no',
25720             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25721         }, this.el
25722         );
25723         
25724         
25725         this.iframe = iframe.dom;
25726
25727          this.assignDocWin();
25728         
25729         this.doc.designMode = 'on';
25730        
25731         this.doc.open();
25732         this.doc.write(this.getDocMarkup());
25733         this.doc.close();
25734
25735         
25736         var task = { // must defer to wait for browser to be ready
25737             run : function(){
25738                 //console.log("run task?" + this.doc.readyState);
25739                 this.assignDocWin();
25740                 if(this.doc.body || this.doc.readyState == 'complete'){
25741                     try {
25742                         this.doc.designMode="on";
25743                     } catch (e) {
25744                         return;
25745                     }
25746                     Roo.TaskMgr.stop(task);
25747                     this.initEditor.defer(10, this);
25748                 }
25749             },
25750             interval : 10,
25751             duration: 10000,
25752             scope: this
25753         };
25754         Roo.TaskMgr.start(task);
25755
25756     },
25757
25758     // private
25759     onResize : function(w, h)
25760     {
25761          Roo.log('resize: ' +w + ',' + h );
25762         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25763         if(!this.iframe){
25764             return;
25765         }
25766         if(typeof w == 'number'){
25767             
25768             this.iframe.style.width = w + 'px';
25769         }
25770         if(typeof h == 'number'){
25771             
25772             this.iframe.style.height = h + 'px';
25773             if(this.doc){
25774                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25775             }
25776         }
25777         
25778     },
25779
25780     /**
25781      * Toggles the editor between standard and source edit mode.
25782      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25783      */
25784     toggleSourceEdit : function(sourceEditMode){
25785         
25786         this.sourceEditMode = sourceEditMode === true;
25787         
25788         if(this.sourceEditMode){
25789  
25790             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25791             
25792         }else{
25793             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25794             //this.iframe.className = '';
25795             this.deferFocus();
25796         }
25797         //this.setSize(this.owner.wrap.getSize());
25798         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25799     },
25800
25801     
25802   
25803
25804     /**
25805      * Protected method that will not generally be called directly. If you need/want
25806      * custom HTML cleanup, this is the method you should override.
25807      * @param {String} html The HTML to be cleaned
25808      * return {String} The cleaned HTML
25809      */
25810     cleanHtml : function(html){
25811         html = String(html);
25812         if(html.length > 5){
25813             if(Roo.isSafari){ // strip safari nonsense
25814                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25815             }
25816         }
25817         if(html == '&nbsp;'){
25818             html = '';
25819         }
25820         return html;
25821     },
25822
25823     /**
25824      * HTML Editor -> Textarea
25825      * Protected method that will not generally be called directly. Syncs the contents
25826      * of the editor iframe with the textarea.
25827      */
25828     syncValue : function(){
25829         if(this.initialized){
25830             var bd = (this.doc.body || this.doc.documentElement);
25831             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25832             var html = bd.innerHTML;
25833             if(Roo.isSafari){
25834                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25835                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25836                 if(m && m[1]){
25837                     html = '<div style="'+m[0]+'">' + html + '</div>';
25838                 }
25839             }
25840             html = this.cleanHtml(html);
25841             // fix up the special chars.. normaly like back quotes in word...
25842             // however we do not want to do this with chinese..
25843             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25844                 
25845                 var cc = match.charCodeAt();
25846
25847                 // Get the character value, handling surrogate pairs
25848                 if (match.length == 2) {
25849                     // It's a surrogate pair, calculate the Unicode code point
25850                     var high = match.charCodeAt(0) - 0xD800;
25851                     var low  = match.charCodeAt(1) - 0xDC00;
25852                     cc = (high * 0x400) + low + 0x10000;
25853                 }  else if (
25854                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25855                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25856                     (cc >= 0xf900 && cc < 0xfb00 )
25857                 ) {
25858                         return match;
25859                 }  
25860          
25861                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25862                 return "&#" + cc + ";";
25863                 
25864                 
25865             });
25866             
25867             
25868              
25869             if(this.owner.fireEvent('beforesync', this, html) !== false){
25870                 this.el.dom.value = html;
25871                 this.owner.fireEvent('sync', this, html);
25872             }
25873         }
25874     },
25875
25876     /**
25877      * Protected method that will not generally be called directly. Pushes the value of the textarea
25878      * into the iframe editor.
25879      */
25880     pushValue : function(){
25881         if(this.initialized){
25882             var v = this.el.dom.value.trim();
25883             
25884 //            if(v.length < 1){
25885 //                v = '&#160;';
25886 //            }
25887             
25888             if(this.owner.fireEvent('beforepush', this, v) !== false){
25889                 var d = (this.doc.body || this.doc.documentElement);
25890                 d.innerHTML = v;
25891                 this.cleanUpPaste();
25892                 this.el.dom.value = d.innerHTML;
25893                 this.owner.fireEvent('push', this, v);
25894             }
25895         }
25896     },
25897
25898     // private
25899     deferFocus : function(){
25900         this.focus.defer(10, this);
25901     },
25902
25903     // doc'ed in Field
25904     focus : function(){
25905         if(this.win && !this.sourceEditMode){
25906             this.win.focus();
25907         }else{
25908             this.el.focus();
25909         }
25910     },
25911     
25912     assignDocWin: function()
25913     {
25914         var iframe = this.iframe;
25915         
25916          if(Roo.isIE){
25917             this.doc = iframe.contentWindow.document;
25918             this.win = iframe.contentWindow;
25919         } else {
25920 //            if (!Roo.get(this.frameId)) {
25921 //                return;
25922 //            }
25923 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25924 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25925             
25926             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25927                 return;
25928             }
25929             
25930             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25931             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25932         }
25933     },
25934     
25935     // private
25936     initEditor : function(){
25937         //console.log("INIT EDITOR");
25938         this.assignDocWin();
25939         
25940         
25941         
25942         this.doc.designMode="on";
25943         this.doc.open();
25944         this.doc.write(this.getDocMarkup());
25945         this.doc.close();
25946         
25947         var dbody = (this.doc.body || this.doc.documentElement);
25948         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25949         // this copies styles from the containing element into thsi one..
25950         // not sure why we need all of this..
25951         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25952         
25953         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25954         //ss['background-attachment'] = 'fixed'; // w3c
25955         dbody.bgProperties = 'fixed'; // ie
25956         //Roo.DomHelper.applyStyles(dbody, ss);
25957         Roo.EventManager.on(this.doc, {
25958             //'mousedown': this.onEditorEvent,
25959             'mouseup': this.onEditorEvent,
25960             'dblclick': this.onEditorEvent,
25961             'click': this.onEditorEvent,
25962             'keyup': this.onEditorEvent,
25963             buffer:100,
25964             scope: this
25965         });
25966         if(Roo.isGecko){
25967             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25968         }
25969         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25970             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25971         }
25972         this.initialized = true;
25973
25974         this.owner.fireEvent('initialize', this);
25975         this.pushValue();
25976     },
25977
25978     // private
25979     onDestroy : function(){
25980         
25981         
25982         
25983         if(this.rendered){
25984             
25985             //for (var i =0; i < this.toolbars.length;i++) {
25986             //    // fixme - ask toolbars for heights?
25987             //    this.toolbars[i].onDestroy();
25988            // }
25989             
25990             //this.wrap.dom.innerHTML = '';
25991             //this.wrap.remove();
25992         }
25993     },
25994
25995     // private
25996     onFirstFocus : function(){
25997         
25998         this.assignDocWin();
25999         
26000         
26001         this.activated = true;
26002          
26003     
26004         if(Roo.isGecko){ // prevent silly gecko errors
26005             this.win.focus();
26006             var s = this.win.getSelection();
26007             if(!s.focusNode || s.focusNode.nodeType != 3){
26008                 var r = s.getRangeAt(0);
26009                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
26010                 r.collapse(true);
26011                 this.deferFocus();
26012             }
26013             try{
26014                 this.execCmd('useCSS', true);
26015                 this.execCmd('styleWithCSS', false);
26016             }catch(e){}
26017         }
26018         this.owner.fireEvent('activate', this);
26019     },
26020
26021     // private
26022     adjustFont: function(btn){
26023         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
26024         //if(Roo.isSafari){ // safari
26025         //    adjust *= 2;
26026        // }
26027         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
26028         if(Roo.isSafari){ // safari
26029             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
26030             v =  (v < 10) ? 10 : v;
26031             v =  (v > 48) ? 48 : v;
26032             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
26033             
26034         }
26035         
26036         
26037         v = Math.max(1, v+adjust);
26038         
26039         this.execCmd('FontSize', v  );
26040     },
26041
26042     onEditorEvent : function(e)
26043     {
26044         this.owner.fireEvent('editorevent', this, e);
26045       //  this.updateToolbar();
26046         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
26047     },
26048
26049     insertTag : function(tg)
26050     {
26051         // could be a bit smarter... -> wrap the current selected tRoo..
26052         if (tg.toLowerCase() == 'span' ||
26053             tg.toLowerCase() == 'code' ||
26054             tg.toLowerCase() == 'sup' ||
26055             tg.toLowerCase() == 'sub' 
26056             ) {
26057             
26058             range = this.createRange(this.getSelection());
26059             var wrappingNode = this.doc.createElement(tg.toLowerCase());
26060             wrappingNode.appendChild(range.extractContents());
26061             range.insertNode(wrappingNode);
26062
26063             return;
26064             
26065             
26066             
26067         }
26068         this.execCmd("formatblock",   tg);
26069         
26070     },
26071     
26072     insertText : function(txt)
26073     {
26074         
26075         
26076         var range = this.createRange();
26077         range.deleteContents();
26078                //alert(Sender.getAttribute('label'));
26079                
26080         range.insertNode(this.doc.createTextNode(txt));
26081     } ,
26082     
26083      
26084
26085     /**
26086      * Executes a Midas editor command on the editor document and performs necessary focus and
26087      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
26088      * @param {String} cmd The Midas command
26089      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26090      */
26091     relayCmd : function(cmd, value){
26092         this.win.focus();
26093         this.execCmd(cmd, value);
26094         this.owner.fireEvent('editorevent', this);
26095         //this.updateToolbar();
26096         this.owner.deferFocus();
26097     },
26098
26099     /**
26100      * Executes a Midas editor command directly on the editor document.
26101      * For visual commands, you should use {@link #relayCmd} instead.
26102      * <b>This should only be called after the editor is initialized.</b>
26103      * @param {String} cmd The Midas command
26104      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
26105      */
26106     execCmd : function(cmd, value){
26107         this.doc.execCommand(cmd, false, value === undefined ? null : value);
26108         this.syncValue();
26109     },
26110  
26111  
26112    
26113     /**
26114      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
26115      * to insert tRoo.
26116      * @param {String} text | dom node.. 
26117      */
26118     insertAtCursor : function(text)
26119     {
26120         
26121         if(!this.activated){
26122             return;
26123         }
26124         /*
26125         if(Roo.isIE){
26126             this.win.focus();
26127             var r = this.doc.selection.createRange();
26128             if(r){
26129                 r.collapse(true);
26130                 r.pasteHTML(text);
26131                 this.syncValue();
26132                 this.deferFocus();
26133             
26134             }
26135             return;
26136         }
26137         */
26138         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
26139             this.win.focus();
26140             
26141             
26142             // from jquery ui (MIT licenced)
26143             var range, node;
26144             var win = this.win;
26145             
26146             if (win.getSelection && win.getSelection().getRangeAt) {
26147                 range = win.getSelection().getRangeAt(0);
26148                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
26149                 range.insertNode(node);
26150             } else if (win.document.selection && win.document.selection.createRange) {
26151                 // no firefox support
26152                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26153                 win.document.selection.createRange().pasteHTML(txt);
26154             } else {
26155                 // no firefox support
26156                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
26157                 this.execCmd('InsertHTML', txt);
26158             } 
26159             
26160             this.syncValue();
26161             
26162             this.deferFocus();
26163         }
26164     },
26165  // private
26166     mozKeyPress : function(e){
26167         if(e.ctrlKey){
26168             var c = e.getCharCode(), cmd;
26169           
26170             if(c > 0){
26171                 c = String.fromCharCode(c).toLowerCase();
26172                 switch(c){
26173                     case 'b':
26174                         cmd = 'bold';
26175                         break;
26176                     case 'i':
26177                         cmd = 'italic';
26178                         break;
26179                     
26180                     case 'u':
26181                         cmd = 'underline';
26182                         break;
26183                     
26184                     case 'v':
26185                         this.cleanUpPaste.defer(100, this);
26186                         return;
26187                         
26188                 }
26189                 if(cmd){
26190                     this.win.focus();
26191                     this.execCmd(cmd);
26192                     this.deferFocus();
26193                     e.preventDefault();
26194                 }
26195                 
26196             }
26197         }
26198     },
26199
26200     // private
26201     fixKeys : function(){ // load time branching for fastest keydown performance
26202         if(Roo.isIE){
26203             return function(e){
26204                 var k = e.getKey(), r;
26205                 if(k == e.TAB){
26206                     e.stopEvent();
26207                     r = this.doc.selection.createRange();
26208                     if(r){
26209                         r.collapse(true);
26210                         r.pasteHTML('&#160;&#160;&#160;&#160;');
26211                         this.deferFocus();
26212                     }
26213                     return;
26214                 }
26215                 
26216                 if(k == e.ENTER){
26217                     r = this.doc.selection.createRange();
26218                     if(r){
26219                         var target = r.parentElement();
26220                         if(!target || target.tagName.toLowerCase() != 'li'){
26221                             e.stopEvent();
26222                             r.pasteHTML('<br />');
26223                             r.collapse(false);
26224                             r.select();
26225                         }
26226                     }
26227                 }
26228                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26229                     this.cleanUpPaste.defer(100, this);
26230                     return;
26231                 }
26232                 
26233                 
26234             };
26235         }else if(Roo.isOpera){
26236             return function(e){
26237                 var k = e.getKey();
26238                 if(k == e.TAB){
26239                     e.stopEvent();
26240                     this.win.focus();
26241                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
26242                     this.deferFocus();
26243                 }
26244                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26245                     this.cleanUpPaste.defer(100, this);
26246                     return;
26247                 }
26248                 
26249             };
26250         }else if(Roo.isSafari){
26251             return function(e){
26252                 var k = e.getKey();
26253                 
26254                 if(k == e.TAB){
26255                     e.stopEvent();
26256                     this.execCmd('InsertText','\t');
26257                     this.deferFocus();
26258                     return;
26259                 }
26260                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
26261                     this.cleanUpPaste.defer(100, this);
26262                     return;
26263                 }
26264                 
26265              };
26266         }
26267     }(),
26268     
26269     getAllAncestors: function()
26270     {
26271         var p = this.getSelectedNode();
26272         var a = [];
26273         if (!p) {
26274             a.push(p); // push blank onto stack..
26275             p = this.getParentElement();
26276         }
26277         
26278         
26279         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
26280             a.push(p);
26281             p = p.parentNode;
26282         }
26283         a.push(this.doc.body);
26284         return a;
26285     },
26286     lastSel : false,
26287     lastSelNode : false,
26288     
26289     
26290     getSelection : function() 
26291     {
26292         this.assignDocWin();
26293         return Roo.isIE ? this.doc.selection : this.win.getSelection();
26294     },
26295     
26296     getSelectedNode: function() 
26297     {
26298         // this may only work on Gecko!!!
26299         
26300         // should we cache this!!!!
26301         
26302         
26303         
26304          
26305         var range = this.createRange(this.getSelection()).cloneRange();
26306         
26307         if (Roo.isIE) {
26308             var parent = range.parentElement();
26309             while (true) {
26310                 var testRange = range.duplicate();
26311                 testRange.moveToElementText(parent);
26312                 if (testRange.inRange(range)) {
26313                     break;
26314                 }
26315                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
26316                     break;
26317                 }
26318                 parent = parent.parentElement;
26319             }
26320             return parent;
26321         }
26322         
26323         // is ancestor a text element.
26324         var ac =  range.commonAncestorContainer;
26325         if (ac.nodeType == 3) {
26326             ac = ac.parentNode;
26327         }
26328         
26329         var ar = ac.childNodes;
26330          
26331         var nodes = [];
26332         var other_nodes = [];
26333         var has_other_nodes = false;
26334         for (var i=0;i<ar.length;i++) {
26335             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
26336                 continue;
26337             }
26338             // fullly contained node.
26339             
26340             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
26341                 nodes.push(ar[i]);
26342                 continue;
26343             }
26344             
26345             // probably selected..
26346             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
26347                 other_nodes.push(ar[i]);
26348                 continue;
26349             }
26350             // outer..
26351             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
26352                 continue;
26353             }
26354             
26355             
26356             has_other_nodes = true;
26357         }
26358         if (!nodes.length && other_nodes.length) {
26359             nodes= other_nodes;
26360         }
26361         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
26362             return false;
26363         }
26364         
26365         return nodes[0];
26366     },
26367     createRange: function(sel)
26368     {
26369         // this has strange effects when using with 
26370         // top toolbar - not sure if it's a great idea.
26371         //this.editor.contentWindow.focus();
26372         if (typeof sel != "undefined") {
26373             try {
26374                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
26375             } catch(e) {
26376                 return this.doc.createRange();
26377             }
26378         } else {
26379             return this.doc.createRange();
26380         }
26381     },
26382     getParentElement: function()
26383     {
26384         
26385         this.assignDocWin();
26386         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
26387         
26388         var range = this.createRange(sel);
26389          
26390         try {
26391             var p = range.commonAncestorContainer;
26392             while (p.nodeType == 3) { // text node
26393                 p = p.parentNode;
26394             }
26395             return p;
26396         } catch (e) {
26397             return null;
26398         }
26399     
26400     },
26401     /***
26402      *
26403      * Range intersection.. the hard stuff...
26404      *  '-1' = before
26405      *  '0' = hits..
26406      *  '1' = after.
26407      *         [ -- selected range --- ]
26408      *   [fail]                        [fail]
26409      *
26410      *    basically..
26411      *      if end is before start or  hits it. fail.
26412      *      if start is after end or hits it fail.
26413      *
26414      *   if either hits (but other is outside. - then it's not 
26415      *   
26416      *    
26417      **/
26418     
26419     
26420     // @see http://www.thismuchiknow.co.uk/?p=64.
26421     rangeIntersectsNode : function(range, node)
26422     {
26423         var nodeRange = node.ownerDocument.createRange();
26424         try {
26425             nodeRange.selectNode(node);
26426         } catch (e) {
26427             nodeRange.selectNodeContents(node);
26428         }
26429     
26430         var rangeStartRange = range.cloneRange();
26431         rangeStartRange.collapse(true);
26432     
26433         var rangeEndRange = range.cloneRange();
26434         rangeEndRange.collapse(false);
26435     
26436         var nodeStartRange = nodeRange.cloneRange();
26437         nodeStartRange.collapse(true);
26438     
26439         var nodeEndRange = nodeRange.cloneRange();
26440         nodeEndRange.collapse(false);
26441     
26442         return rangeStartRange.compareBoundaryPoints(
26443                  Range.START_TO_START, nodeEndRange) == -1 &&
26444                rangeEndRange.compareBoundaryPoints(
26445                  Range.START_TO_START, nodeStartRange) == 1;
26446         
26447          
26448     },
26449     rangeCompareNode : function(range, node)
26450     {
26451         var nodeRange = node.ownerDocument.createRange();
26452         try {
26453             nodeRange.selectNode(node);
26454         } catch (e) {
26455             nodeRange.selectNodeContents(node);
26456         }
26457         
26458         
26459         range.collapse(true);
26460     
26461         nodeRange.collapse(true);
26462      
26463         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
26464         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26465          
26466         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26467         
26468         var nodeIsBefore   =  ss == 1;
26469         var nodeIsAfter    = ee == -1;
26470         
26471         if (nodeIsBefore && nodeIsAfter) {
26472             return 0; // outer
26473         }
26474         if (!nodeIsBefore && nodeIsAfter) {
26475             return 1; //right trailed.
26476         }
26477         
26478         if (nodeIsBefore && !nodeIsAfter) {
26479             return 2;  // left trailed.
26480         }
26481         // fully contined.
26482         return 3;
26483     },
26484
26485     // private? - in a new class?
26486     cleanUpPaste :  function()
26487     {
26488         // cleans up the whole document..
26489         Roo.log('cleanuppaste');
26490         
26491         this.cleanUpChildren(this.doc.body);
26492         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26493         if (clean != this.doc.body.innerHTML) {
26494             this.doc.body.innerHTML = clean;
26495         }
26496         
26497     },
26498     
26499     cleanWordChars : function(input) {// change the chars to hex code
26500         var he = Roo.HtmlEditorCore;
26501         
26502         var output = input;
26503         Roo.each(he.swapCodes, function(sw) { 
26504             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26505             
26506             output = output.replace(swapper, sw[1]);
26507         });
26508         
26509         return output;
26510     },
26511     
26512     
26513     cleanUpChildren : function (n)
26514     {
26515         if (!n.childNodes.length) {
26516             return;
26517         }
26518         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26519            this.cleanUpChild(n.childNodes[i]);
26520         }
26521     },
26522     
26523     
26524         
26525     
26526     cleanUpChild : function (node)
26527     {
26528         var ed = this;
26529         //console.log(node);
26530         if (node.nodeName == "#text") {
26531             // clean up silly Windows -- stuff?
26532             return; 
26533         }
26534         if (node.nodeName == "#comment") {
26535             if (!this.allowComments) {
26536                 node.parentNode.removeChild(node);
26537             }
26538             // clean up silly Windows -- stuff?
26539             return; 
26540         }
26541         var lcname = node.tagName.toLowerCase();
26542         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26543         // whitelist of tags..
26544         
26545         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26546             // remove node.
26547             node.parentNode.removeChild(node);
26548             return;
26549             
26550         }
26551         
26552         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26553         
26554         // spans with no attributes - just remove them..
26555         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26556             remove_keep_children = true;
26557         }
26558         
26559         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26560         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26561         
26562         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26563         //    remove_keep_children = true;
26564         //}
26565         
26566         if (remove_keep_children) {
26567             this.cleanUpChildren(node);
26568             // inserts everything just before this node...
26569             while (node.childNodes.length) {
26570                 var cn = node.childNodes[0];
26571                 node.removeChild(cn);
26572                 node.parentNode.insertBefore(cn, node);
26573             }
26574             node.parentNode.removeChild(node);
26575             return;
26576         }
26577         
26578         if (!node.attributes || !node.attributes.length) {
26579             
26580           
26581             
26582             
26583             this.cleanUpChildren(node);
26584             return;
26585         }
26586         
26587         function cleanAttr(n,v)
26588         {
26589             
26590             if (v.match(/^\./) || v.match(/^\//)) {
26591                 return;
26592             }
26593             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26594                 return;
26595             }
26596             if (v.match(/^#/)) {
26597                 return;
26598             }
26599             if (v.match(/^\{/)) { // allow template editing.
26600                 return;
26601             }
26602 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26603             node.removeAttribute(n);
26604             
26605         }
26606         
26607         var cwhite = this.cwhite;
26608         var cblack = this.cblack;
26609             
26610         function cleanStyle(n,v)
26611         {
26612             if (v.match(/expression/)) { //XSS?? should we even bother..
26613                 node.removeAttribute(n);
26614                 return;
26615             }
26616             
26617             var parts = v.split(/;/);
26618             var clean = [];
26619             
26620             Roo.each(parts, function(p) {
26621                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26622                 if (!p.length) {
26623                     return true;
26624                 }
26625                 var l = p.split(':').shift().replace(/\s+/g,'');
26626                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26627                 
26628                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26629 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26630                     //node.removeAttribute(n);
26631                     return true;
26632                 }
26633                 //Roo.log()
26634                 // only allow 'c whitelisted system attributes'
26635                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26636 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26637                     //node.removeAttribute(n);
26638                     return true;
26639                 }
26640                 
26641                 
26642                  
26643                 
26644                 clean.push(p);
26645                 return true;
26646             });
26647             if (clean.length) { 
26648                 node.setAttribute(n, clean.join(';'));
26649             } else {
26650                 node.removeAttribute(n);
26651             }
26652             
26653         }
26654         
26655         
26656         for (var i = node.attributes.length-1; i > -1 ; i--) {
26657             var a = node.attributes[i];
26658             //console.log(a);
26659             
26660             if (a.name.toLowerCase().substr(0,2)=='on')  {
26661                 node.removeAttribute(a.name);
26662                 continue;
26663             }
26664             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26665                 node.removeAttribute(a.name);
26666                 continue;
26667             }
26668             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26669                 cleanAttr(a.name,a.value); // fixme..
26670                 continue;
26671             }
26672             if (a.name == 'style') {
26673                 cleanStyle(a.name,a.value);
26674                 continue;
26675             }
26676             /// clean up MS crap..
26677             // tecnically this should be a list of valid class'es..
26678             
26679             
26680             if (a.name == 'class') {
26681                 if (a.value.match(/^Mso/)) {
26682                     node.removeAttribute('class');
26683                 }
26684                 
26685                 if (a.value.match(/^body$/)) {
26686                     node.removeAttribute('class');
26687                 }
26688                 continue;
26689             }
26690             
26691             // style cleanup!?
26692             // class cleanup?
26693             
26694         }
26695         
26696         
26697         this.cleanUpChildren(node);
26698         
26699         
26700     },
26701     
26702     /**
26703      * Clean up MS wordisms...
26704      */
26705     cleanWord : function(node)
26706     {
26707         if (!node) {
26708             this.cleanWord(this.doc.body);
26709             return;
26710         }
26711         
26712         if(
26713                 node.nodeName == 'SPAN' &&
26714                 !node.hasAttributes() &&
26715                 node.childNodes.length == 1 &&
26716                 node.firstChild.nodeName == "#text"  
26717         ) {
26718             var textNode = node.firstChild;
26719             node.removeChild(textNode);
26720             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26721                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26722             }
26723             node.parentNode.insertBefore(textNode, node);
26724             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26725                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26726             }
26727             node.parentNode.removeChild(node);
26728         }
26729         
26730         if (node.nodeName == "#text") {
26731             // clean up silly Windows -- stuff?
26732             return; 
26733         }
26734         if (node.nodeName == "#comment") {
26735             node.parentNode.removeChild(node);
26736             // clean up silly Windows -- stuff?
26737             return; 
26738         }
26739         
26740         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26741             node.parentNode.removeChild(node);
26742             return;
26743         }
26744         //Roo.log(node.tagName);
26745         // remove - but keep children..
26746         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26747             //Roo.log('-- removed');
26748             while (node.childNodes.length) {
26749                 var cn = node.childNodes[0];
26750                 node.removeChild(cn);
26751                 node.parentNode.insertBefore(cn, node);
26752                 // move node to parent - and clean it..
26753                 this.cleanWord(cn);
26754             }
26755             node.parentNode.removeChild(node);
26756             /// no need to iterate chidlren = it's got none..
26757             //this.iterateChildren(node, this.cleanWord);
26758             return;
26759         }
26760         // clean styles
26761         if (node.className.length) {
26762             
26763             var cn = node.className.split(/\W+/);
26764             var cna = [];
26765             Roo.each(cn, function(cls) {
26766                 if (cls.match(/Mso[a-zA-Z]+/)) {
26767                     return;
26768                 }
26769                 cna.push(cls);
26770             });
26771             node.className = cna.length ? cna.join(' ') : '';
26772             if (!cna.length) {
26773                 node.removeAttribute("class");
26774             }
26775         }
26776         
26777         if (node.hasAttribute("lang")) {
26778             node.removeAttribute("lang");
26779         }
26780         
26781         if (node.hasAttribute("style")) {
26782             
26783             var styles = node.getAttribute("style").split(";");
26784             var nstyle = [];
26785             Roo.each(styles, function(s) {
26786                 if (!s.match(/:/)) {
26787                     return;
26788                 }
26789                 var kv = s.split(":");
26790                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26791                     return;
26792                 }
26793                 // what ever is left... we allow.
26794                 nstyle.push(s);
26795             });
26796             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26797             if (!nstyle.length) {
26798                 node.removeAttribute('style');
26799             }
26800         }
26801         this.iterateChildren(node, this.cleanWord);
26802         
26803         
26804         
26805     },
26806     /**
26807      * iterateChildren of a Node, calling fn each time, using this as the scole..
26808      * @param {DomNode} node node to iterate children of.
26809      * @param {Function} fn method of this class to call on each item.
26810      */
26811     iterateChildren : function(node, fn)
26812     {
26813         if (!node.childNodes.length) {
26814                 return;
26815         }
26816         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26817            fn.call(this, node.childNodes[i])
26818         }
26819     },
26820     
26821     
26822     /**
26823      * cleanTableWidths.
26824      *
26825      * Quite often pasting from word etc.. results in tables with column and widths.
26826      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26827      *
26828      */
26829     cleanTableWidths : function(node)
26830     {
26831          
26832          
26833         if (!node) {
26834             this.cleanTableWidths(this.doc.body);
26835             return;
26836         }
26837         
26838         // ignore list...
26839         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26840             return; 
26841         }
26842         Roo.log(node.tagName);
26843         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26844             this.iterateChildren(node, this.cleanTableWidths);
26845             return;
26846         }
26847         if (node.hasAttribute('width')) {
26848             node.removeAttribute('width');
26849         }
26850         
26851          
26852         if (node.hasAttribute("style")) {
26853             // pretty basic...
26854             
26855             var styles = node.getAttribute("style").split(";");
26856             var nstyle = [];
26857             Roo.each(styles, function(s) {
26858                 if (!s.match(/:/)) {
26859                     return;
26860                 }
26861                 var kv = s.split(":");
26862                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26863                     return;
26864                 }
26865                 // what ever is left... we allow.
26866                 nstyle.push(s);
26867             });
26868             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26869             if (!nstyle.length) {
26870                 node.removeAttribute('style');
26871             }
26872         }
26873         
26874         this.iterateChildren(node, this.cleanTableWidths);
26875         
26876         
26877     },
26878     
26879     
26880     
26881     
26882     domToHTML : function(currentElement, depth, nopadtext) {
26883         
26884         depth = depth || 0;
26885         nopadtext = nopadtext || false;
26886     
26887         if (!currentElement) {
26888             return this.domToHTML(this.doc.body);
26889         }
26890         
26891         //Roo.log(currentElement);
26892         var j;
26893         var allText = false;
26894         var nodeName = currentElement.nodeName;
26895         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26896         
26897         if  (nodeName == '#text') {
26898             
26899             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26900         }
26901         
26902         
26903         var ret = '';
26904         if (nodeName != 'BODY') {
26905              
26906             var i = 0;
26907             // Prints the node tagName, such as <A>, <IMG>, etc
26908             if (tagName) {
26909                 var attr = [];
26910                 for(i = 0; i < currentElement.attributes.length;i++) {
26911                     // quoting?
26912                     var aname = currentElement.attributes.item(i).name;
26913                     if (!currentElement.attributes.item(i).value.length) {
26914                         continue;
26915                     }
26916                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26917                 }
26918                 
26919                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26920             } 
26921             else {
26922                 
26923                 // eack
26924             }
26925         } else {
26926             tagName = false;
26927         }
26928         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26929             return ret;
26930         }
26931         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26932             nopadtext = true;
26933         }
26934         
26935         
26936         // Traverse the tree
26937         i = 0;
26938         var currentElementChild = currentElement.childNodes.item(i);
26939         var allText = true;
26940         var innerHTML  = '';
26941         lastnode = '';
26942         while (currentElementChild) {
26943             // Formatting code (indent the tree so it looks nice on the screen)
26944             var nopad = nopadtext;
26945             if (lastnode == 'SPAN') {
26946                 nopad  = true;
26947             }
26948             // text
26949             if  (currentElementChild.nodeName == '#text') {
26950                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26951                 toadd = nopadtext ? toadd : toadd.trim();
26952                 if (!nopad && toadd.length > 80) {
26953                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26954                 }
26955                 innerHTML  += toadd;
26956                 
26957                 i++;
26958                 currentElementChild = currentElement.childNodes.item(i);
26959                 lastNode = '';
26960                 continue;
26961             }
26962             allText = false;
26963             
26964             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26965                 
26966             // Recursively traverse the tree structure of the child node
26967             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26968             lastnode = currentElementChild.nodeName;
26969             i++;
26970             currentElementChild=currentElement.childNodes.item(i);
26971         }
26972         
26973         ret += innerHTML;
26974         
26975         if (!allText) {
26976                 // The remaining code is mostly for formatting the tree
26977             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26978         }
26979         
26980         
26981         if (tagName) {
26982             ret+= "</"+tagName+">";
26983         }
26984         return ret;
26985         
26986     },
26987         
26988     applyBlacklists : function()
26989     {
26990         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26991         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26992         
26993         this.white = [];
26994         this.black = [];
26995         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26996             if (b.indexOf(tag) > -1) {
26997                 return;
26998             }
26999             this.white.push(tag);
27000             
27001         }, this);
27002         
27003         Roo.each(w, function(tag) {
27004             if (b.indexOf(tag) > -1) {
27005                 return;
27006             }
27007             if (this.white.indexOf(tag) > -1) {
27008                 return;
27009             }
27010             this.white.push(tag);
27011             
27012         }, this);
27013         
27014         
27015         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
27016             if (w.indexOf(tag) > -1) {
27017                 return;
27018             }
27019             this.black.push(tag);
27020             
27021         }, this);
27022         
27023         Roo.each(b, function(tag) {
27024             if (w.indexOf(tag) > -1) {
27025                 return;
27026             }
27027             if (this.black.indexOf(tag) > -1) {
27028                 return;
27029             }
27030             this.black.push(tag);
27031             
27032         }, this);
27033         
27034         
27035         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
27036         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
27037         
27038         this.cwhite = [];
27039         this.cblack = [];
27040         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
27041             if (b.indexOf(tag) > -1) {
27042                 return;
27043             }
27044             this.cwhite.push(tag);
27045             
27046         }, this);
27047         
27048         Roo.each(w, function(tag) {
27049             if (b.indexOf(tag) > -1) {
27050                 return;
27051             }
27052             if (this.cwhite.indexOf(tag) > -1) {
27053                 return;
27054             }
27055             this.cwhite.push(tag);
27056             
27057         }, this);
27058         
27059         
27060         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
27061             if (w.indexOf(tag) > -1) {
27062                 return;
27063             }
27064             this.cblack.push(tag);
27065             
27066         }, this);
27067         
27068         Roo.each(b, function(tag) {
27069             if (w.indexOf(tag) > -1) {
27070                 return;
27071             }
27072             if (this.cblack.indexOf(tag) > -1) {
27073                 return;
27074             }
27075             this.cblack.push(tag);
27076             
27077         }, this);
27078     },
27079     
27080     setStylesheets : function(stylesheets)
27081     {
27082         if(typeof(stylesheets) == 'string'){
27083             Roo.get(this.iframe.contentDocument.head).createChild({
27084                 tag : 'link',
27085                 rel : 'stylesheet',
27086                 type : 'text/css',
27087                 href : stylesheets
27088             });
27089             
27090             return;
27091         }
27092         var _this = this;
27093      
27094         Roo.each(stylesheets, function(s) {
27095             if(!s.length){
27096                 return;
27097             }
27098             
27099             Roo.get(_this.iframe.contentDocument.head).createChild({
27100                 tag : 'link',
27101                 rel : 'stylesheet',
27102                 type : 'text/css',
27103                 href : s
27104             });
27105         });
27106
27107         
27108     },
27109     
27110     removeStylesheets : function()
27111     {
27112         var _this = this;
27113         
27114         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
27115             s.remove();
27116         });
27117     },
27118     
27119     setStyle : function(style)
27120     {
27121         Roo.get(this.iframe.contentDocument.head).createChild({
27122             tag : 'style',
27123             type : 'text/css',
27124             html : style
27125         });
27126
27127         return;
27128     }
27129     
27130     // hide stuff that is not compatible
27131     /**
27132      * @event blur
27133      * @hide
27134      */
27135     /**
27136      * @event change
27137      * @hide
27138      */
27139     /**
27140      * @event focus
27141      * @hide
27142      */
27143     /**
27144      * @event specialkey
27145      * @hide
27146      */
27147     /**
27148      * @cfg {String} fieldClass @hide
27149      */
27150     /**
27151      * @cfg {String} focusClass @hide
27152      */
27153     /**
27154      * @cfg {String} autoCreate @hide
27155      */
27156     /**
27157      * @cfg {String} inputType @hide
27158      */
27159     /**
27160      * @cfg {String} invalidClass @hide
27161      */
27162     /**
27163      * @cfg {String} invalidText @hide
27164      */
27165     /**
27166      * @cfg {String} msgFx @hide
27167      */
27168     /**
27169      * @cfg {String} validateOnBlur @hide
27170      */
27171 });
27172
27173 Roo.HtmlEditorCore.white = [
27174         'area', 'br', 'img', 'input', 'hr', 'wbr',
27175         
27176        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
27177        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
27178        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
27179        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
27180        'table',   'ul',         'xmp', 
27181        
27182        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
27183       'thead',   'tr', 
27184      
27185       'dir', 'menu', 'ol', 'ul', 'dl',
27186        
27187       'embed',  'object'
27188 ];
27189
27190
27191 Roo.HtmlEditorCore.black = [
27192     //    'embed',  'object', // enable - backend responsiblity to clean thiese
27193         'applet', // 
27194         'base',   'basefont', 'bgsound', 'blink',  'body', 
27195         'frame',  'frameset', 'head',    'html',   'ilayer', 
27196         'iframe', 'layer',  'link',     'meta',    'object',   
27197         'script', 'style' ,'title',  'xml' // clean later..
27198 ];
27199 Roo.HtmlEditorCore.clean = [
27200     'script', 'style', 'title', 'xml'
27201 ];
27202 Roo.HtmlEditorCore.remove = [
27203     'font'
27204 ];
27205 // attributes..
27206
27207 Roo.HtmlEditorCore.ablack = [
27208     'on'
27209 ];
27210     
27211 Roo.HtmlEditorCore.aclean = [ 
27212     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
27213 ];
27214
27215 // protocols..
27216 Roo.HtmlEditorCore.pwhite= [
27217         'http',  'https',  'mailto'
27218 ];
27219
27220 // white listed style attributes.
27221 Roo.HtmlEditorCore.cwhite= [
27222       //  'text-align', /// default is to allow most things..
27223       
27224          
27225 //        'font-size'//??
27226 ];
27227
27228 // black listed style attributes.
27229 Roo.HtmlEditorCore.cblack= [
27230       //  'font-size' -- this can be set by the project 
27231 ];
27232
27233
27234 Roo.HtmlEditorCore.swapCodes   =[ 
27235     [    8211, "&#8211;" ], 
27236     [    8212, "&#8212;" ], 
27237     [    8216,  "'" ],  
27238     [    8217, "'" ],  
27239     [    8220, '"' ],  
27240     [    8221, '"' ],  
27241     [    8226, "*" ],  
27242     [    8230, "..." ]
27243 ]; 
27244
27245     /*
27246  * - LGPL
27247  *
27248  * HtmlEditor
27249  * 
27250  */
27251
27252 /**
27253  * @class Roo.bootstrap.HtmlEditor
27254  * @extends Roo.bootstrap.TextArea
27255  * Bootstrap HtmlEditor class
27256
27257  * @constructor
27258  * Create a new HtmlEditor
27259  * @param {Object} config The config object
27260  */
27261
27262 Roo.bootstrap.HtmlEditor = function(config){
27263     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
27264     if (!this.toolbars) {
27265         this.toolbars = [];
27266     }
27267     
27268     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
27269     this.addEvents({
27270             /**
27271              * @event initialize
27272              * Fires when the editor is fully initialized (including the iframe)
27273              * @param {HtmlEditor} this
27274              */
27275             initialize: true,
27276             /**
27277              * @event activate
27278              * Fires when the editor is first receives the focus. Any insertion must wait
27279              * until after this event.
27280              * @param {HtmlEditor} this
27281              */
27282             activate: true,
27283              /**
27284              * @event beforesync
27285              * Fires before the textarea is updated with content from the editor iframe. Return false
27286              * to cancel the sync.
27287              * @param {HtmlEditor} this
27288              * @param {String} html
27289              */
27290             beforesync: true,
27291              /**
27292              * @event beforepush
27293              * Fires before the iframe editor is updated with content from the textarea. Return false
27294              * to cancel the push.
27295              * @param {HtmlEditor} this
27296              * @param {String} html
27297              */
27298             beforepush: true,
27299              /**
27300              * @event sync
27301              * Fires when the textarea is updated with content from the editor iframe.
27302              * @param {HtmlEditor} this
27303              * @param {String} html
27304              */
27305             sync: true,
27306              /**
27307              * @event push
27308              * Fires when the iframe editor is updated with content from the textarea.
27309              * @param {HtmlEditor} this
27310              * @param {String} html
27311              */
27312             push: true,
27313              /**
27314              * @event editmodechange
27315              * Fires when the editor switches edit modes
27316              * @param {HtmlEditor} this
27317              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
27318              */
27319             editmodechange: true,
27320             /**
27321              * @event editorevent
27322              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
27323              * @param {HtmlEditor} this
27324              */
27325             editorevent: true,
27326             /**
27327              * @event firstfocus
27328              * Fires when on first focus - needed by toolbars..
27329              * @param {HtmlEditor} this
27330              */
27331             firstfocus: true,
27332             /**
27333              * @event autosave
27334              * Auto save the htmlEditor value as a file into Events
27335              * @param {HtmlEditor} this
27336              */
27337             autosave: true,
27338             /**
27339              * @event savedpreview
27340              * preview the saved version of htmlEditor
27341              * @param {HtmlEditor} this
27342              */
27343             savedpreview: true
27344         });
27345 };
27346
27347
27348 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
27349     
27350     
27351       /**
27352      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
27353      */
27354     toolbars : false,
27355     
27356      /**
27357     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
27358     */
27359     btns : [],
27360    
27361      /**
27362      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
27363      *                        Roo.resizable.
27364      */
27365     resizable : false,
27366      /**
27367      * @cfg {Number} height (in pixels)
27368      */   
27369     height: 300,
27370    /**
27371      * @cfg {Number} width (in pixels)
27372      */   
27373     width: false,
27374     
27375     /**
27376      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
27377      * 
27378      */
27379     stylesheets: false,
27380     
27381     // id of frame..
27382     frameId: false,
27383     
27384     // private properties
27385     validationEvent : false,
27386     deferHeight: true,
27387     initialized : false,
27388     activated : false,
27389     
27390     onFocus : Roo.emptyFn,
27391     iframePad:3,
27392     hideMode:'offsets',
27393     
27394     tbContainer : false,
27395     
27396     bodyCls : '',
27397     
27398     toolbarContainer :function() {
27399         return this.wrap.select('.x-html-editor-tb',true).first();
27400     },
27401
27402     /**
27403      * Protected method that will not generally be called directly. It
27404      * is called when the editor creates its toolbar. Override this method if you need to
27405      * add custom toolbar buttons.
27406      * @param {HtmlEditor} editor
27407      */
27408     createToolbar : function(){
27409         Roo.log('renewing');
27410         Roo.log("create toolbars");
27411         
27412         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
27413         this.toolbars[0].render(this.toolbarContainer());
27414         
27415         return;
27416         
27417 //        if (!editor.toolbars || !editor.toolbars.length) {
27418 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
27419 //        }
27420 //        
27421 //        for (var i =0 ; i < editor.toolbars.length;i++) {
27422 //            editor.toolbars[i] = Roo.factory(
27423 //                    typeof(editor.toolbars[i]) == 'string' ?
27424 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
27425 //                Roo.bootstrap.HtmlEditor);
27426 //            editor.toolbars[i].init(editor);
27427 //        }
27428     },
27429
27430      
27431     // private
27432     onRender : function(ct, position)
27433     {
27434        // Roo.log("Call onRender: " + this.xtype);
27435         var _t = this;
27436         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
27437       
27438         this.wrap = this.inputEl().wrap({
27439             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
27440         });
27441         
27442         this.editorcore.onRender(ct, position);
27443          
27444         if (this.resizable) {
27445             this.resizeEl = new Roo.Resizable(this.wrap, {
27446                 pinned : true,
27447                 wrap: true,
27448                 dynamic : true,
27449                 minHeight : this.height,
27450                 height: this.height,
27451                 handles : this.resizable,
27452                 width: this.width,
27453                 listeners : {
27454                     resize : function(r, w, h) {
27455                         _t.onResize(w,h); // -something
27456                     }
27457                 }
27458             });
27459             
27460         }
27461         this.createToolbar(this);
27462        
27463         
27464         if(!this.width && this.resizable){
27465             this.setSize(this.wrap.getSize());
27466         }
27467         if (this.resizeEl) {
27468             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27469             // should trigger onReize..
27470         }
27471         
27472     },
27473
27474     // private
27475     onResize : function(w, h)
27476     {
27477         Roo.log('resize: ' +w + ',' + h );
27478         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27479         var ew = false;
27480         var eh = false;
27481         
27482         if(this.inputEl() ){
27483             if(typeof w == 'number'){
27484                 var aw = w - this.wrap.getFrameWidth('lr');
27485                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27486                 ew = aw;
27487             }
27488             if(typeof h == 'number'){
27489                  var tbh = -11;  // fixme it needs to tool bar size!
27490                 for (var i =0; i < this.toolbars.length;i++) {
27491                     // fixme - ask toolbars for heights?
27492                     tbh += this.toolbars[i].el.getHeight();
27493                     //if (this.toolbars[i].footer) {
27494                     //    tbh += this.toolbars[i].footer.el.getHeight();
27495                     //}
27496                 }
27497               
27498                 
27499                 
27500                 
27501                 
27502                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27503                 ah -= 5; // knock a few pixes off for look..
27504                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27505                 var eh = ah;
27506             }
27507         }
27508         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27509         this.editorcore.onResize(ew,eh);
27510         
27511     },
27512
27513     /**
27514      * Toggles the editor between standard and source edit mode.
27515      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27516      */
27517     toggleSourceEdit : function(sourceEditMode)
27518     {
27519         this.editorcore.toggleSourceEdit(sourceEditMode);
27520         
27521         if(this.editorcore.sourceEditMode){
27522             Roo.log('editor - showing textarea');
27523             
27524 //            Roo.log('in');
27525 //            Roo.log(this.syncValue());
27526             this.syncValue();
27527             this.inputEl().removeClass(['hide', 'x-hidden']);
27528             this.inputEl().dom.removeAttribute('tabIndex');
27529             this.inputEl().focus();
27530         }else{
27531             Roo.log('editor - hiding textarea');
27532 //            Roo.log('out')
27533 //            Roo.log(this.pushValue()); 
27534             this.pushValue();
27535             
27536             this.inputEl().addClass(['hide', 'x-hidden']);
27537             this.inputEl().dom.setAttribute('tabIndex', -1);
27538             //this.deferFocus();
27539         }
27540          
27541         if(this.resizable){
27542             this.setSize(this.wrap.getSize());
27543         }
27544         
27545         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27546     },
27547  
27548     // private (for BoxComponent)
27549     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27550
27551     // private (for BoxComponent)
27552     getResizeEl : function(){
27553         return this.wrap;
27554     },
27555
27556     // private (for BoxComponent)
27557     getPositionEl : function(){
27558         return this.wrap;
27559     },
27560
27561     // private
27562     initEvents : function(){
27563         this.originalValue = this.getValue();
27564     },
27565
27566 //    /**
27567 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27568 //     * @method
27569 //     */
27570 //    markInvalid : Roo.emptyFn,
27571 //    /**
27572 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27573 //     * @method
27574 //     */
27575 //    clearInvalid : Roo.emptyFn,
27576
27577     setValue : function(v){
27578         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27579         this.editorcore.pushValue();
27580     },
27581
27582      
27583     // private
27584     deferFocus : function(){
27585         this.focus.defer(10, this);
27586     },
27587
27588     // doc'ed in Field
27589     focus : function(){
27590         this.editorcore.focus();
27591         
27592     },
27593       
27594
27595     // private
27596     onDestroy : function(){
27597         
27598         
27599         
27600         if(this.rendered){
27601             
27602             for (var i =0; i < this.toolbars.length;i++) {
27603                 // fixme - ask toolbars for heights?
27604                 this.toolbars[i].onDestroy();
27605             }
27606             
27607             this.wrap.dom.innerHTML = '';
27608             this.wrap.remove();
27609         }
27610     },
27611
27612     // private
27613     onFirstFocus : function(){
27614         //Roo.log("onFirstFocus");
27615         this.editorcore.onFirstFocus();
27616          for (var i =0; i < this.toolbars.length;i++) {
27617             this.toolbars[i].onFirstFocus();
27618         }
27619         
27620     },
27621     
27622     // private
27623     syncValue : function()
27624     {   
27625         this.editorcore.syncValue();
27626     },
27627     
27628     pushValue : function()
27629     {   
27630         this.editorcore.pushValue();
27631     }
27632      
27633     
27634     // hide stuff that is not compatible
27635     /**
27636      * @event blur
27637      * @hide
27638      */
27639     /**
27640      * @event change
27641      * @hide
27642      */
27643     /**
27644      * @event focus
27645      * @hide
27646      */
27647     /**
27648      * @event specialkey
27649      * @hide
27650      */
27651     /**
27652      * @cfg {String} fieldClass @hide
27653      */
27654     /**
27655      * @cfg {String} focusClass @hide
27656      */
27657     /**
27658      * @cfg {String} autoCreate @hide
27659      */
27660     /**
27661      * @cfg {String} inputType @hide
27662      */
27663      
27664     /**
27665      * @cfg {String} invalidText @hide
27666      */
27667     /**
27668      * @cfg {String} msgFx @hide
27669      */
27670     /**
27671      * @cfg {String} validateOnBlur @hide
27672      */
27673 });
27674  
27675     
27676    
27677    
27678    
27679       
27680 Roo.namespace('Roo.bootstrap.htmleditor');
27681 /**
27682  * @class Roo.bootstrap.HtmlEditorToolbar1
27683  * Basic Toolbar
27684  * 
27685  * @example
27686  * Usage:
27687  *
27688  new Roo.bootstrap.HtmlEditor({
27689     ....
27690     toolbars : [
27691         new Roo.bootstrap.HtmlEditorToolbar1({
27692             disable : { fonts: 1 , format: 1, ..., ... , ...],
27693             btns : [ .... ]
27694         })
27695     }
27696      
27697  * 
27698  * @cfg {Object} disable List of elements to disable..
27699  * @cfg {Array} btns List of additional buttons.
27700  * 
27701  * 
27702  * NEEDS Extra CSS? 
27703  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27704  */
27705  
27706 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27707 {
27708     
27709     Roo.apply(this, config);
27710     
27711     // default disabled, based on 'good practice'..
27712     this.disable = this.disable || {};
27713     Roo.applyIf(this.disable, {
27714         fontSize : true,
27715         colors : true,
27716         specialElements : true
27717     });
27718     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27719     
27720     this.editor = config.editor;
27721     this.editorcore = config.editor.editorcore;
27722     
27723     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27724     
27725     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27726     // dont call parent... till later.
27727 }
27728 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27729      
27730     bar : true,
27731     
27732     editor : false,
27733     editorcore : false,
27734     
27735     
27736     formats : [
27737         "p" ,  
27738         "h1","h2","h3","h4","h5","h6", 
27739         "pre", "code", 
27740         "abbr", "acronym", "address", "cite", "samp", "var",
27741         'div','span'
27742     ],
27743     
27744     onRender : function(ct, position)
27745     {
27746        // Roo.log("Call onRender: " + this.xtype);
27747         
27748        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27749        Roo.log(this.el);
27750        this.el.dom.style.marginBottom = '0';
27751        var _this = this;
27752        var editorcore = this.editorcore;
27753        var editor= this.editor;
27754        
27755        var children = [];
27756        var btn = function(id,cmd , toggle, handler, html){
27757        
27758             var  event = toggle ? 'toggle' : 'click';
27759        
27760             var a = {
27761                 size : 'sm',
27762                 xtype: 'Button',
27763                 xns: Roo.bootstrap,
27764                 //glyphicon : id,
27765                 fa: id,
27766                 cmd : id || cmd,
27767                 enableToggle:toggle !== false,
27768                 html : html || '',
27769                 pressed : toggle ? false : null,
27770                 listeners : {}
27771             };
27772             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27773                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27774             };
27775             children.push(a);
27776             return a;
27777        }
27778        
27779     //    var cb_box = function...
27780         
27781         var style = {
27782                 xtype: 'Button',
27783                 size : 'sm',
27784                 xns: Roo.bootstrap,
27785                 fa : 'font',
27786                 //html : 'submit'
27787                 menu : {
27788                     xtype: 'Menu',
27789                     xns: Roo.bootstrap,
27790                     items:  []
27791                 }
27792         };
27793         Roo.each(this.formats, function(f) {
27794             style.menu.items.push({
27795                 xtype :'MenuItem',
27796                 xns: Roo.bootstrap,
27797                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27798                 tagname : f,
27799                 listeners : {
27800                     click : function()
27801                     {
27802                         editorcore.insertTag(this.tagname);
27803                         editor.focus();
27804                     }
27805                 }
27806                 
27807             });
27808         });
27809         children.push(style);   
27810         
27811         btn('bold',false,true);
27812         btn('italic',false,true);
27813         btn('align-left', 'justifyleft',true);
27814         btn('align-center', 'justifycenter',true);
27815         btn('align-right' , 'justifyright',true);
27816         btn('link', false, false, function(btn) {
27817             //Roo.log("create link?");
27818             var url = prompt(this.createLinkText, this.defaultLinkValue);
27819             if(url && url != 'http:/'+'/'){
27820                 this.editorcore.relayCmd('createlink', url);
27821             }
27822         }),
27823         btn('list','insertunorderedlist',true);
27824         btn('pencil', false,true, function(btn){
27825                 Roo.log(this);
27826                 this.toggleSourceEdit(btn.pressed);
27827         });
27828         
27829         if (this.editor.btns.length > 0) {
27830             for (var i = 0; i<this.editor.btns.length; i++) {
27831                 children.push(this.editor.btns[i]);
27832             }
27833         }
27834         
27835         /*
27836         var cog = {
27837                 xtype: 'Button',
27838                 size : 'sm',
27839                 xns: Roo.bootstrap,
27840                 glyphicon : 'cog',
27841                 //html : 'submit'
27842                 menu : {
27843                     xtype: 'Menu',
27844                     xns: Roo.bootstrap,
27845                     items:  []
27846                 }
27847         };
27848         
27849         cog.menu.items.push({
27850             xtype :'MenuItem',
27851             xns: Roo.bootstrap,
27852             html : Clean styles,
27853             tagname : f,
27854             listeners : {
27855                 click : function()
27856                 {
27857                     editorcore.insertTag(this.tagname);
27858                     editor.focus();
27859                 }
27860             }
27861             
27862         });
27863        */
27864         
27865          
27866        this.xtype = 'NavSimplebar';
27867         
27868         for(var i=0;i< children.length;i++) {
27869             
27870             this.buttons.add(this.addxtypeChild(children[i]));
27871             
27872         }
27873         
27874         editor.on('editorevent', this.updateToolbar, this);
27875     },
27876     onBtnClick : function(id)
27877     {
27878        this.editorcore.relayCmd(id);
27879        this.editorcore.focus();
27880     },
27881     
27882     /**
27883      * Protected method that will not generally be called directly. It triggers
27884      * a toolbar update by reading the markup state of the current selection in the editor.
27885      */
27886     updateToolbar: function(){
27887
27888         if(!this.editorcore.activated){
27889             this.editor.onFirstFocus(); // is this neeed?
27890             return;
27891         }
27892
27893         var btns = this.buttons; 
27894         var doc = this.editorcore.doc;
27895         btns.get('bold').setActive(doc.queryCommandState('bold'));
27896         btns.get('italic').setActive(doc.queryCommandState('italic'));
27897         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27898         
27899         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27900         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27901         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27902         
27903         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27904         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27905          /*
27906         
27907         var ans = this.editorcore.getAllAncestors();
27908         if (this.formatCombo) {
27909             
27910             
27911             var store = this.formatCombo.store;
27912             this.formatCombo.setValue("");
27913             for (var i =0; i < ans.length;i++) {
27914                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27915                     // select it..
27916                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27917                     break;
27918                 }
27919             }
27920         }
27921         
27922         
27923         
27924         // hides menus... - so this cant be on a menu...
27925         Roo.bootstrap.MenuMgr.hideAll();
27926         */
27927         Roo.bootstrap.MenuMgr.hideAll();
27928         //this.editorsyncValue();
27929     },
27930     onFirstFocus: function() {
27931         this.buttons.each(function(item){
27932            item.enable();
27933         });
27934     },
27935     toggleSourceEdit : function(sourceEditMode){
27936         
27937           
27938         if(sourceEditMode){
27939             Roo.log("disabling buttons");
27940            this.buttons.each( function(item){
27941                 if(item.cmd != 'pencil'){
27942                     item.disable();
27943                 }
27944             });
27945           
27946         }else{
27947             Roo.log("enabling buttons");
27948             if(this.editorcore.initialized){
27949                 this.buttons.each( function(item){
27950                     item.enable();
27951                 });
27952             }
27953             
27954         }
27955         Roo.log("calling toggole on editor");
27956         // tell the editor that it's been pressed..
27957         this.editor.toggleSourceEdit(sourceEditMode);
27958        
27959     }
27960 });
27961
27962
27963
27964
27965  
27966 /*
27967  * - LGPL
27968  */
27969
27970 /**
27971  * @class Roo.bootstrap.Markdown
27972  * @extends Roo.bootstrap.TextArea
27973  * Bootstrap Showdown editable area
27974  * @cfg {string} content
27975  * 
27976  * @constructor
27977  * Create a new Showdown
27978  */
27979
27980 Roo.bootstrap.Markdown = function(config){
27981     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27982    
27983 };
27984
27985 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27986     
27987     editing :false,
27988     
27989     initEvents : function()
27990     {
27991         
27992         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27993         this.markdownEl = this.el.createChild({
27994             cls : 'roo-markdown-area'
27995         });
27996         this.inputEl().addClass('d-none');
27997         if (this.getValue() == '') {
27998             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27999             
28000         } else {
28001             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28002         }
28003         this.markdownEl.on('click', this.toggleTextEdit, this);
28004         this.on('blur', this.toggleTextEdit, this);
28005         this.on('specialkey', this.resizeTextArea, this);
28006     },
28007     
28008     toggleTextEdit : function()
28009     {
28010         var sh = this.markdownEl.getHeight();
28011         this.inputEl().addClass('d-none');
28012         this.markdownEl.addClass('d-none');
28013         if (!this.editing) {
28014             // show editor?
28015             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
28016             this.inputEl().removeClass('d-none');
28017             this.inputEl().focus();
28018             this.editing = true;
28019             return;
28020         }
28021         // show showdown...
28022         this.updateMarkdown();
28023         this.markdownEl.removeClass('d-none');
28024         this.editing = false;
28025         return;
28026     },
28027     updateMarkdown : function()
28028     {
28029         if (this.getValue() == '') {
28030             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
28031             return;
28032         }
28033  
28034         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
28035     },
28036     
28037     resizeTextArea: function () {
28038         
28039         var sh = 100;
28040         Roo.log([sh, this.getValue().split("\n").length * 30]);
28041         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
28042     },
28043     setValue : function(val)
28044     {
28045         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
28046         if (!this.editing) {
28047             this.updateMarkdown();
28048         }
28049         
28050     },
28051     focus : function()
28052     {
28053         if (!this.editing) {
28054             this.toggleTextEdit();
28055         }
28056         
28057     }
28058
28059
28060 });/*
28061  * Based on:
28062  * Ext JS Library 1.1.1
28063  * Copyright(c) 2006-2007, Ext JS, LLC.
28064  *
28065  * Originally Released Under LGPL - original licence link has changed is not relivant.
28066  *
28067  * Fork - LGPL
28068  * <script type="text/javascript">
28069  */
28070  
28071 /**
28072  * @class Roo.bootstrap.PagingToolbar
28073  * @extends Roo.bootstrap.NavSimplebar
28074  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
28075  * @constructor
28076  * Create a new PagingToolbar
28077  * @param {Object} config The config object
28078  * @param {Roo.data.Store} store
28079  */
28080 Roo.bootstrap.PagingToolbar = function(config)
28081 {
28082     // old args format still supported... - xtype is prefered..
28083         // created from xtype...
28084     
28085     this.ds = config.dataSource;
28086     
28087     if (config.store && !this.ds) {
28088         this.store= Roo.factory(config.store, Roo.data);
28089         this.ds = this.store;
28090         this.ds.xmodule = this.xmodule || false;
28091     }
28092     
28093     this.toolbarItems = [];
28094     if (config.items) {
28095         this.toolbarItems = config.items;
28096     }
28097     
28098     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
28099     
28100     this.cursor = 0;
28101     
28102     if (this.ds) { 
28103         this.bind(this.ds);
28104     }
28105     
28106     if (Roo.bootstrap.version == 4) {
28107         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
28108     } else {
28109         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
28110     }
28111     
28112 };
28113
28114 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
28115     /**
28116      * @cfg {Roo.data.Store} dataSource
28117      * The underlying data store providing the paged data
28118      */
28119     /**
28120      * @cfg {String/HTMLElement/Element} container
28121      * container The id or element that will contain the toolbar
28122      */
28123     /**
28124      * @cfg {Boolean} displayInfo
28125      * True to display the displayMsg (defaults to false)
28126      */
28127     /**
28128      * @cfg {Number} pageSize
28129      * The number of records to display per page (defaults to 20)
28130      */
28131     pageSize: 20,
28132     /**
28133      * @cfg {String} displayMsg
28134      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
28135      */
28136     displayMsg : 'Displaying {0} - {1} of {2}',
28137     /**
28138      * @cfg {String} emptyMsg
28139      * The message to display when no records are found (defaults to "No data to display")
28140      */
28141     emptyMsg : 'No data to display',
28142     /**
28143      * Customizable piece of the default paging text (defaults to "Page")
28144      * @type String
28145      */
28146     beforePageText : "Page",
28147     /**
28148      * Customizable piece of the default paging text (defaults to "of %0")
28149      * @type String
28150      */
28151     afterPageText : "of {0}",
28152     /**
28153      * Customizable piece of the default paging text (defaults to "First Page")
28154      * @type String
28155      */
28156     firstText : "First Page",
28157     /**
28158      * Customizable piece of the default paging text (defaults to "Previous Page")
28159      * @type String
28160      */
28161     prevText : "Previous Page",
28162     /**
28163      * Customizable piece of the default paging text (defaults to "Next Page")
28164      * @type String
28165      */
28166     nextText : "Next Page",
28167     /**
28168      * Customizable piece of the default paging text (defaults to "Last Page")
28169      * @type String
28170      */
28171     lastText : "Last Page",
28172     /**
28173      * Customizable piece of the default paging text (defaults to "Refresh")
28174      * @type String
28175      */
28176     refreshText : "Refresh",
28177
28178     buttons : false,
28179     // private
28180     onRender : function(ct, position) 
28181     {
28182         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
28183         this.navgroup.parentId = this.id;
28184         this.navgroup.onRender(this.el, null);
28185         // add the buttons to the navgroup
28186         
28187         if(this.displayInfo){
28188             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
28189             this.displayEl = this.el.select('.x-paging-info', true).first();
28190 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
28191 //            this.displayEl = navel.el.select('span',true).first();
28192         }
28193         
28194         var _this = this;
28195         
28196         if(this.buttons){
28197             Roo.each(_this.buttons, function(e){ // this might need to use render????
28198                Roo.factory(e).render(_this.el);
28199             });
28200         }
28201             
28202         Roo.each(_this.toolbarItems, function(e) {
28203             _this.navgroup.addItem(e);
28204         });
28205         
28206         
28207         this.first = this.navgroup.addItem({
28208             tooltip: this.firstText,
28209             cls: "prev btn-outline-secondary",
28210             html : ' <i class="fa fa-step-backward"></i>',
28211             disabled: true,
28212             preventDefault: true,
28213             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
28214         });
28215         
28216         this.prev =  this.navgroup.addItem({
28217             tooltip: this.prevText,
28218             cls: "prev btn-outline-secondary",
28219             html : ' <i class="fa fa-backward"></i>',
28220             disabled: true,
28221             preventDefault: true,
28222             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
28223         });
28224     //this.addSeparator();
28225         
28226         
28227         var field = this.navgroup.addItem( {
28228             tagtype : 'span',
28229             cls : 'x-paging-position  btn-outline-secondary',
28230              disabled: true,
28231             html : this.beforePageText  +
28232                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
28233                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
28234          } ); //?? escaped?
28235         
28236         this.field = field.el.select('input', true).first();
28237         this.field.on("keydown", this.onPagingKeydown, this);
28238         this.field.on("focus", function(){this.dom.select();});
28239     
28240     
28241         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
28242         //this.field.setHeight(18);
28243         //this.addSeparator();
28244         this.next = this.navgroup.addItem({
28245             tooltip: this.nextText,
28246             cls: "next btn-outline-secondary",
28247             html : ' <i class="fa fa-forward"></i>',
28248             disabled: true,
28249             preventDefault: true,
28250             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
28251         });
28252         this.last = this.navgroup.addItem({
28253             tooltip: this.lastText,
28254             html : ' <i class="fa fa-step-forward"></i>',
28255             cls: "next btn-outline-secondary",
28256             disabled: true,
28257             preventDefault: true,
28258             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
28259         });
28260     //this.addSeparator();
28261         this.loading = this.navgroup.addItem({
28262             tooltip: this.refreshText,
28263             cls: "btn-outline-secondary",
28264             html : ' <i class="fa fa-refresh"></i>',
28265             preventDefault: true,
28266             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
28267         });
28268         
28269     },
28270
28271     // private
28272     updateInfo : function(){
28273         if(this.displayEl){
28274             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
28275             var msg = count == 0 ?
28276                 this.emptyMsg :
28277                 String.format(
28278                     this.displayMsg,
28279                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
28280                 );
28281             this.displayEl.update(msg);
28282         }
28283     },
28284
28285     // private
28286     onLoad : function(ds, r, o)
28287     {
28288         this.cursor = o.params && o.params.start ? o.params.start : 0;
28289         
28290         var d = this.getPageData(),
28291             ap = d.activePage,
28292             ps = d.pages;
28293         
28294         
28295         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
28296         this.field.dom.value = ap;
28297         this.first.setDisabled(ap == 1);
28298         this.prev.setDisabled(ap == 1);
28299         this.next.setDisabled(ap == ps);
28300         this.last.setDisabled(ap == ps);
28301         this.loading.enable();
28302         this.updateInfo();
28303     },
28304
28305     // private
28306     getPageData : function(){
28307         var total = this.ds.getTotalCount();
28308         return {
28309             total : total,
28310             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
28311             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
28312         };
28313     },
28314
28315     // private
28316     onLoadError : function(){
28317         this.loading.enable();
28318     },
28319
28320     // private
28321     onPagingKeydown : function(e){
28322         var k = e.getKey();
28323         var d = this.getPageData();
28324         if(k == e.RETURN){
28325             var v = this.field.dom.value, pageNum;
28326             if(!v || isNaN(pageNum = parseInt(v, 10))){
28327                 this.field.dom.value = d.activePage;
28328                 return;
28329             }
28330             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
28331             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28332             e.stopEvent();
28333         }
28334         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))
28335         {
28336           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
28337           this.field.dom.value = pageNum;
28338           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
28339           e.stopEvent();
28340         }
28341         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
28342         {
28343           var v = this.field.dom.value, pageNum; 
28344           var increment = (e.shiftKey) ? 10 : 1;
28345           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
28346                 increment *= -1;
28347           }
28348           if(!v || isNaN(pageNum = parseInt(v, 10))) {
28349             this.field.dom.value = d.activePage;
28350             return;
28351           }
28352           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
28353           {
28354             this.field.dom.value = parseInt(v, 10) + increment;
28355             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
28356             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
28357           }
28358           e.stopEvent();
28359         }
28360     },
28361
28362     // private
28363     beforeLoad : function(){
28364         if(this.loading){
28365             this.loading.disable();
28366         }
28367     },
28368
28369     // private
28370     onClick : function(which){
28371         
28372         var ds = this.ds;
28373         if (!ds) {
28374             return;
28375         }
28376         
28377         switch(which){
28378             case "first":
28379                 ds.load({params:{start: 0, limit: this.pageSize}});
28380             break;
28381             case "prev":
28382                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
28383             break;
28384             case "next":
28385                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
28386             break;
28387             case "last":
28388                 var total = ds.getTotalCount();
28389                 var extra = total % this.pageSize;
28390                 var lastStart = extra ? (total - extra) : total-this.pageSize;
28391                 ds.load({params:{start: lastStart, limit: this.pageSize}});
28392             break;
28393             case "refresh":
28394                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
28395             break;
28396         }
28397     },
28398
28399     /**
28400      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
28401      * @param {Roo.data.Store} store The data store to unbind
28402      */
28403     unbind : function(ds){
28404         ds.un("beforeload", this.beforeLoad, this);
28405         ds.un("load", this.onLoad, this);
28406         ds.un("loadexception", this.onLoadError, this);
28407         ds.un("remove", this.updateInfo, this);
28408         ds.un("add", this.updateInfo, this);
28409         this.ds = undefined;
28410     },
28411
28412     /**
28413      * Binds the paging toolbar to the specified {@link Roo.data.Store}
28414      * @param {Roo.data.Store} store The data store to bind
28415      */
28416     bind : function(ds){
28417         ds.on("beforeload", this.beforeLoad, this);
28418         ds.on("load", this.onLoad, this);
28419         ds.on("loadexception", this.onLoadError, this);
28420         ds.on("remove", this.updateInfo, this);
28421         ds.on("add", this.updateInfo, this);
28422         this.ds = ds;
28423     }
28424 });/*
28425  * - LGPL
28426  *
28427  * element
28428  * 
28429  */
28430
28431 /**
28432  * @class Roo.bootstrap.MessageBar
28433  * @extends Roo.bootstrap.Component
28434  * Bootstrap MessageBar class
28435  * @cfg {String} html contents of the MessageBar
28436  * @cfg {String} weight (info | success | warning | danger) default info
28437  * @cfg {String} beforeClass insert the bar before the given class
28438  * @cfg {Boolean} closable (true | false) default false
28439  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
28440  * 
28441  * @constructor
28442  * Create a new Element
28443  * @param {Object} config The config object
28444  */
28445
28446 Roo.bootstrap.MessageBar = function(config){
28447     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
28448 };
28449
28450 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
28451     
28452     html: '',
28453     weight: 'info',
28454     closable: false,
28455     fixed: false,
28456     beforeClass: 'bootstrap-sticky-wrap',
28457     
28458     getAutoCreate : function(){
28459         
28460         var cfg = {
28461             tag: 'div',
28462             cls: 'alert alert-dismissable alert-' + this.weight,
28463             cn: [
28464                 {
28465                     tag: 'span',
28466                     cls: 'message',
28467                     html: this.html || ''
28468                 }
28469             ]
28470         };
28471         
28472         if(this.fixed){
28473             cfg.cls += ' alert-messages-fixed';
28474         }
28475         
28476         if(this.closable){
28477             cfg.cn.push({
28478                 tag: 'button',
28479                 cls: 'close',
28480                 html: 'x'
28481             });
28482         }
28483         
28484         return cfg;
28485     },
28486     
28487     onRender : function(ct, position)
28488     {
28489         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28490         
28491         if(!this.el){
28492             var cfg = Roo.apply({},  this.getAutoCreate());
28493             cfg.id = Roo.id();
28494             
28495             if (this.cls) {
28496                 cfg.cls += ' ' + this.cls;
28497             }
28498             if (this.style) {
28499                 cfg.style = this.style;
28500             }
28501             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28502             
28503             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28504         }
28505         
28506         this.el.select('>button.close').on('click', this.hide, this);
28507         
28508     },
28509     
28510     show : function()
28511     {
28512         if (!this.rendered) {
28513             this.render();
28514         }
28515         
28516         this.el.show();
28517         
28518         this.fireEvent('show', this);
28519         
28520     },
28521     
28522     hide : function()
28523     {
28524         if (!this.rendered) {
28525             this.render();
28526         }
28527         
28528         this.el.hide();
28529         
28530         this.fireEvent('hide', this);
28531     },
28532     
28533     update : function()
28534     {
28535 //        var e = this.el.dom.firstChild;
28536 //        
28537 //        if(this.closable){
28538 //            e = e.nextSibling;
28539 //        }
28540 //        
28541 //        e.data = this.html || '';
28542
28543         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28544     }
28545    
28546 });
28547
28548  
28549
28550      /*
28551  * - LGPL
28552  *
28553  * Graph
28554  * 
28555  */
28556
28557
28558 /**
28559  * @class Roo.bootstrap.Graph
28560  * @extends Roo.bootstrap.Component
28561  * Bootstrap Graph class
28562 > Prameters
28563  -sm {number} sm 4
28564  -md {number} md 5
28565  @cfg {String} graphtype  bar | vbar | pie
28566  @cfg {number} g_x coodinator | centre x (pie)
28567  @cfg {number} g_y coodinator | centre y (pie)
28568  @cfg {number} g_r radius (pie)
28569  @cfg {number} g_height height of the chart (respected by all elements in the set)
28570  @cfg {number} g_width width of the chart (respected by all elements in the set)
28571  @cfg {Object} title The title of the chart
28572     
28573  -{Array}  values
28574  -opts (object) options for the chart 
28575      o {
28576      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28577      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28578      o vgutter (number)
28579      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.
28580      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28581      o to
28582      o stretch (boolean)
28583      o }
28584  -opts (object) options for the pie
28585      o{
28586      o cut
28587      o startAngle (number)
28588      o endAngle (number)
28589      } 
28590  *
28591  * @constructor
28592  * Create a new Input
28593  * @param {Object} config The config object
28594  */
28595
28596 Roo.bootstrap.Graph = function(config){
28597     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28598     
28599     this.addEvents({
28600         // img events
28601         /**
28602          * @event click
28603          * The img click event for the img.
28604          * @param {Roo.EventObject} e
28605          */
28606         "click" : true
28607     });
28608 };
28609
28610 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28611     
28612     sm: 4,
28613     md: 5,
28614     graphtype: 'bar',
28615     g_height: 250,
28616     g_width: 400,
28617     g_x: 50,
28618     g_y: 50,
28619     g_r: 30,
28620     opts:{
28621         //g_colors: this.colors,
28622         g_type: 'soft',
28623         g_gutter: '20%'
28624
28625     },
28626     title : false,
28627
28628     getAutoCreate : function(){
28629         
28630         var cfg = {
28631             tag: 'div',
28632             html : null
28633         };
28634         
28635         
28636         return  cfg;
28637     },
28638
28639     onRender : function(ct,position){
28640         
28641         
28642         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28643         
28644         if (typeof(Raphael) == 'undefined') {
28645             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28646             return;
28647         }
28648         
28649         this.raphael = Raphael(this.el.dom);
28650         
28651                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28652                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28653                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28654                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28655                 /*
28656                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28657                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28658                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28659                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28660                 
28661                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28662                 r.barchart(330, 10, 300, 220, data1);
28663                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28664                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28665                 */
28666                 
28667                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28668                 // r.barchart(30, 30, 560, 250,  xdata, {
28669                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28670                 //     axis : "0 0 1 1",
28671                 //     axisxlabels :  xdata
28672                 //     //yvalues : cols,
28673                    
28674                 // });
28675 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28676 //        
28677 //        this.load(null,xdata,{
28678 //                axis : "0 0 1 1",
28679 //                axisxlabels :  xdata
28680 //                });
28681
28682     },
28683
28684     load : function(graphtype,xdata,opts)
28685     {
28686         this.raphael.clear();
28687         if(!graphtype) {
28688             graphtype = this.graphtype;
28689         }
28690         if(!opts){
28691             opts = this.opts;
28692         }
28693         var r = this.raphael,
28694             fin = function () {
28695                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28696             },
28697             fout = function () {
28698                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28699             },
28700             pfin = function() {
28701                 this.sector.stop();
28702                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28703
28704                 if (this.label) {
28705                     this.label[0].stop();
28706                     this.label[0].attr({ r: 7.5 });
28707                     this.label[1].attr({ "font-weight": 800 });
28708                 }
28709             },
28710             pfout = function() {
28711                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28712
28713                 if (this.label) {
28714                     this.label[0].animate({ r: 5 }, 500, "bounce");
28715                     this.label[1].attr({ "font-weight": 400 });
28716                 }
28717             };
28718
28719         switch(graphtype){
28720             case 'bar':
28721                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28722                 break;
28723             case 'hbar':
28724                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28725                 break;
28726             case 'pie':
28727 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28728 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28729 //            
28730                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28731                 
28732                 break;
28733
28734         }
28735         
28736         if(this.title){
28737             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28738         }
28739         
28740     },
28741     
28742     setTitle: function(o)
28743     {
28744         this.title = o;
28745     },
28746     
28747     initEvents: function() {
28748         
28749         if(!this.href){
28750             this.el.on('click', this.onClick, this);
28751         }
28752     },
28753     
28754     onClick : function(e)
28755     {
28756         Roo.log('img onclick');
28757         this.fireEvent('click', this, e);
28758     }
28759    
28760 });
28761
28762  
28763 /*
28764  * - LGPL
28765  *
28766  * numberBox
28767  * 
28768  */
28769 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28770
28771 /**
28772  * @class Roo.bootstrap.dash.NumberBox
28773  * @extends Roo.bootstrap.Component
28774  * Bootstrap NumberBox class
28775  * @cfg {String} headline Box headline
28776  * @cfg {String} content Box content
28777  * @cfg {String} icon Box icon
28778  * @cfg {String} footer Footer text
28779  * @cfg {String} fhref Footer href
28780  * 
28781  * @constructor
28782  * Create a new NumberBox
28783  * @param {Object} config The config object
28784  */
28785
28786
28787 Roo.bootstrap.dash.NumberBox = function(config){
28788     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28789     
28790 };
28791
28792 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28793     
28794     headline : '',
28795     content : '',
28796     icon : '',
28797     footer : '',
28798     fhref : '',
28799     ficon : '',
28800     
28801     getAutoCreate : function(){
28802         
28803         var cfg = {
28804             tag : 'div',
28805             cls : 'small-box ',
28806             cn : [
28807                 {
28808                     tag : 'div',
28809                     cls : 'inner',
28810                     cn :[
28811                         {
28812                             tag : 'h3',
28813                             cls : 'roo-headline',
28814                             html : this.headline
28815                         },
28816                         {
28817                             tag : 'p',
28818                             cls : 'roo-content',
28819                             html : this.content
28820                         }
28821                     ]
28822                 }
28823             ]
28824         };
28825         
28826         if(this.icon){
28827             cfg.cn.push({
28828                 tag : 'div',
28829                 cls : 'icon',
28830                 cn :[
28831                     {
28832                         tag : 'i',
28833                         cls : 'ion ' + this.icon
28834                     }
28835                 ]
28836             });
28837         }
28838         
28839         if(this.footer){
28840             var footer = {
28841                 tag : 'a',
28842                 cls : 'small-box-footer',
28843                 href : this.fhref || '#',
28844                 html : this.footer
28845             };
28846             
28847             cfg.cn.push(footer);
28848             
28849         }
28850         
28851         return  cfg;
28852     },
28853
28854     onRender : function(ct,position){
28855         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28856
28857
28858        
28859                 
28860     },
28861
28862     setHeadline: function (value)
28863     {
28864         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28865     },
28866     
28867     setFooter: function (value, href)
28868     {
28869         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28870         
28871         if(href){
28872             this.el.select('a.small-box-footer',true).first().attr('href', href);
28873         }
28874         
28875     },
28876
28877     setContent: function (value)
28878     {
28879         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28880     },
28881
28882     initEvents: function() 
28883     {   
28884         
28885     }
28886     
28887 });
28888
28889  
28890 /*
28891  * - LGPL
28892  *
28893  * TabBox
28894  * 
28895  */
28896 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28897
28898 /**
28899  * @class Roo.bootstrap.dash.TabBox
28900  * @extends Roo.bootstrap.Component
28901  * Bootstrap TabBox class
28902  * @cfg {String} title Title of the TabBox
28903  * @cfg {String} icon Icon of the TabBox
28904  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28905  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28906  * 
28907  * @constructor
28908  * Create a new TabBox
28909  * @param {Object} config The config object
28910  */
28911
28912
28913 Roo.bootstrap.dash.TabBox = function(config){
28914     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28915     this.addEvents({
28916         // raw events
28917         /**
28918          * @event addpane
28919          * When a pane is added
28920          * @param {Roo.bootstrap.dash.TabPane} pane
28921          */
28922         "addpane" : true,
28923         /**
28924          * @event activatepane
28925          * When a pane is activated
28926          * @param {Roo.bootstrap.dash.TabPane} pane
28927          */
28928         "activatepane" : true
28929         
28930          
28931     });
28932     
28933     this.panes = [];
28934 };
28935
28936 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28937
28938     title : '',
28939     icon : false,
28940     showtabs : true,
28941     tabScrollable : false,
28942     
28943     getChildContainer : function()
28944     {
28945         return this.el.select('.tab-content', true).first();
28946     },
28947     
28948     getAutoCreate : function(){
28949         
28950         var header = {
28951             tag: 'li',
28952             cls: 'pull-left header',
28953             html: this.title,
28954             cn : []
28955         };
28956         
28957         if(this.icon){
28958             header.cn.push({
28959                 tag: 'i',
28960                 cls: 'fa ' + this.icon
28961             });
28962         }
28963         
28964         var h = {
28965             tag: 'ul',
28966             cls: 'nav nav-tabs pull-right',
28967             cn: [
28968                 header
28969             ]
28970         };
28971         
28972         if(this.tabScrollable){
28973             h = {
28974                 tag: 'div',
28975                 cls: 'tab-header',
28976                 cn: [
28977                     {
28978                         tag: 'ul',
28979                         cls: 'nav nav-tabs pull-right',
28980                         cn: [
28981                             header
28982                         ]
28983                     }
28984                 ]
28985             };
28986         }
28987         
28988         var cfg = {
28989             tag: 'div',
28990             cls: 'nav-tabs-custom',
28991             cn: [
28992                 h,
28993                 {
28994                     tag: 'div',
28995                     cls: 'tab-content no-padding',
28996                     cn: []
28997                 }
28998             ]
28999         };
29000
29001         return  cfg;
29002     },
29003     initEvents : function()
29004     {
29005         //Roo.log('add add pane handler');
29006         this.on('addpane', this.onAddPane, this);
29007     },
29008      /**
29009      * Updates the box title
29010      * @param {String} html to set the title to.
29011      */
29012     setTitle : function(value)
29013     {
29014         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
29015     },
29016     onAddPane : function(pane)
29017     {
29018         this.panes.push(pane);
29019         //Roo.log('addpane');
29020         //Roo.log(pane);
29021         // tabs are rendere left to right..
29022         if(!this.showtabs){
29023             return;
29024         }
29025         
29026         var ctr = this.el.select('.nav-tabs', true).first();
29027          
29028          
29029         var existing = ctr.select('.nav-tab',true);
29030         var qty = existing.getCount();;
29031         
29032         
29033         var tab = ctr.createChild({
29034             tag : 'li',
29035             cls : 'nav-tab' + (qty ? '' : ' active'),
29036             cn : [
29037                 {
29038                     tag : 'a',
29039                     href:'#',
29040                     html : pane.title
29041                 }
29042             ]
29043         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
29044         pane.tab = tab;
29045         
29046         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
29047         if (!qty) {
29048             pane.el.addClass('active');
29049         }
29050         
29051                 
29052     },
29053     onTabClick : function(ev,un,ob,pane)
29054     {
29055         //Roo.log('tab - prev default');
29056         ev.preventDefault();
29057         
29058         
29059         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
29060         pane.tab.addClass('active');
29061         //Roo.log(pane.title);
29062         this.getChildContainer().select('.tab-pane',true).removeClass('active');
29063         // technically we should have a deactivate event.. but maybe add later.
29064         // and it should not de-activate the selected tab...
29065         this.fireEvent('activatepane', pane);
29066         pane.el.addClass('active');
29067         pane.fireEvent('activate');
29068         
29069         
29070     },
29071     
29072     getActivePane : function()
29073     {
29074         var r = false;
29075         Roo.each(this.panes, function(p) {
29076             if(p.el.hasClass('active')){
29077                 r = p;
29078                 return false;
29079             }
29080             
29081             return;
29082         });
29083         
29084         return r;
29085     }
29086     
29087     
29088 });
29089
29090  
29091 /*
29092  * - LGPL
29093  *
29094  * Tab pane
29095  * 
29096  */
29097 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
29098 /**
29099  * @class Roo.bootstrap.TabPane
29100  * @extends Roo.bootstrap.Component
29101  * Bootstrap TabPane class
29102  * @cfg {Boolean} active (false | true) Default false
29103  * @cfg {String} title title of panel
29104
29105  * 
29106  * @constructor
29107  * Create a new TabPane
29108  * @param {Object} config The config object
29109  */
29110
29111 Roo.bootstrap.dash.TabPane = function(config){
29112     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
29113     
29114     this.addEvents({
29115         // raw events
29116         /**
29117          * @event activate
29118          * When a pane is activated
29119          * @param {Roo.bootstrap.dash.TabPane} pane
29120          */
29121         "activate" : true
29122          
29123     });
29124 };
29125
29126 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
29127     
29128     active : false,
29129     title : '',
29130     
29131     // the tabBox that this is attached to.
29132     tab : false,
29133      
29134     getAutoCreate : function() 
29135     {
29136         var cfg = {
29137             tag: 'div',
29138             cls: 'tab-pane'
29139         };
29140         
29141         if(this.active){
29142             cfg.cls += ' active';
29143         }
29144         
29145         return cfg;
29146     },
29147     initEvents  : function()
29148     {
29149         //Roo.log('trigger add pane handler');
29150         this.parent().fireEvent('addpane', this)
29151     },
29152     
29153      /**
29154      * Updates the tab title 
29155      * @param {String} html to set the title to.
29156      */
29157     setTitle: function(str)
29158     {
29159         if (!this.tab) {
29160             return;
29161         }
29162         this.title = str;
29163         this.tab.select('a', true).first().dom.innerHTML = str;
29164         
29165     }
29166     
29167     
29168     
29169 });
29170
29171  
29172
29173
29174  /*
29175  * - LGPL
29176  *
29177  * menu
29178  * 
29179  */
29180 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29181
29182 /**
29183  * @class Roo.bootstrap.menu.Menu
29184  * @extends Roo.bootstrap.Component
29185  * Bootstrap Menu class - container for Menu
29186  * @cfg {String} html Text of the menu
29187  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
29188  * @cfg {String} icon Font awesome icon
29189  * @cfg {String} pos Menu align to (top | bottom) default bottom
29190  * 
29191  * 
29192  * @constructor
29193  * Create a new Menu
29194  * @param {Object} config The config object
29195  */
29196
29197
29198 Roo.bootstrap.menu.Menu = function(config){
29199     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
29200     
29201     this.addEvents({
29202         /**
29203          * @event beforeshow
29204          * Fires before this menu is displayed
29205          * @param {Roo.bootstrap.menu.Menu} this
29206          */
29207         beforeshow : true,
29208         /**
29209          * @event beforehide
29210          * Fires before this menu is hidden
29211          * @param {Roo.bootstrap.menu.Menu} this
29212          */
29213         beforehide : true,
29214         /**
29215          * @event show
29216          * Fires after this menu is displayed
29217          * @param {Roo.bootstrap.menu.Menu} this
29218          */
29219         show : true,
29220         /**
29221          * @event hide
29222          * Fires after this menu is hidden
29223          * @param {Roo.bootstrap.menu.Menu} this
29224          */
29225         hide : true,
29226         /**
29227          * @event click
29228          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
29229          * @param {Roo.bootstrap.menu.Menu} this
29230          * @param {Roo.EventObject} e
29231          */
29232         click : true
29233     });
29234     
29235 };
29236
29237 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
29238     
29239     submenu : false,
29240     html : '',
29241     weight : 'default',
29242     icon : false,
29243     pos : 'bottom',
29244     
29245     
29246     getChildContainer : function() {
29247         if(this.isSubMenu){
29248             return this.el;
29249         }
29250         
29251         return this.el.select('ul.dropdown-menu', true).first();  
29252     },
29253     
29254     getAutoCreate : function()
29255     {
29256         var text = [
29257             {
29258                 tag : 'span',
29259                 cls : 'roo-menu-text',
29260                 html : this.html
29261             }
29262         ];
29263         
29264         if(this.icon){
29265             text.unshift({
29266                 tag : 'i',
29267                 cls : 'fa ' + this.icon
29268             })
29269         }
29270         
29271         
29272         var cfg = {
29273             tag : 'div',
29274             cls : 'btn-group',
29275             cn : [
29276                 {
29277                     tag : 'button',
29278                     cls : 'dropdown-button btn btn-' + this.weight,
29279                     cn : text
29280                 },
29281                 {
29282                     tag : 'button',
29283                     cls : 'dropdown-toggle btn btn-' + this.weight,
29284                     cn : [
29285                         {
29286                             tag : 'span',
29287                             cls : 'caret'
29288                         }
29289                     ]
29290                 },
29291                 {
29292                     tag : 'ul',
29293                     cls : 'dropdown-menu'
29294                 }
29295             ]
29296             
29297         };
29298         
29299         if(this.pos == 'top'){
29300             cfg.cls += ' dropup';
29301         }
29302         
29303         if(this.isSubMenu){
29304             cfg = {
29305                 tag : 'ul',
29306                 cls : 'dropdown-menu'
29307             }
29308         }
29309         
29310         return cfg;
29311     },
29312     
29313     onRender : function(ct, position)
29314     {
29315         this.isSubMenu = ct.hasClass('dropdown-submenu');
29316         
29317         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
29318     },
29319     
29320     initEvents : function() 
29321     {
29322         if(this.isSubMenu){
29323             return;
29324         }
29325         
29326         this.hidden = true;
29327         
29328         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
29329         this.triggerEl.on('click', this.onTriggerPress, this);
29330         
29331         this.buttonEl = this.el.select('button.dropdown-button', true).first();
29332         this.buttonEl.on('click', this.onClick, this);
29333         
29334     },
29335     
29336     list : function()
29337     {
29338         if(this.isSubMenu){
29339             return this.el;
29340         }
29341         
29342         return this.el.select('ul.dropdown-menu', true).first();
29343     },
29344     
29345     onClick : function(e)
29346     {
29347         this.fireEvent("click", this, e);
29348     },
29349     
29350     onTriggerPress  : function(e)
29351     {   
29352         if (this.isVisible()) {
29353             this.hide();
29354         } else {
29355             this.show();
29356         }
29357     },
29358     
29359     isVisible : function(){
29360         return !this.hidden;
29361     },
29362     
29363     show : function()
29364     {
29365         this.fireEvent("beforeshow", this);
29366         
29367         this.hidden = false;
29368         this.el.addClass('open');
29369         
29370         Roo.get(document).on("mouseup", this.onMouseUp, this);
29371         
29372         this.fireEvent("show", this);
29373         
29374         
29375     },
29376     
29377     hide : function()
29378     {
29379         this.fireEvent("beforehide", this);
29380         
29381         this.hidden = true;
29382         this.el.removeClass('open');
29383         
29384         Roo.get(document).un("mouseup", this.onMouseUp);
29385         
29386         this.fireEvent("hide", this);
29387     },
29388     
29389     onMouseUp : function()
29390     {
29391         this.hide();
29392     }
29393     
29394 });
29395
29396  
29397  /*
29398  * - LGPL
29399  *
29400  * menu item
29401  * 
29402  */
29403 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29404
29405 /**
29406  * @class Roo.bootstrap.menu.Item
29407  * @extends Roo.bootstrap.Component
29408  * Bootstrap MenuItem class
29409  * @cfg {Boolean} submenu (true | false) default false
29410  * @cfg {String} html text of the item
29411  * @cfg {String} href the link
29412  * @cfg {Boolean} disable (true | false) default false
29413  * @cfg {Boolean} preventDefault (true | false) default true
29414  * @cfg {String} icon Font awesome icon
29415  * @cfg {String} pos Submenu align to (left | right) default right 
29416  * 
29417  * 
29418  * @constructor
29419  * Create a new Item
29420  * @param {Object} config The config object
29421  */
29422
29423
29424 Roo.bootstrap.menu.Item = function(config){
29425     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
29426     this.addEvents({
29427         /**
29428          * @event mouseover
29429          * Fires when the mouse is hovering over this menu
29430          * @param {Roo.bootstrap.menu.Item} this
29431          * @param {Roo.EventObject} e
29432          */
29433         mouseover : true,
29434         /**
29435          * @event mouseout
29436          * Fires when the mouse exits this menu
29437          * @param {Roo.bootstrap.menu.Item} this
29438          * @param {Roo.EventObject} e
29439          */
29440         mouseout : true,
29441         // raw events
29442         /**
29443          * @event click
29444          * The raw click event for the entire grid.
29445          * @param {Roo.EventObject} e
29446          */
29447         click : true
29448     });
29449 };
29450
29451 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
29452     
29453     submenu : false,
29454     href : '',
29455     html : '',
29456     preventDefault: true,
29457     disable : false,
29458     icon : false,
29459     pos : 'right',
29460     
29461     getAutoCreate : function()
29462     {
29463         var text = [
29464             {
29465                 tag : 'span',
29466                 cls : 'roo-menu-item-text',
29467                 html : this.html
29468             }
29469         ];
29470         
29471         if(this.icon){
29472             text.unshift({
29473                 tag : 'i',
29474                 cls : 'fa ' + this.icon
29475             })
29476         }
29477         
29478         var cfg = {
29479             tag : 'li',
29480             cn : [
29481                 {
29482                     tag : 'a',
29483                     href : this.href || '#',
29484                     cn : text
29485                 }
29486             ]
29487         };
29488         
29489         if(this.disable){
29490             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29491         }
29492         
29493         if(this.submenu){
29494             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29495             
29496             if(this.pos == 'left'){
29497                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29498             }
29499         }
29500         
29501         return cfg;
29502     },
29503     
29504     initEvents : function() 
29505     {
29506         this.el.on('mouseover', this.onMouseOver, this);
29507         this.el.on('mouseout', this.onMouseOut, this);
29508         
29509         this.el.select('a', true).first().on('click', this.onClick, this);
29510         
29511     },
29512     
29513     onClick : function(e)
29514     {
29515         if(this.preventDefault){
29516             e.preventDefault();
29517         }
29518         
29519         this.fireEvent("click", this, e);
29520     },
29521     
29522     onMouseOver : function(e)
29523     {
29524         if(this.submenu && this.pos == 'left'){
29525             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29526         }
29527         
29528         this.fireEvent("mouseover", this, e);
29529     },
29530     
29531     onMouseOut : function(e)
29532     {
29533         this.fireEvent("mouseout", this, e);
29534     }
29535 });
29536
29537  
29538
29539  /*
29540  * - LGPL
29541  *
29542  * menu separator
29543  * 
29544  */
29545 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29546
29547 /**
29548  * @class Roo.bootstrap.menu.Separator
29549  * @extends Roo.bootstrap.Component
29550  * Bootstrap Separator class
29551  * 
29552  * @constructor
29553  * Create a new Separator
29554  * @param {Object} config The config object
29555  */
29556
29557
29558 Roo.bootstrap.menu.Separator = function(config){
29559     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29560 };
29561
29562 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29563     
29564     getAutoCreate : function(){
29565         var cfg = {
29566             tag : 'li',
29567             cls: 'dropdown-divider divider'
29568         };
29569         
29570         return cfg;
29571     }
29572    
29573 });
29574
29575  
29576
29577  /*
29578  * - LGPL
29579  *
29580  * Tooltip
29581  * 
29582  */
29583
29584 /**
29585  * @class Roo.bootstrap.Tooltip
29586  * Bootstrap Tooltip class
29587  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29588  * to determine which dom element triggers the tooltip.
29589  * 
29590  * It needs to add support for additional attributes like tooltip-position
29591  * 
29592  * @constructor
29593  * Create a new Toolti
29594  * @param {Object} config The config object
29595  */
29596
29597 Roo.bootstrap.Tooltip = function(config){
29598     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29599     
29600     this.alignment = Roo.bootstrap.Tooltip.alignment;
29601     
29602     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29603         this.alignment = config.alignment;
29604     }
29605     
29606 };
29607
29608 Roo.apply(Roo.bootstrap.Tooltip, {
29609     /**
29610      * @function init initialize tooltip monitoring.
29611      * @static
29612      */
29613     currentEl : false,
29614     currentTip : false,
29615     currentRegion : false,
29616     
29617     //  init : delay?
29618     
29619     init : function()
29620     {
29621         Roo.get(document).on('mouseover', this.enter ,this);
29622         Roo.get(document).on('mouseout', this.leave, this);
29623          
29624         
29625         this.currentTip = new Roo.bootstrap.Tooltip();
29626     },
29627     
29628     enter : function(ev)
29629     {
29630         var dom = ev.getTarget();
29631         
29632         //Roo.log(['enter',dom]);
29633         var el = Roo.fly(dom);
29634         if (this.currentEl) {
29635             //Roo.log(dom);
29636             //Roo.log(this.currentEl);
29637             //Roo.log(this.currentEl.contains(dom));
29638             if (this.currentEl == el) {
29639                 return;
29640             }
29641             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29642                 return;
29643             }
29644
29645         }
29646         
29647         if (this.currentTip.el) {
29648             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29649         }    
29650         //Roo.log(ev);
29651         
29652         if(!el || el.dom == document){
29653             return;
29654         }
29655         
29656         var bindEl = el; 
29657         var pel = false;
29658         if (!el.attr('tooltip')) {
29659             pel = el.findParent("[tooltip]");
29660             if (pel) {
29661                 bindEl = Roo.get(pel);
29662             }
29663         }
29664         
29665        
29666         
29667         // you can not look for children, as if el is the body.. then everythign is the child..
29668         if (!pel && !el.attr('tooltip')) { //
29669             if (!el.select("[tooltip]").elements.length) {
29670                 return;
29671             }
29672             // is the mouse over this child...?
29673             bindEl = el.select("[tooltip]").first();
29674             var xy = ev.getXY();
29675             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29676                 //Roo.log("not in region.");
29677                 return;
29678             }
29679             //Roo.log("child element over..");
29680             
29681         }
29682         this.currentEl = el;
29683         this.currentTip.bind(bindEl);
29684         this.currentRegion = Roo.lib.Region.getRegion(dom);
29685         this.currentTip.enter();
29686         
29687     },
29688     leave : function(ev)
29689     {
29690         var dom = ev.getTarget();
29691         //Roo.log(['leave',dom]);
29692         if (!this.currentEl) {
29693             return;
29694         }
29695         
29696         
29697         if (dom != this.currentEl.dom) {
29698             return;
29699         }
29700         var xy = ev.getXY();
29701         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29702             return;
29703         }
29704         // only activate leave if mouse cursor is outside... bounding box..
29705         
29706         
29707         
29708         
29709         if (this.currentTip) {
29710             this.currentTip.leave();
29711         }
29712         //Roo.log('clear currentEl');
29713         this.currentEl = false;
29714         
29715         
29716     },
29717     alignment : {
29718         'left' : ['r-l', [-2,0], 'right'],
29719         'right' : ['l-r', [2,0], 'left'],
29720         'bottom' : ['t-b', [0,2], 'top'],
29721         'top' : [ 'b-t', [0,-2], 'bottom']
29722     }
29723     
29724 });
29725
29726
29727 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29728     
29729     
29730     bindEl : false,
29731     
29732     delay : null, // can be { show : 300 , hide: 500}
29733     
29734     timeout : null,
29735     
29736     hoverState : null, //???
29737     
29738     placement : 'bottom', 
29739     
29740     alignment : false,
29741     
29742     getAutoCreate : function(){
29743     
29744         var cfg = {
29745            cls : 'tooltip',   
29746            role : 'tooltip',
29747            cn : [
29748                 {
29749                     cls : 'tooltip-arrow arrow'
29750                 },
29751                 {
29752                     cls : 'tooltip-inner'
29753                 }
29754            ]
29755         };
29756         
29757         return cfg;
29758     },
29759     bind : function(el)
29760     {
29761         this.bindEl = el;
29762     },
29763     
29764     initEvents : function()
29765     {
29766         this.arrowEl = this.el.select('.arrow', true).first();
29767         this.innerEl = this.el.select('.tooltip-inner', true).first();
29768     },
29769     
29770     enter : function () {
29771        
29772         if (this.timeout != null) {
29773             clearTimeout(this.timeout);
29774         }
29775         
29776         this.hoverState = 'in';
29777          //Roo.log("enter - show");
29778         if (!this.delay || !this.delay.show) {
29779             this.show();
29780             return;
29781         }
29782         var _t = this;
29783         this.timeout = setTimeout(function () {
29784             if (_t.hoverState == 'in') {
29785                 _t.show();
29786             }
29787         }, this.delay.show);
29788     },
29789     leave : function()
29790     {
29791         clearTimeout(this.timeout);
29792     
29793         this.hoverState = 'out';
29794          if (!this.delay || !this.delay.hide) {
29795             this.hide();
29796             return;
29797         }
29798        
29799         var _t = this;
29800         this.timeout = setTimeout(function () {
29801             //Roo.log("leave - timeout");
29802             
29803             if (_t.hoverState == 'out') {
29804                 _t.hide();
29805                 Roo.bootstrap.Tooltip.currentEl = false;
29806             }
29807         }, delay);
29808     },
29809     
29810     show : function (msg)
29811     {
29812         if (!this.el) {
29813             this.render(document.body);
29814         }
29815         // set content.
29816         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29817         
29818         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29819         
29820         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29821         
29822         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29823                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29824         
29825         var placement = typeof this.placement == 'function' ?
29826             this.placement.call(this, this.el, on_el) :
29827             this.placement;
29828             
29829         var autoToken = /\s?auto?\s?/i;
29830         var autoPlace = autoToken.test(placement);
29831         if (autoPlace) {
29832             placement = placement.replace(autoToken, '') || 'top';
29833         }
29834         
29835         //this.el.detach()
29836         //this.el.setXY([0,0]);
29837         this.el.show();
29838         //this.el.dom.style.display='block';
29839         
29840         //this.el.appendTo(on_el);
29841         
29842         var p = this.getPosition();
29843         var box = this.el.getBox();
29844         
29845         if (autoPlace) {
29846             // fixme..
29847         }
29848         
29849         var align = this.alignment[placement];
29850         
29851         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29852         
29853         if(placement == 'top' || placement == 'bottom'){
29854             if(xy[0] < 0){
29855                 placement = 'right';
29856             }
29857             
29858             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29859                 placement = 'left';
29860             }
29861             
29862             var scroll = Roo.select('body', true).first().getScroll();
29863             
29864             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29865                 placement = 'top';
29866             }
29867             
29868             align = this.alignment[placement];
29869             
29870             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29871             
29872         }
29873         
29874         var elems = document.getElementsByTagName('div');
29875         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29876         for (var i = 0; i < elems.length; i++) {
29877           var zindex = Number.parseInt(
29878                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29879                 10
29880           );
29881           if (zindex > highest) {
29882             highest = zindex;
29883           }
29884         }
29885         
29886         
29887         
29888         this.el.dom.style.zIndex = highest;
29889         
29890         this.el.alignTo(this.bindEl, align[0],align[1]);
29891         //var arrow = this.el.select('.arrow',true).first();
29892         //arrow.set(align[2], 
29893         
29894         this.el.addClass(placement);
29895         this.el.addClass("bs-tooltip-"+ placement);
29896         
29897         this.el.addClass('in fade show');
29898         
29899         this.hoverState = null;
29900         
29901         if (this.el.hasClass('fade')) {
29902             // fade it?
29903         }
29904         
29905         
29906         
29907         
29908         
29909     },
29910     hide : function()
29911     {
29912          
29913         if (!this.el) {
29914             return;
29915         }
29916         //this.el.setXY([0,0]);
29917         this.el.removeClass(['show', 'in']);
29918         //this.el.hide();
29919         
29920     }
29921     
29922 });
29923  
29924
29925  /*
29926  * - LGPL
29927  *
29928  * Location Picker
29929  * 
29930  */
29931
29932 /**
29933  * @class Roo.bootstrap.LocationPicker
29934  * @extends Roo.bootstrap.Component
29935  * Bootstrap LocationPicker class
29936  * @cfg {Number} latitude Position when init default 0
29937  * @cfg {Number} longitude Position when init default 0
29938  * @cfg {Number} zoom default 15
29939  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29940  * @cfg {Boolean} mapTypeControl default false
29941  * @cfg {Boolean} disableDoubleClickZoom default false
29942  * @cfg {Boolean} scrollwheel default true
29943  * @cfg {Boolean} streetViewControl default false
29944  * @cfg {Number} radius default 0
29945  * @cfg {String} locationName
29946  * @cfg {Boolean} draggable default true
29947  * @cfg {Boolean} enableAutocomplete default false
29948  * @cfg {Boolean} enableReverseGeocode default true
29949  * @cfg {String} markerTitle
29950  * 
29951  * @constructor
29952  * Create a new LocationPicker
29953  * @param {Object} config The config object
29954  */
29955
29956
29957 Roo.bootstrap.LocationPicker = function(config){
29958     
29959     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29960     
29961     this.addEvents({
29962         /**
29963          * @event initial
29964          * Fires when the picker initialized.
29965          * @param {Roo.bootstrap.LocationPicker} this
29966          * @param {Google Location} location
29967          */
29968         initial : true,
29969         /**
29970          * @event positionchanged
29971          * Fires when the picker position changed.
29972          * @param {Roo.bootstrap.LocationPicker} this
29973          * @param {Google Location} location
29974          */
29975         positionchanged : true,
29976         /**
29977          * @event resize
29978          * Fires when the map resize.
29979          * @param {Roo.bootstrap.LocationPicker} this
29980          */
29981         resize : true,
29982         /**
29983          * @event show
29984          * Fires when the map show.
29985          * @param {Roo.bootstrap.LocationPicker} this
29986          */
29987         show : true,
29988         /**
29989          * @event hide
29990          * Fires when the map hide.
29991          * @param {Roo.bootstrap.LocationPicker} this
29992          */
29993         hide : true,
29994         /**
29995          * @event mapClick
29996          * Fires when click the map.
29997          * @param {Roo.bootstrap.LocationPicker} this
29998          * @param {Map event} e
29999          */
30000         mapClick : true,
30001         /**
30002          * @event mapRightClick
30003          * Fires when right click the map.
30004          * @param {Roo.bootstrap.LocationPicker} this
30005          * @param {Map event} e
30006          */
30007         mapRightClick : true,
30008         /**
30009          * @event markerClick
30010          * Fires when click the marker.
30011          * @param {Roo.bootstrap.LocationPicker} this
30012          * @param {Map event} e
30013          */
30014         markerClick : true,
30015         /**
30016          * @event markerRightClick
30017          * Fires when right click the marker.
30018          * @param {Roo.bootstrap.LocationPicker} this
30019          * @param {Map event} e
30020          */
30021         markerRightClick : true,
30022         /**
30023          * @event OverlayViewDraw
30024          * Fires when OverlayView Draw
30025          * @param {Roo.bootstrap.LocationPicker} this
30026          */
30027         OverlayViewDraw : true,
30028         /**
30029          * @event OverlayViewOnAdd
30030          * Fires when OverlayView Draw
30031          * @param {Roo.bootstrap.LocationPicker} this
30032          */
30033         OverlayViewOnAdd : true,
30034         /**
30035          * @event OverlayViewOnRemove
30036          * Fires when OverlayView Draw
30037          * @param {Roo.bootstrap.LocationPicker} this
30038          */
30039         OverlayViewOnRemove : true,
30040         /**
30041          * @event OverlayViewShow
30042          * Fires when OverlayView Draw
30043          * @param {Roo.bootstrap.LocationPicker} this
30044          * @param {Pixel} cpx
30045          */
30046         OverlayViewShow : true,
30047         /**
30048          * @event OverlayViewHide
30049          * Fires when OverlayView Draw
30050          * @param {Roo.bootstrap.LocationPicker} this
30051          */
30052         OverlayViewHide : true,
30053         /**
30054          * @event loadexception
30055          * Fires when load google lib failed.
30056          * @param {Roo.bootstrap.LocationPicker} this
30057          */
30058         loadexception : true
30059     });
30060         
30061 };
30062
30063 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
30064     
30065     gMapContext: false,
30066     
30067     latitude: 0,
30068     longitude: 0,
30069     zoom: 15,
30070     mapTypeId: false,
30071     mapTypeControl: false,
30072     disableDoubleClickZoom: false,
30073     scrollwheel: true,
30074     streetViewControl: false,
30075     radius: 0,
30076     locationName: '',
30077     draggable: true,
30078     enableAutocomplete: false,
30079     enableReverseGeocode: true,
30080     markerTitle: '',
30081     
30082     getAutoCreate: function()
30083     {
30084
30085         var cfg = {
30086             tag: 'div',
30087             cls: 'roo-location-picker'
30088         };
30089         
30090         return cfg
30091     },
30092     
30093     initEvents: function(ct, position)
30094     {       
30095         if(!this.el.getWidth() || this.isApplied()){
30096             return;
30097         }
30098         
30099         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30100         
30101         this.initial();
30102     },
30103     
30104     initial: function()
30105     {
30106         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
30107             this.fireEvent('loadexception', this);
30108             return;
30109         }
30110         
30111         if(!this.mapTypeId){
30112             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
30113         }
30114         
30115         this.gMapContext = this.GMapContext();
30116         
30117         this.initOverlayView();
30118         
30119         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
30120         
30121         var _this = this;
30122                 
30123         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
30124             _this.setPosition(_this.gMapContext.marker.position);
30125         });
30126         
30127         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
30128             _this.fireEvent('mapClick', this, event);
30129             
30130         });
30131
30132         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
30133             _this.fireEvent('mapRightClick', this, event);
30134             
30135         });
30136         
30137         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
30138             _this.fireEvent('markerClick', this, event);
30139             
30140         });
30141
30142         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
30143             _this.fireEvent('markerRightClick', this, event);
30144             
30145         });
30146         
30147         this.setPosition(this.gMapContext.location);
30148         
30149         this.fireEvent('initial', this, this.gMapContext.location);
30150     },
30151     
30152     initOverlayView: function()
30153     {
30154         var _this = this;
30155         
30156         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
30157             
30158             draw: function()
30159             {
30160                 _this.fireEvent('OverlayViewDraw', _this);
30161             },
30162             
30163             onAdd: function()
30164             {
30165                 _this.fireEvent('OverlayViewOnAdd', _this);
30166             },
30167             
30168             onRemove: function()
30169             {
30170                 _this.fireEvent('OverlayViewOnRemove', _this);
30171             },
30172             
30173             show: function(cpx)
30174             {
30175                 _this.fireEvent('OverlayViewShow', _this, cpx);
30176             },
30177             
30178             hide: function()
30179             {
30180                 _this.fireEvent('OverlayViewHide', _this);
30181             }
30182             
30183         });
30184     },
30185     
30186     fromLatLngToContainerPixel: function(event)
30187     {
30188         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
30189     },
30190     
30191     isApplied: function() 
30192     {
30193         return this.getGmapContext() == false ? false : true;
30194     },
30195     
30196     getGmapContext: function() 
30197     {
30198         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
30199     },
30200     
30201     GMapContext: function() 
30202     {
30203         var position = new google.maps.LatLng(this.latitude, this.longitude);
30204         
30205         var _map = new google.maps.Map(this.el.dom, {
30206             center: position,
30207             zoom: this.zoom,
30208             mapTypeId: this.mapTypeId,
30209             mapTypeControl: this.mapTypeControl,
30210             disableDoubleClickZoom: this.disableDoubleClickZoom,
30211             scrollwheel: this.scrollwheel,
30212             streetViewControl: this.streetViewControl,
30213             locationName: this.locationName,
30214             draggable: this.draggable,
30215             enableAutocomplete: this.enableAutocomplete,
30216             enableReverseGeocode: this.enableReverseGeocode
30217         });
30218         
30219         var _marker = new google.maps.Marker({
30220             position: position,
30221             map: _map,
30222             title: this.markerTitle,
30223             draggable: this.draggable
30224         });
30225         
30226         return {
30227             map: _map,
30228             marker: _marker,
30229             circle: null,
30230             location: position,
30231             radius: this.radius,
30232             locationName: this.locationName,
30233             addressComponents: {
30234                 formatted_address: null,
30235                 addressLine1: null,
30236                 addressLine2: null,
30237                 streetName: null,
30238                 streetNumber: null,
30239                 city: null,
30240                 district: null,
30241                 state: null,
30242                 stateOrProvince: null
30243             },
30244             settings: this,
30245             domContainer: this.el.dom,
30246             geodecoder: new google.maps.Geocoder()
30247         };
30248     },
30249     
30250     drawCircle: function(center, radius, options) 
30251     {
30252         if (this.gMapContext.circle != null) {
30253             this.gMapContext.circle.setMap(null);
30254         }
30255         if (radius > 0) {
30256             radius *= 1;
30257             options = Roo.apply({}, options, {
30258                 strokeColor: "#0000FF",
30259                 strokeOpacity: .35,
30260                 strokeWeight: 2,
30261                 fillColor: "#0000FF",
30262                 fillOpacity: .2
30263             });
30264             
30265             options.map = this.gMapContext.map;
30266             options.radius = radius;
30267             options.center = center;
30268             this.gMapContext.circle = new google.maps.Circle(options);
30269             return this.gMapContext.circle;
30270         }
30271         
30272         return null;
30273     },
30274     
30275     setPosition: function(location) 
30276     {
30277         this.gMapContext.location = location;
30278         this.gMapContext.marker.setPosition(location);
30279         this.gMapContext.map.panTo(location);
30280         this.drawCircle(location, this.gMapContext.radius, {});
30281         
30282         var _this = this;
30283         
30284         if (this.gMapContext.settings.enableReverseGeocode) {
30285             this.gMapContext.geodecoder.geocode({
30286                 latLng: this.gMapContext.location
30287             }, function(results, status) {
30288                 
30289                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
30290                     _this.gMapContext.locationName = results[0].formatted_address;
30291                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
30292                     
30293                     _this.fireEvent('positionchanged', this, location);
30294                 }
30295             });
30296             
30297             return;
30298         }
30299         
30300         this.fireEvent('positionchanged', this, location);
30301     },
30302     
30303     resize: function()
30304     {
30305         google.maps.event.trigger(this.gMapContext.map, "resize");
30306         
30307         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
30308         
30309         this.fireEvent('resize', this);
30310     },
30311     
30312     setPositionByLatLng: function(latitude, longitude)
30313     {
30314         this.setPosition(new google.maps.LatLng(latitude, longitude));
30315     },
30316     
30317     getCurrentPosition: function() 
30318     {
30319         return {
30320             latitude: this.gMapContext.location.lat(),
30321             longitude: this.gMapContext.location.lng()
30322         };
30323     },
30324     
30325     getAddressName: function() 
30326     {
30327         return this.gMapContext.locationName;
30328     },
30329     
30330     getAddressComponents: function() 
30331     {
30332         return this.gMapContext.addressComponents;
30333     },
30334     
30335     address_component_from_google_geocode: function(address_components) 
30336     {
30337         var result = {};
30338         
30339         for (var i = 0; i < address_components.length; i++) {
30340             var component = address_components[i];
30341             if (component.types.indexOf("postal_code") >= 0) {
30342                 result.postalCode = component.short_name;
30343             } else if (component.types.indexOf("street_number") >= 0) {
30344                 result.streetNumber = component.short_name;
30345             } else if (component.types.indexOf("route") >= 0) {
30346                 result.streetName = component.short_name;
30347             } else if (component.types.indexOf("neighborhood") >= 0) {
30348                 result.city = component.short_name;
30349             } else if (component.types.indexOf("locality") >= 0) {
30350                 result.city = component.short_name;
30351             } else if (component.types.indexOf("sublocality") >= 0) {
30352                 result.district = component.short_name;
30353             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
30354                 result.stateOrProvince = component.short_name;
30355             } else if (component.types.indexOf("country") >= 0) {
30356                 result.country = component.short_name;
30357             }
30358         }
30359         
30360         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
30361         result.addressLine2 = "";
30362         return result;
30363     },
30364     
30365     setZoomLevel: function(zoom)
30366     {
30367         this.gMapContext.map.setZoom(zoom);
30368     },
30369     
30370     show: function()
30371     {
30372         if(!this.el){
30373             return;
30374         }
30375         
30376         this.el.show();
30377         
30378         this.resize();
30379         
30380         this.fireEvent('show', this);
30381     },
30382     
30383     hide: function()
30384     {
30385         if(!this.el){
30386             return;
30387         }
30388         
30389         this.el.hide();
30390         
30391         this.fireEvent('hide', this);
30392     }
30393     
30394 });
30395
30396 Roo.apply(Roo.bootstrap.LocationPicker, {
30397     
30398     OverlayView : function(map, options)
30399     {
30400         options = options || {};
30401         
30402         this.setMap(map);
30403     }
30404     
30405     
30406 });/**
30407  * @class Roo.bootstrap.Alert
30408  * @extends Roo.bootstrap.Component
30409  * Bootstrap Alert class - shows an alert area box
30410  * eg
30411  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
30412   Enter a valid email address
30413 </div>
30414  * @licence LGPL
30415  * @cfg {String} title The title of alert
30416  * @cfg {String} html The content of alert
30417  * @cfg {String} weight (success|info|warning|danger) Weight of the message
30418  * @cfg {String} fa font-awesomeicon
30419  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
30420  * @cfg {Boolean} close true to show a x closer
30421  * 
30422  * 
30423  * @constructor
30424  * Create a new alert
30425  * @param {Object} config The config object
30426  */
30427
30428
30429 Roo.bootstrap.Alert = function(config){
30430     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
30431     
30432 };
30433
30434 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
30435     
30436     title: '',
30437     html: '',
30438     weight: false,
30439     fa: false,
30440     faicon: false, // BC
30441     close : false,
30442     
30443     
30444     getAutoCreate : function()
30445     {
30446         
30447         var cfg = {
30448             tag : 'div',
30449             cls : 'alert',
30450             cn : [
30451                 {
30452                     tag: 'button',
30453                     type :  "button",
30454                     cls: "close",
30455                     html : '×',
30456                     style : this.close ? '' : 'display:none'
30457                 },
30458                 {
30459                     tag : 'i',
30460                     cls : 'roo-alert-icon'
30461                     
30462                 },
30463                 {
30464                     tag : 'b',
30465                     cls : 'roo-alert-title',
30466                     html : this.title
30467                 },
30468                 {
30469                     tag : 'span',
30470                     cls : 'roo-alert-text',
30471                     html : this.html
30472                 }
30473             ]
30474         };
30475         
30476         if(this.faicon){
30477             cfg.cn[0].cls += ' fa ' + this.faicon;
30478         }
30479         if(this.fa){
30480             cfg.cn[0].cls += ' fa ' + this.fa;
30481         }
30482         
30483         if(this.weight){
30484             cfg.cls += ' alert-' + this.weight;
30485         }
30486         
30487         return cfg;
30488     },
30489     
30490     initEvents: function() 
30491     {
30492         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30493         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30494         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30495         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30496         if (this.seconds > 0) {
30497             this.hide.defer(this.seconds, this);
30498         }
30499     },
30500     /**
30501      * Set the Title Message HTML
30502      * @param {String} html
30503      */
30504     setTitle : function(str)
30505     {
30506         this.titleEl.dom.innerHTML = str;
30507     },
30508      
30509      /**
30510      * Set the Body Message HTML
30511      * @param {String} html
30512      */
30513     setHtml : function(str)
30514     {
30515         this.htmlEl.dom.innerHTML = str;
30516     },
30517     /**
30518      * Set the Weight of the alert
30519      * @param {String} (success|info|warning|danger) weight
30520      */
30521     
30522     setWeight : function(weight)
30523     {
30524         if(this.weight){
30525             this.el.removeClass('alert-' + this.weight);
30526         }
30527         
30528         this.weight = weight;
30529         
30530         this.el.addClass('alert-' + this.weight);
30531     },
30532       /**
30533      * Set the Icon of the alert
30534      * @param {String} see fontawsome names (name without the 'fa-' bit)
30535      */
30536     setIcon : function(icon)
30537     {
30538         if(this.faicon){
30539             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30540         }
30541         
30542         this.faicon = icon;
30543         
30544         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30545     },
30546     /**
30547      * Hide the Alert
30548      */
30549     hide: function() 
30550     {
30551         this.el.hide();   
30552     },
30553     /**
30554      * Show the Alert
30555      */
30556     show: function() 
30557     {  
30558         this.el.show();   
30559     }
30560     
30561 });
30562
30563  
30564 /*
30565 * Licence: LGPL
30566 */
30567
30568 /**
30569  * @class Roo.bootstrap.UploadCropbox
30570  * @extends Roo.bootstrap.Component
30571  * Bootstrap UploadCropbox class
30572  * @cfg {String} emptyText show when image has been loaded
30573  * @cfg {String} rotateNotify show when image too small to rotate
30574  * @cfg {Number} errorTimeout default 3000
30575  * @cfg {Number} minWidth default 300
30576  * @cfg {Number} minHeight default 300
30577  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30578  * @cfg {Boolean} isDocument (true|false) default false
30579  * @cfg {String} url action url
30580  * @cfg {String} paramName default 'imageUpload'
30581  * @cfg {String} method default POST
30582  * @cfg {Boolean} loadMask (true|false) default true
30583  * @cfg {Boolean} loadingText default 'Loading...'
30584  * 
30585  * @constructor
30586  * Create a new UploadCropbox
30587  * @param {Object} config The config object
30588  */
30589
30590 Roo.bootstrap.UploadCropbox = function(config){
30591     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30592     
30593     this.addEvents({
30594         /**
30595          * @event beforeselectfile
30596          * Fire before select file
30597          * @param {Roo.bootstrap.UploadCropbox} this
30598          */
30599         "beforeselectfile" : true,
30600         /**
30601          * @event initial
30602          * Fire after initEvent
30603          * @param {Roo.bootstrap.UploadCropbox} this
30604          */
30605         "initial" : true,
30606         /**
30607          * @event crop
30608          * Fire after initEvent
30609          * @param {Roo.bootstrap.UploadCropbox} this
30610          * @param {String} data
30611          */
30612         "crop" : true,
30613         /**
30614          * @event prepare
30615          * Fire when preparing the file data
30616          * @param {Roo.bootstrap.UploadCropbox} this
30617          * @param {Object} file
30618          */
30619         "prepare" : true,
30620         /**
30621          * @event exception
30622          * Fire when get exception
30623          * @param {Roo.bootstrap.UploadCropbox} this
30624          * @param {XMLHttpRequest} xhr
30625          */
30626         "exception" : true,
30627         /**
30628          * @event beforeloadcanvas
30629          * Fire before load the canvas
30630          * @param {Roo.bootstrap.UploadCropbox} this
30631          * @param {String} src
30632          */
30633         "beforeloadcanvas" : true,
30634         /**
30635          * @event trash
30636          * Fire when trash image
30637          * @param {Roo.bootstrap.UploadCropbox} this
30638          */
30639         "trash" : true,
30640         /**
30641          * @event download
30642          * Fire when download the image
30643          * @param {Roo.bootstrap.UploadCropbox} this
30644          */
30645         "download" : true,
30646         /**
30647          * @event footerbuttonclick
30648          * Fire when footerbuttonclick
30649          * @param {Roo.bootstrap.UploadCropbox} this
30650          * @param {String} type
30651          */
30652         "footerbuttonclick" : true,
30653         /**
30654          * @event resize
30655          * Fire when resize
30656          * @param {Roo.bootstrap.UploadCropbox} this
30657          */
30658         "resize" : true,
30659         /**
30660          * @event rotate
30661          * Fire when rotate the image
30662          * @param {Roo.bootstrap.UploadCropbox} this
30663          * @param {String} pos
30664          */
30665         "rotate" : true,
30666         /**
30667          * @event inspect
30668          * Fire when inspect the file
30669          * @param {Roo.bootstrap.UploadCropbox} this
30670          * @param {Object} file
30671          */
30672         "inspect" : true,
30673         /**
30674          * @event upload
30675          * Fire when xhr upload the file
30676          * @param {Roo.bootstrap.UploadCropbox} this
30677          * @param {Object} data
30678          */
30679         "upload" : true,
30680         /**
30681          * @event arrange
30682          * Fire when arrange the file data
30683          * @param {Roo.bootstrap.UploadCropbox} this
30684          * @param {Object} formData
30685          */
30686         "arrange" : true
30687     });
30688     
30689     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30690 };
30691
30692 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30693     
30694     emptyText : 'Click to upload image',
30695     rotateNotify : 'Image is too small to rotate',
30696     errorTimeout : 3000,
30697     scale : 0,
30698     baseScale : 1,
30699     rotate : 0,
30700     dragable : false,
30701     pinching : false,
30702     mouseX : 0,
30703     mouseY : 0,
30704     cropData : false,
30705     minWidth : 300,
30706     minHeight : 300,
30707     file : false,
30708     exif : {},
30709     baseRotate : 1,
30710     cropType : 'image/jpeg',
30711     buttons : false,
30712     canvasLoaded : false,
30713     isDocument : false,
30714     method : 'POST',
30715     paramName : 'imageUpload',
30716     loadMask : true,
30717     loadingText : 'Loading...',
30718     maskEl : false,
30719     
30720     getAutoCreate : function()
30721     {
30722         var cfg = {
30723             tag : 'div',
30724             cls : 'roo-upload-cropbox',
30725             cn : [
30726                 {
30727                     tag : 'input',
30728                     cls : 'roo-upload-cropbox-selector',
30729                     type : 'file'
30730                 },
30731                 {
30732                     tag : 'div',
30733                     cls : 'roo-upload-cropbox-body',
30734                     style : 'cursor:pointer',
30735                     cn : [
30736                         {
30737                             tag : 'div',
30738                             cls : 'roo-upload-cropbox-preview'
30739                         },
30740                         {
30741                             tag : 'div',
30742                             cls : 'roo-upload-cropbox-thumb'
30743                         },
30744                         {
30745                             tag : 'div',
30746                             cls : 'roo-upload-cropbox-empty-notify',
30747                             html : this.emptyText
30748                         },
30749                         {
30750                             tag : 'div',
30751                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30752                             html : this.rotateNotify
30753                         }
30754                     ]
30755                 },
30756                 {
30757                     tag : 'div',
30758                     cls : 'roo-upload-cropbox-footer',
30759                     cn : {
30760                         tag : 'div',
30761                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30762                         cn : []
30763                     }
30764                 }
30765             ]
30766         };
30767         
30768         return cfg;
30769     },
30770     
30771     onRender : function(ct, position)
30772     {
30773         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30774         
30775         if (this.buttons.length) {
30776             
30777             Roo.each(this.buttons, function(bb) {
30778                 
30779                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30780                 
30781                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30782                 
30783             }, this);
30784         }
30785         
30786         if(this.loadMask){
30787             this.maskEl = this.el;
30788         }
30789     },
30790     
30791     initEvents : function()
30792     {
30793         this.urlAPI = (window.createObjectURL && window) || 
30794                                 (window.URL && URL.revokeObjectURL && URL) || 
30795                                 (window.webkitURL && webkitURL);
30796                         
30797         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30798         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30799         
30800         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30801         this.selectorEl.hide();
30802         
30803         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30804         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30805         
30806         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30807         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30808         this.thumbEl.hide();
30809         
30810         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30811         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30812         
30813         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30814         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30815         this.errorEl.hide();
30816         
30817         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30818         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30819         this.footerEl.hide();
30820         
30821         this.setThumbBoxSize();
30822         
30823         this.bind();
30824         
30825         this.resize();
30826         
30827         this.fireEvent('initial', this);
30828     },
30829
30830     bind : function()
30831     {
30832         var _this = this;
30833         
30834         window.addEventListener("resize", function() { _this.resize(); } );
30835         
30836         this.bodyEl.on('click', this.beforeSelectFile, this);
30837         
30838         if(Roo.isTouch){
30839             this.bodyEl.on('touchstart', this.onTouchStart, this);
30840             this.bodyEl.on('touchmove', this.onTouchMove, this);
30841             this.bodyEl.on('touchend', this.onTouchEnd, this);
30842         }
30843         
30844         if(!Roo.isTouch){
30845             this.bodyEl.on('mousedown', this.onMouseDown, this);
30846             this.bodyEl.on('mousemove', this.onMouseMove, this);
30847             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30848             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30849             Roo.get(document).on('mouseup', this.onMouseUp, this);
30850         }
30851         
30852         this.selectorEl.on('change', this.onFileSelected, this);
30853     },
30854     
30855     reset : function()
30856     {    
30857         this.scale = 0;
30858         this.baseScale = 1;
30859         this.rotate = 0;
30860         this.baseRotate = 1;
30861         this.dragable = false;
30862         this.pinching = false;
30863         this.mouseX = 0;
30864         this.mouseY = 0;
30865         this.cropData = false;
30866         this.notifyEl.dom.innerHTML = this.emptyText;
30867         
30868         this.selectorEl.dom.value = '';
30869         
30870     },
30871     
30872     resize : function()
30873     {
30874         if(this.fireEvent('resize', this) != false){
30875             this.setThumbBoxPosition();
30876             this.setCanvasPosition();
30877         }
30878     },
30879     
30880     onFooterButtonClick : function(e, el, o, type)
30881     {
30882         switch (type) {
30883             case 'rotate-left' :
30884                 this.onRotateLeft(e);
30885                 break;
30886             case 'rotate-right' :
30887                 this.onRotateRight(e);
30888                 break;
30889             case 'picture' :
30890                 this.beforeSelectFile(e);
30891                 break;
30892             case 'trash' :
30893                 this.trash(e);
30894                 break;
30895             case 'crop' :
30896                 this.crop(e);
30897                 break;
30898             case 'download' :
30899                 this.download(e);
30900                 break;
30901             default :
30902                 break;
30903         }
30904         
30905         this.fireEvent('footerbuttonclick', this, type);
30906     },
30907     
30908     beforeSelectFile : function(e)
30909     {
30910         e.preventDefault();
30911         
30912         if(this.fireEvent('beforeselectfile', this) != false){
30913             this.selectorEl.dom.click();
30914         }
30915     },
30916     
30917     onFileSelected : function(e)
30918     {
30919         e.preventDefault();
30920         
30921         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30922             return;
30923         }
30924         
30925         var file = this.selectorEl.dom.files[0];
30926         
30927         if(this.fireEvent('inspect', this, file) != false){
30928             this.prepare(file);
30929         }
30930         
30931     },
30932     
30933     trash : function(e)
30934     {
30935         this.fireEvent('trash', this);
30936     },
30937     
30938     download : function(e)
30939     {
30940         this.fireEvent('download', this);
30941     },
30942     
30943     loadCanvas : function(src)
30944     {   
30945         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30946             
30947             this.reset();
30948             
30949             this.imageEl = document.createElement('img');
30950             
30951             var _this = this;
30952             
30953             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30954             
30955             this.imageEl.src = src;
30956         }
30957     },
30958     
30959     onLoadCanvas : function()
30960     {   
30961         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30962         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30963         
30964         this.bodyEl.un('click', this.beforeSelectFile, this);
30965         
30966         this.notifyEl.hide();
30967         this.thumbEl.show();
30968         this.footerEl.show();
30969         
30970         this.baseRotateLevel();
30971         
30972         if(this.isDocument){
30973             this.setThumbBoxSize();
30974         }
30975         
30976         this.setThumbBoxPosition();
30977         
30978         this.baseScaleLevel();
30979         
30980         this.draw();
30981         
30982         this.resize();
30983         
30984         this.canvasLoaded = true;
30985         
30986         if(this.loadMask){
30987             this.maskEl.unmask();
30988         }
30989         
30990     },
30991     
30992     setCanvasPosition : function()
30993     {   
30994         if(!this.canvasEl){
30995             return;
30996         }
30997         
30998         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30999         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
31000         
31001         this.previewEl.setLeft(pw);
31002         this.previewEl.setTop(ph);
31003         
31004     },
31005     
31006     onMouseDown : function(e)
31007     {   
31008         e.stopEvent();
31009         
31010         this.dragable = true;
31011         this.pinching = false;
31012         
31013         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
31014             this.dragable = false;
31015             return;
31016         }
31017         
31018         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31019         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31020         
31021     },
31022     
31023     onMouseMove : function(e)
31024     {   
31025         e.stopEvent();
31026         
31027         if(!this.canvasLoaded){
31028             return;
31029         }
31030         
31031         if (!this.dragable){
31032             return;
31033         }
31034         
31035         var minX = Math.ceil(this.thumbEl.getLeft(true));
31036         var minY = Math.ceil(this.thumbEl.getTop(true));
31037         
31038         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
31039         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
31040         
31041         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31042         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31043         
31044         x = x - this.mouseX;
31045         y = y - this.mouseY;
31046         
31047         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
31048         var bgY = Math.ceil(y + this.previewEl.getTop(true));
31049         
31050         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
31051         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
31052         
31053         this.previewEl.setLeft(bgX);
31054         this.previewEl.setTop(bgY);
31055         
31056         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
31057         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
31058     },
31059     
31060     onMouseUp : function(e)
31061     {   
31062         e.stopEvent();
31063         
31064         this.dragable = false;
31065     },
31066     
31067     onMouseWheel : function(e)
31068     {   
31069         e.stopEvent();
31070         
31071         this.startScale = this.scale;
31072         
31073         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
31074         
31075         if(!this.zoomable()){
31076             this.scale = this.startScale;
31077             return;
31078         }
31079         
31080         this.draw();
31081         
31082         return;
31083     },
31084     
31085     zoomable : function()
31086     {
31087         var minScale = this.thumbEl.getWidth() / this.minWidth;
31088         
31089         if(this.minWidth < this.minHeight){
31090             minScale = this.thumbEl.getHeight() / this.minHeight;
31091         }
31092         
31093         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
31094         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
31095         
31096         if(
31097                 this.isDocument &&
31098                 (this.rotate == 0 || this.rotate == 180) && 
31099                 (
31100                     width > this.imageEl.OriginWidth || 
31101                     height > this.imageEl.OriginHeight ||
31102                     (width < this.minWidth && height < this.minHeight)
31103                 )
31104         ){
31105             return false;
31106         }
31107         
31108         if(
31109                 this.isDocument &&
31110                 (this.rotate == 90 || this.rotate == 270) && 
31111                 (
31112                     width > this.imageEl.OriginWidth || 
31113                     height > this.imageEl.OriginHeight ||
31114                     (width < this.minHeight && height < this.minWidth)
31115                 )
31116         ){
31117             return false;
31118         }
31119         
31120         if(
31121                 !this.isDocument &&
31122                 (this.rotate == 0 || this.rotate == 180) && 
31123                 (
31124                     width < this.minWidth || 
31125                     width > this.imageEl.OriginWidth || 
31126                     height < this.minHeight || 
31127                     height > this.imageEl.OriginHeight
31128                 )
31129         ){
31130             return false;
31131         }
31132         
31133         if(
31134                 !this.isDocument &&
31135                 (this.rotate == 90 || this.rotate == 270) && 
31136                 (
31137                     width < this.minHeight || 
31138                     width > this.imageEl.OriginWidth || 
31139                     height < this.minWidth || 
31140                     height > this.imageEl.OriginHeight
31141                 )
31142         ){
31143             return false;
31144         }
31145         
31146         return true;
31147         
31148     },
31149     
31150     onRotateLeft : function(e)
31151     {   
31152         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31153             
31154             var minScale = this.thumbEl.getWidth() / this.minWidth;
31155             
31156             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31157             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31158             
31159             this.startScale = this.scale;
31160             
31161             while (this.getScaleLevel() < minScale){
31162             
31163                 this.scale = this.scale + 1;
31164                 
31165                 if(!this.zoomable()){
31166                     break;
31167                 }
31168                 
31169                 if(
31170                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31171                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31172                 ){
31173                     continue;
31174                 }
31175                 
31176                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31177
31178                 this.draw();
31179                 
31180                 return;
31181             }
31182             
31183             this.scale = this.startScale;
31184             
31185             this.onRotateFail();
31186             
31187             return false;
31188         }
31189         
31190         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
31191
31192         if(this.isDocument){
31193             this.setThumbBoxSize();
31194             this.setThumbBoxPosition();
31195             this.setCanvasPosition();
31196         }
31197         
31198         this.draw();
31199         
31200         this.fireEvent('rotate', this, 'left');
31201         
31202     },
31203     
31204     onRotateRight : function(e)
31205     {
31206         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
31207             
31208             var minScale = this.thumbEl.getWidth() / this.minWidth;
31209         
31210             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
31211             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
31212             
31213             this.startScale = this.scale;
31214             
31215             while (this.getScaleLevel() < minScale){
31216             
31217                 this.scale = this.scale + 1;
31218                 
31219                 if(!this.zoomable()){
31220                     break;
31221                 }
31222                 
31223                 if(
31224                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
31225                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
31226                 ){
31227                     continue;
31228                 }
31229                 
31230                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31231
31232                 this.draw();
31233                 
31234                 return;
31235             }
31236             
31237             this.scale = this.startScale;
31238             
31239             this.onRotateFail();
31240             
31241             return false;
31242         }
31243         
31244         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
31245
31246         if(this.isDocument){
31247             this.setThumbBoxSize();
31248             this.setThumbBoxPosition();
31249             this.setCanvasPosition();
31250         }
31251         
31252         this.draw();
31253         
31254         this.fireEvent('rotate', this, 'right');
31255     },
31256     
31257     onRotateFail : function()
31258     {
31259         this.errorEl.show(true);
31260         
31261         var _this = this;
31262         
31263         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
31264     },
31265     
31266     draw : function()
31267     {
31268         this.previewEl.dom.innerHTML = '';
31269         
31270         var canvasEl = document.createElement("canvas");
31271         
31272         var contextEl = canvasEl.getContext("2d");
31273         
31274         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31275         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31276         var center = this.imageEl.OriginWidth / 2;
31277         
31278         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
31279             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31280             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31281             center = this.imageEl.OriginHeight / 2;
31282         }
31283         
31284         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
31285         
31286         contextEl.translate(center, center);
31287         contextEl.rotate(this.rotate * Math.PI / 180);
31288
31289         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31290         
31291         this.canvasEl = document.createElement("canvas");
31292         
31293         this.contextEl = this.canvasEl.getContext("2d");
31294         
31295         switch (this.rotate) {
31296             case 0 :
31297                 
31298                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31299                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
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 90 : 
31305                 
31306                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31307                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31308                 
31309                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31310                     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);
31311                     break;
31312                 }
31313                 
31314                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31315                 
31316                 break;
31317             case 180 :
31318                 
31319                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
31320                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
31321                 
31322                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31323                     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);
31324                     break;
31325                 }
31326                 
31327                 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);
31328                 
31329                 break;
31330             case 270 :
31331                 
31332                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
31333                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
31334         
31335                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31336                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
31337                     break;
31338                 }
31339                 
31340                 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);
31341                 
31342                 break;
31343             default : 
31344                 break;
31345         }
31346         
31347         this.previewEl.appendChild(this.canvasEl);
31348         
31349         this.setCanvasPosition();
31350     },
31351     
31352     crop : function()
31353     {
31354         if(!this.canvasLoaded){
31355             return;
31356         }
31357         
31358         var imageCanvas = document.createElement("canvas");
31359         
31360         var imageContext = imageCanvas.getContext("2d");
31361         
31362         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31363         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
31364         
31365         var center = imageCanvas.width / 2;
31366         
31367         imageContext.translate(center, center);
31368         
31369         imageContext.rotate(this.rotate * Math.PI / 180);
31370         
31371         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
31372         
31373         var canvas = document.createElement("canvas");
31374         
31375         var context = canvas.getContext("2d");
31376                 
31377         canvas.width = this.minWidth;
31378         canvas.height = this.minHeight;
31379
31380         switch (this.rotate) {
31381             case 0 :
31382                 
31383                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31384                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31385                 
31386                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31387                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31388                 
31389                 var targetWidth = this.minWidth - 2 * x;
31390                 var targetHeight = this.minHeight - 2 * y;
31391                 
31392                 var scale = 1;
31393                 
31394                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31395                     scale = targetWidth / width;
31396                 }
31397                 
31398                 if(x > 0 && y == 0){
31399                     scale = targetHeight / height;
31400                 }
31401                 
31402                 if(x > 0 && y > 0){
31403                     scale = targetWidth / width;
31404                     
31405                     if(width < height){
31406                         scale = targetHeight / height;
31407                     }
31408                 }
31409                 
31410                 context.scale(scale, scale);
31411                 
31412                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31413                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31414
31415                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31416                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31417
31418                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31419                 
31420                 break;
31421             case 90 : 
31422                 
31423                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31424                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31425                 
31426                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31427                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31428                 
31429                 var targetWidth = this.minWidth - 2 * x;
31430                 var targetHeight = this.minHeight - 2 * y;
31431                 
31432                 var scale = 1;
31433                 
31434                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31435                     scale = targetWidth / width;
31436                 }
31437                 
31438                 if(x > 0 && y == 0){
31439                     scale = targetHeight / height;
31440                 }
31441                 
31442                 if(x > 0 && y > 0){
31443                     scale = targetWidth / width;
31444                     
31445                     if(width < height){
31446                         scale = targetHeight / height;
31447                     }
31448                 }
31449                 
31450                 context.scale(scale, scale);
31451                 
31452                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31453                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31454
31455                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31456                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31457                 
31458                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31459                 
31460                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31461                 
31462                 break;
31463             case 180 :
31464                 
31465                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
31466                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31467                 
31468                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31469                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31470                 
31471                 var targetWidth = this.minWidth - 2 * x;
31472                 var targetHeight = this.minHeight - 2 * y;
31473                 
31474                 var scale = 1;
31475                 
31476                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31477                     scale = targetWidth / width;
31478                 }
31479                 
31480                 if(x > 0 && y == 0){
31481                     scale = targetHeight / height;
31482                 }
31483                 
31484                 if(x > 0 && y > 0){
31485                     scale = targetWidth / width;
31486                     
31487                     if(width < height){
31488                         scale = targetHeight / height;
31489                     }
31490                 }
31491                 
31492                 context.scale(scale, scale);
31493                 
31494                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31495                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31496
31497                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31498                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31499
31500                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31501                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31502                 
31503                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31504                 
31505                 break;
31506             case 270 :
31507                 
31508                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31509                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31510                 
31511                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31512                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31513                 
31514                 var targetWidth = this.minWidth - 2 * x;
31515                 var targetHeight = this.minHeight - 2 * y;
31516                 
31517                 var scale = 1;
31518                 
31519                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31520                     scale = targetWidth / width;
31521                 }
31522                 
31523                 if(x > 0 && y == 0){
31524                     scale = targetHeight / height;
31525                 }
31526                 
31527                 if(x > 0 && y > 0){
31528                     scale = targetWidth / width;
31529                     
31530                     if(width < height){
31531                         scale = targetHeight / height;
31532                     }
31533                 }
31534                 
31535                 context.scale(scale, scale);
31536                 
31537                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31538                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31539
31540                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31541                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31542                 
31543                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31544                 
31545                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31546                 
31547                 break;
31548             default : 
31549                 break;
31550         }
31551         
31552         this.cropData = canvas.toDataURL(this.cropType);
31553         
31554         if(this.fireEvent('crop', this, this.cropData) !== false){
31555             this.process(this.file, this.cropData);
31556         }
31557         
31558         return;
31559         
31560     },
31561     
31562     setThumbBoxSize : function()
31563     {
31564         var width, height;
31565         
31566         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31567             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31568             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31569             
31570             this.minWidth = width;
31571             this.minHeight = height;
31572             
31573             if(this.rotate == 90 || this.rotate == 270){
31574                 this.minWidth = height;
31575                 this.minHeight = width;
31576             }
31577         }
31578         
31579         height = 300;
31580         width = Math.ceil(this.minWidth * height / this.minHeight);
31581         
31582         if(this.minWidth > this.minHeight){
31583             width = 300;
31584             height = Math.ceil(this.minHeight * width / this.minWidth);
31585         }
31586         
31587         this.thumbEl.setStyle({
31588             width : width + 'px',
31589             height : height + 'px'
31590         });
31591
31592         return;
31593             
31594     },
31595     
31596     setThumbBoxPosition : function()
31597     {
31598         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31599         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31600         
31601         this.thumbEl.setLeft(x);
31602         this.thumbEl.setTop(y);
31603         
31604     },
31605     
31606     baseRotateLevel : function()
31607     {
31608         this.baseRotate = 1;
31609         
31610         if(
31611                 typeof(this.exif) != 'undefined' &&
31612                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31613                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31614         ){
31615             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31616         }
31617         
31618         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31619         
31620     },
31621     
31622     baseScaleLevel : function()
31623     {
31624         var width, height;
31625         
31626         if(this.isDocument){
31627             
31628             if(this.baseRotate == 6 || this.baseRotate == 8){
31629             
31630                 height = this.thumbEl.getHeight();
31631                 this.baseScale = height / this.imageEl.OriginWidth;
31632
31633                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31634                     width = this.thumbEl.getWidth();
31635                     this.baseScale = width / this.imageEl.OriginHeight;
31636                 }
31637
31638                 return;
31639             }
31640
31641             height = this.thumbEl.getHeight();
31642             this.baseScale = height / this.imageEl.OriginHeight;
31643
31644             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31645                 width = this.thumbEl.getWidth();
31646                 this.baseScale = width / this.imageEl.OriginWidth;
31647             }
31648
31649             return;
31650         }
31651         
31652         if(this.baseRotate == 6 || this.baseRotate == 8){
31653             
31654             width = this.thumbEl.getHeight();
31655             this.baseScale = width / this.imageEl.OriginHeight;
31656             
31657             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31658                 height = this.thumbEl.getWidth();
31659                 this.baseScale = height / this.imageEl.OriginHeight;
31660             }
31661             
31662             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31663                 height = this.thumbEl.getWidth();
31664                 this.baseScale = height / this.imageEl.OriginHeight;
31665                 
31666                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31667                     width = this.thumbEl.getHeight();
31668                     this.baseScale = width / this.imageEl.OriginWidth;
31669                 }
31670             }
31671             
31672             return;
31673         }
31674         
31675         width = this.thumbEl.getWidth();
31676         this.baseScale = width / this.imageEl.OriginWidth;
31677         
31678         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31679             height = this.thumbEl.getHeight();
31680             this.baseScale = height / this.imageEl.OriginHeight;
31681         }
31682         
31683         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31684             
31685             height = this.thumbEl.getHeight();
31686             this.baseScale = height / this.imageEl.OriginHeight;
31687             
31688             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31689                 width = this.thumbEl.getWidth();
31690                 this.baseScale = width / this.imageEl.OriginWidth;
31691             }
31692             
31693         }
31694         
31695         return;
31696     },
31697     
31698     getScaleLevel : function()
31699     {
31700         return this.baseScale * Math.pow(1.1, this.scale);
31701     },
31702     
31703     onTouchStart : function(e)
31704     {
31705         if(!this.canvasLoaded){
31706             this.beforeSelectFile(e);
31707             return;
31708         }
31709         
31710         var touches = e.browserEvent.touches;
31711         
31712         if(!touches){
31713             return;
31714         }
31715         
31716         if(touches.length == 1){
31717             this.onMouseDown(e);
31718             return;
31719         }
31720         
31721         if(touches.length != 2){
31722             return;
31723         }
31724         
31725         var coords = [];
31726         
31727         for(var i = 0, finger; finger = touches[i]; i++){
31728             coords.push(finger.pageX, finger.pageY);
31729         }
31730         
31731         var x = Math.pow(coords[0] - coords[2], 2);
31732         var y = Math.pow(coords[1] - coords[3], 2);
31733         
31734         this.startDistance = Math.sqrt(x + y);
31735         
31736         this.startScale = this.scale;
31737         
31738         this.pinching = true;
31739         this.dragable = false;
31740         
31741     },
31742     
31743     onTouchMove : function(e)
31744     {
31745         if(!this.pinching && !this.dragable){
31746             return;
31747         }
31748         
31749         var touches = e.browserEvent.touches;
31750         
31751         if(!touches){
31752             return;
31753         }
31754         
31755         if(this.dragable){
31756             this.onMouseMove(e);
31757             return;
31758         }
31759         
31760         var coords = [];
31761         
31762         for(var i = 0, finger; finger = touches[i]; i++){
31763             coords.push(finger.pageX, finger.pageY);
31764         }
31765         
31766         var x = Math.pow(coords[0] - coords[2], 2);
31767         var y = Math.pow(coords[1] - coords[3], 2);
31768         
31769         this.endDistance = Math.sqrt(x + y);
31770         
31771         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31772         
31773         if(!this.zoomable()){
31774             this.scale = this.startScale;
31775             return;
31776         }
31777         
31778         this.draw();
31779         
31780     },
31781     
31782     onTouchEnd : function(e)
31783     {
31784         this.pinching = false;
31785         this.dragable = false;
31786         
31787     },
31788     
31789     process : function(file, crop)
31790     {
31791         if(this.loadMask){
31792             this.maskEl.mask(this.loadingText);
31793         }
31794         
31795         this.xhr = new XMLHttpRequest();
31796         
31797         file.xhr = this.xhr;
31798
31799         this.xhr.open(this.method, this.url, true);
31800         
31801         var headers = {
31802             "Accept": "application/json",
31803             "Cache-Control": "no-cache",
31804             "X-Requested-With": "XMLHttpRequest"
31805         };
31806         
31807         for (var headerName in headers) {
31808             var headerValue = headers[headerName];
31809             if (headerValue) {
31810                 this.xhr.setRequestHeader(headerName, headerValue);
31811             }
31812         }
31813         
31814         var _this = this;
31815         
31816         this.xhr.onload = function()
31817         {
31818             _this.xhrOnLoad(_this.xhr);
31819         }
31820         
31821         this.xhr.onerror = function()
31822         {
31823             _this.xhrOnError(_this.xhr);
31824         }
31825         
31826         var formData = new FormData();
31827
31828         formData.append('returnHTML', 'NO');
31829         
31830         if(crop){
31831             formData.append('crop', crop);
31832         }
31833         
31834         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31835             formData.append(this.paramName, file, file.name);
31836         }
31837         
31838         if(typeof(file.filename) != 'undefined'){
31839             formData.append('filename', file.filename);
31840         }
31841         
31842         if(typeof(file.mimetype) != 'undefined'){
31843             formData.append('mimetype', file.mimetype);
31844         }
31845         
31846         if(this.fireEvent('arrange', this, formData) != false){
31847             this.xhr.send(formData);
31848         };
31849     },
31850     
31851     xhrOnLoad : function(xhr)
31852     {
31853         if(this.loadMask){
31854             this.maskEl.unmask();
31855         }
31856         
31857         if (xhr.readyState !== 4) {
31858             this.fireEvent('exception', this, xhr);
31859             return;
31860         }
31861
31862         var response = Roo.decode(xhr.responseText);
31863         
31864         if(!response.success){
31865             this.fireEvent('exception', this, xhr);
31866             return;
31867         }
31868         
31869         var response = Roo.decode(xhr.responseText);
31870         
31871         this.fireEvent('upload', this, response);
31872         
31873     },
31874     
31875     xhrOnError : function()
31876     {
31877         if(this.loadMask){
31878             this.maskEl.unmask();
31879         }
31880         
31881         Roo.log('xhr on error');
31882         
31883         var response = Roo.decode(xhr.responseText);
31884           
31885         Roo.log(response);
31886         
31887     },
31888     
31889     prepare : function(file)
31890     {   
31891         if(this.loadMask){
31892             this.maskEl.mask(this.loadingText);
31893         }
31894         
31895         this.file = false;
31896         this.exif = {};
31897         
31898         if(typeof(file) === 'string'){
31899             this.loadCanvas(file);
31900             return;
31901         }
31902         
31903         if(!file || !this.urlAPI){
31904             return;
31905         }
31906         
31907         this.file = file;
31908         this.cropType = file.type;
31909         
31910         var _this = this;
31911         
31912         if(this.fireEvent('prepare', this, this.file) != false){
31913             
31914             var reader = new FileReader();
31915             
31916             reader.onload = function (e) {
31917                 if (e.target.error) {
31918                     Roo.log(e.target.error);
31919                     return;
31920                 }
31921                 
31922                 var buffer = e.target.result,
31923                     dataView = new DataView(buffer),
31924                     offset = 2,
31925                     maxOffset = dataView.byteLength - 4,
31926                     markerBytes,
31927                     markerLength;
31928                 
31929                 if (dataView.getUint16(0) === 0xffd8) {
31930                     while (offset < maxOffset) {
31931                         markerBytes = dataView.getUint16(offset);
31932                         
31933                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31934                             markerLength = dataView.getUint16(offset + 2) + 2;
31935                             if (offset + markerLength > dataView.byteLength) {
31936                                 Roo.log('Invalid meta data: Invalid segment size.');
31937                                 break;
31938                             }
31939                             
31940                             if(markerBytes == 0xffe1){
31941                                 _this.parseExifData(
31942                                     dataView,
31943                                     offset,
31944                                     markerLength
31945                                 );
31946                             }
31947                             
31948                             offset += markerLength;
31949                             
31950                             continue;
31951                         }
31952                         
31953                         break;
31954                     }
31955                     
31956                 }
31957                 
31958                 var url = _this.urlAPI.createObjectURL(_this.file);
31959                 
31960                 _this.loadCanvas(url);
31961                 
31962                 return;
31963             }
31964             
31965             reader.readAsArrayBuffer(this.file);
31966             
31967         }
31968         
31969     },
31970     
31971     parseExifData : function(dataView, offset, length)
31972     {
31973         var tiffOffset = offset + 10,
31974             littleEndian,
31975             dirOffset;
31976     
31977         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31978             // No Exif data, might be XMP data instead
31979             return;
31980         }
31981         
31982         // Check for the ASCII code for "Exif" (0x45786966):
31983         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31984             // No Exif data, might be XMP data instead
31985             return;
31986         }
31987         if (tiffOffset + 8 > dataView.byteLength) {
31988             Roo.log('Invalid Exif data: Invalid segment size.');
31989             return;
31990         }
31991         // Check for the two null bytes:
31992         if (dataView.getUint16(offset + 8) !== 0x0000) {
31993             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31994             return;
31995         }
31996         // Check the byte alignment:
31997         switch (dataView.getUint16(tiffOffset)) {
31998         case 0x4949:
31999             littleEndian = true;
32000             break;
32001         case 0x4D4D:
32002             littleEndian = false;
32003             break;
32004         default:
32005             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
32006             return;
32007         }
32008         // Check for the TIFF tag marker (0x002A):
32009         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
32010             Roo.log('Invalid Exif data: Missing TIFF marker.');
32011             return;
32012         }
32013         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
32014         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
32015         
32016         this.parseExifTags(
32017             dataView,
32018             tiffOffset,
32019             tiffOffset + dirOffset,
32020             littleEndian
32021         );
32022     },
32023     
32024     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
32025     {
32026         var tagsNumber,
32027             dirEndOffset,
32028             i;
32029         if (dirOffset + 6 > dataView.byteLength) {
32030             Roo.log('Invalid Exif data: Invalid directory offset.');
32031             return;
32032         }
32033         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
32034         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
32035         if (dirEndOffset + 4 > dataView.byteLength) {
32036             Roo.log('Invalid Exif data: Invalid directory size.');
32037             return;
32038         }
32039         for (i = 0; i < tagsNumber; i += 1) {
32040             this.parseExifTag(
32041                 dataView,
32042                 tiffOffset,
32043                 dirOffset + 2 + 12 * i, // tag offset
32044                 littleEndian
32045             );
32046         }
32047         // Return the offset to the next directory:
32048         return dataView.getUint32(dirEndOffset, littleEndian);
32049     },
32050     
32051     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
32052     {
32053         var tag = dataView.getUint16(offset, littleEndian);
32054         
32055         this.exif[tag] = this.getExifValue(
32056             dataView,
32057             tiffOffset,
32058             offset,
32059             dataView.getUint16(offset + 2, littleEndian), // tag type
32060             dataView.getUint32(offset + 4, littleEndian), // tag length
32061             littleEndian
32062         );
32063     },
32064     
32065     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
32066     {
32067         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
32068             tagSize,
32069             dataOffset,
32070             values,
32071             i,
32072             str,
32073             c;
32074     
32075         if (!tagType) {
32076             Roo.log('Invalid Exif data: Invalid tag type.');
32077             return;
32078         }
32079         
32080         tagSize = tagType.size * length;
32081         // Determine if the value is contained in the dataOffset bytes,
32082         // or if the value at the dataOffset is a pointer to the actual data:
32083         dataOffset = tagSize > 4 ?
32084                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
32085         if (dataOffset + tagSize > dataView.byteLength) {
32086             Roo.log('Invalid Exif data: Invalid data offset.');
32087             return;
32088         }
32089         if (length === 1) {
32090             return tagType.getValue(dataView, dataOffset, littleEndian);
32091         }
32092         values = [];
32093         for (i = 0; i < length; i += 1) {
32094             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
32095         }
32096         
32097         if (tagType.ascii) {
32098             str = '';
32099             // Concatenate the chars:
32100             for (i = 0; i < values.length; i += 1) {
32101                 c = values[i];
32102                 // Ignore the terminating NULL byte(s):
32103                 if (c === '\u0000') {
32104                     break;
32105                 }
32106                 str += c;
32107             }
32108             return str;
32109         }
32110         return values;
32111     }
32112     
32113 });
32114
32115 Roo.apply(Roo.bootstrap.UploadCropbox, {
32116     tags : {
32117         'Orientation': 0x0112
32118     },
32119     
32120     Orientation: {
32121             1: 0, //'top-left',
32122 //            2: 'top-right',
32123             3: 180, //'bottom-right',
32124 //            4: 'bottom-left',
32125 //            5: 'left-top',
32126             6: 90, //'right-top',
32127 //            7: 'right-bottom',
32128             8: 270 //'left-bottom'
32129     },
32130     
32131     exifTagTypes : {
32132         // byte, 8-bit unsigned int:
32133         1: {
32134             getValue: function (dataView, dataOffset) {
32135                 return dataView.getUint8(dataOffset);
32136             },
32137             size: 1
32138         },
32139         // ascii, 8-bit byte:
32140         2: {
32141             getValue: function (dataView, dataOffset) {
32142                 return String.fromCharCode(dataView.getUint8(dataOffset));
32143             },
32144             size: 1,
32145             ascii: true
32146         },
32147         // short, 16 bit int:
32148         3: {
32149             getValue: function (dataView, dataOffset, littleEndian) {
32150                 return dataView.getUint16(dataOffset, littleEndian);
32151             },
32152             size: 2
32153         },
32154         // long, 32 bit int:
32155         4: {
32156             getValue: function (dataView, dataOffset, littleEndian) {
32157                 return dataView.getUint32(dataOffset, littleEndian);
32158             },
32159             size: 4
32160         },
32161         // rational = two long values, first is numerator, second is denominator:
32162         5: {
32163             getValue: function (dataView, dataOffset, littleEndian) {
32164                 return dataView.getUint32(dataOffset, littleEndian) /
32165                     dataView.getUint32(dataOffset + 4, littleEndian);
32166             },
32167             size: 8
32168         },
32169         // slong, 32 bit signed int:
32170         9: {
32171             getValue: function (dataView, dataOffset, littleEndian) {
32172                 return dataView.getInt32(dataOffset, littleEndian);
32173             },
32174             size: 4
32175         },
32176         // srational, two slongs, first is numerator, second is denominator:
32177         10: {
32178             getValue: function (dataView, dataOffset, littleEndian) {
32179                 return dataView.getInt32(dataOffset, littleEndian) /
32180                     dataView.getInt32(dataOffset + 4, littleEndian);
32181             },
32182             size: 8
32183         }
32184     },
32185     
32186     footer : {
32187         STANDARD : [
32188             {
32189                 tag : 'div',
32190                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32191                 action : 'rotate-left',
32192                 cn : [
32193                     {
32194                         tag : 'button',
32195                         cls : 'btn btn-default',
32196                         html : '<i class="fa fa-undo"></i>'
32197                     }
32198                 ]
32199             },
32200             {
32201                 tag : 'div',
32202                 cls : 'btn-group roo-upload-cropbox-picture',
32203                 action : 'picture',
32204                 cn : [
32205                     {
32206                         tag : 'button',
32207                         cls : 'btn btn-default',
32208                         html : '<i class="fa fa-picture-o"></i>'
32209                     }
32210                 ]
32211             },
32212             {
32213                 tag : 'div',
32214                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32215                 action : 'rotate-right',
32216                 cn : [
32217                     {
32218                         tag : 'button',
32219                         cls : 'btn btn-default',
32220                         html : '<i class="fa fa-repeat"></i>'
32221                     }
32222                 ]
32223             }
32224         ],
32225         DOCUMENT : [
32226             {
32227                 tag : 'div',
32228                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32229                 action : 'rotate-left',
32230                 cn : [
32231                     {
32232                         tag : 'button',
32233                         cls : 'btn btn-default',
32234                         html : '<i class="fa fa-undo"></i>'
32235                     }
32236                 ]
32237             },
32238             {
32239                 tag : 'div',
32240                 cls : 'btn-group roo-upload-cropbox-download',
32241                 action : 'download',
32242                 cn : [
32243                     {
32244                         tag : 'button',
32245                         cls : 'btn btn-default',
32246                         html : '<i class="fa fa-download"></i>'
32247                     }
32248                 ]
32249             },
32250             {
32251                 tag : 'div',
32252                 cls : 'btn-group roo-upload-cropbox-crop',
32253                 action : 'crop',
32254                 cn : [
32255                     {
32256                         tag : 'button',
32257                         cls : 'btn btn-default',
32258                         html : '<i class="fa fa-crop"></i>'
32259                     }
32260                 ]
32261             },
32262             {
32263                 tag : 'div',
32264                 cls : 'btn-group roo-upload-cropbox-trash',
32265                 action : 'trash',
32266                 cn : [
32267                     {
32268                         tag : 'button',
32269                         cls : 'btn btn-default',
32270                         html : '<i class="fa fa-trash"></i>'
32271                     }
32272                 ]
32273             },
32274             {
32275                 tag : 'div',
32276                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32277                 action : 'rotate-right',
32278                 cn : [
32279                     {
32280                         tag : 'button',
32281                         cls : 'btn btn-default',
32282                         html : '<i class="fa fa-repeat"></i>'
32283                     }
32284                 ]
32285             }
32286         ],
32287         ROTATOR : [
32288             {
32289                 tag : 'div',
32290                 cls : 'btn-group roo-upload-cropbox-rotate-left',
32291                 action : 'rotate-left',
32292                 cn : [
32293                     {
32294                         tag : 'button',
32295                         cls : 'btn btn-default',
32296                         html : '<i class="fa fa-undo"></i>'
32297                     }
32298                 ]
32299             },
32300             {
32301                 tag : 'div',
32302                 cls : 'btn-group roo-upload-cropbox-rotate-right',
32303                 action : 'rotate-right',
32304                 cn : [
32305                     {
32306                         tag : 'button',
32307                         cls : 'btn btn-default',
32308                         html : '<i class="fa fa-repeat"></i>'
32309                     }
32310                 ]
32311             }
32312         ]
32313     }
32314 });
32315
32316 /*
32317 * Licence: LGPL
32318 */
32319
32320 /**
32321  * @class Roo.bootstrap.DocumentManager
32322  * @extends Roo.bootstrap.Component
32323  * Bootstrap DocumentManager class
32324  * @cfg {String} paramName default 'imageUpload'
32325  * @cfg {String} toolTipName default 'filename'
32326  * @cfg {String} method default POST
32327  * @cfg {String} url action url
32328  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
32329  * @cfg {Boolean} multiple multiple upload default true
32330  * @cfg {Number} thumbSize default 300
32331  * @cfg {String} fieldLabel
32332  * @cfg {Number} labelWidth default 4
32333  * @cfg {String} labelAlign (left|top) default left
32334  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
32335 * @cfg {Number} labellg set the width of label (1-12)
32336  * @cfg {Number} labelmd set the width of label (1-12)
32337  * @cfg {Number} labelsm set the width of label (1-12)
32338  * @cfg {Number} labelxs set the width of label (1-12)
32339  * 
32340  * @constructor
32341  * Create a new DocumentManager
32342  * @param {Object} config The config object
32343  */
32344
32345 Roo.bootstrap.DocumentManager = function(config){
32346     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
32347     
32348     this.files = [];
32349     this.delegates = [];
32350     
32351     this.addEvents({
32352         /**
32353          * @event initial
32354          * Fire when initial the DocumentManager
32355          * @param {Roo.bootstrap.DocumentManager} this
32356          */
32357         "initial" : true,
32358         /**
32359          * @event inspect
32360          * inspect selected file
32361          * @param {Roo.bootstrap.DocumentManager} this
32362          * @param {File} file
32363          */
32364         "inspect" : true,
32365         /**
32366          * @event exception
32367          * Fire when xhr load exception
32368          * @param {Roo.bootstrap.DocumentManager} this
32369          * @param {XMLHttpRequest} xhr
32370          */
32371         "exception" : true,
32372         /**
32373          * @event afterupload
32374          * Fire when xhr load exception
32375          * @param {Roo.bootstrap.DocumentManager} this
32376          * @param {XMLHttpRequest} xhr
32377          */
32378         "afterupload" : true,
32379         /**
32380          * @event prepare
32381          * prepare the form data
32382          * @param {Roo.bootstrap.DocumentManager} this
32383          * @param {Object} formData
32384          */
32385         "prepare" : true,
32386         /**
32387          * @event remove
32388          * Fire when remove the file
32389          * @param {Roo.bootstrap.DocumentManager} this
32390          * @param {Object} file
32391          */
32392         "remove" : true,
32393         /**
32394          * @event refresh
32395          * Fire after refresh the file
32396          * @param {Roo.bootstrap.DocumentManager} this
32397          */
32398         "refresh" : true,
32399         /**
32400          * @event click
32401          * Fire after click the image
32402          * @param {Roo.bootstrap.DocumentManager} this
32403          * @param {Object} file
32404          */
32405         "click" : true,
32406         /**
32407          * @event edit
32408          * Fire when upload a image and editable set to true
32409          * @param {Roo.bootstrap.DocumentManager} this
32410          * @param {Object} file
32411          */
32412         "edit" : true,
32413         /**
32414          * @event beforeselectfile
32415          * Fire before select file
32416          * @param {Roo.bootstrap.DocumentManager} this
32417          */
32418         "beforeselectfile" : true,
32419         /**
32420          * @event process
32421          * Fire before process file
32422          * @param {Roo.bootstrap.DocumentManager} this
32423          * @param {Object} file
32424          */
32425         "process" : true,
32426         /**
32427          * @event previewrendered
32428          * Fire when preview rendered
32429          * @param {Roo.bootstrap.DocumentManager} this
32430          * @param {Object} file
32431          */
32432         "previewrendered" : true,
32433         /**
32434          */
32435         "previewResize" : true
32436         
32437     });
32438 };
32439
32440 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
32441     
32442     boxes : 0,
32443     inputName : '',
32444     thumbSize : 300,
32445     multiple : true,
32446     files : false,
32447     method : 'POST',
32448     url : '',
32449     paramName : 'imageUpload',
32450     toolTipName : 'filename',
32451     fieldLabel : '',
32452     labelWidth : 4,
32453     labelAlign : 'left',
32454     editable : true,
32455     delegates : false,
32456     xhr : false, 
32457     
32458     labellg : 0,
32459     labelmd : 0,
32460     labelsm : 0,
32461     labelxs : 0,
32462     
32463     getAutoCreate : function()
32464     {   
32465         var managerWidget = {
32466             tag : 'div',
32467             cls : 'roo-document-manager',
32468             cn : [
32469                 {
32470                     tag : 'input',
32471                     cls : 'roo-document-manager-selector',
32472                     type : 'file'
32473                 },
32474                 {
32475                     tag : 'div',
32476                     cls : 'roo-document-manager-uploader',
32477                     cn : [
32478                         {
32479                             tag : 'div',
32480                             cls : 'roo-document-manager-upload-btn',
32481                             html : '<i class="fa fa-plus"></i>'
32482                         }
32483                     ]
32484                     
32485                 }
32486             ]
32487         };
32488         
32489         var content = [
32490             {
32491                 tag : 'div',
32492                 cls : 'column col-md-12',
32493                 cn : managerWidget
32494             }
32495         ];
32496         
32497         if(this.fieldLabel.length){
32498             
32499             content = [
32500                 {
32501                     tag : 'div',
32502                     cls : 'column col-md-12',
32503                     html : this.fieldLabel
32504                 },
32505                 {
32506                     tag : 'div',
32507                     cls : 'column col-md-12',
32508                     cn : managerWidget
32509                 }
32510             ];
32511
32512             if(this.labelAlign == 'left'){
32513                 content = [
32514                     {
32515                         tag : 'div',
32516                         cls : 'column',
32517                         html : this.fieldLabel
32518                     },
32519                     {
32520                         tag : 'div',
32521                         cls : 'column',
32522                         cn : managerWidget
32523                     }
32524                 ];
32525                 
32526                 if(this.labelWidth > 12){
32527                     content[0].style = "width: " + this.labelWidth + 'px';
32528                 }
32529
32530                 if(this.labelWidth < 13 && this.labelmd == 0){
32531                     this.labelmd = this.labelWidth;
32532                 }
32533
32534                 if(this.labellg > 0){
32535                     content[0].cls += ' col-lg-' + this.labellg;
32536                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32537                 }
32538
32539                 if(this.labelmd > 0){
32540                     content[0].cls += ' col-md-' + this.labelmd;
32541                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32542                 }
32543
32544                 if(this.labelsm > 0){
32545                     content[0].cls += ' col-sm-' + this.labelsm;
32546                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32547                 }
32548
32549                 if(this.labelxs > 0){
32550                     content[0].cls += ' col-xs-' + this.labelxs;
32551                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32552                 }
32553                 
32554             }
32555         }
32556         
32557         var cfg = {
32558             tag : 'div',
32559             cls : 'row clearfix',
32560             cn : content
32561         };
32562         
32563         return cfg;
32564         
32565     },
32566     
32567     initEvents : function()
32568     {
32569         this.managerEl = this.el.select('.roo-document-manager', true).first();
32570         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32571         
32572         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32573         this.selectorEl.hide();
32574         
32575         if(this.multiple){
32576             this.selectorEl.attr('multiple', 'multiple');
32577         }
32578         
32579         this.selectorEl.on('change', this.onFileSelected, this);
32580         
32581         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32582         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32583         
32584         this.uploader.on('click', this.onUploaderClick, this);
32585         
32586         this.renderProgressDialog();
32587         
32588         var _this = this;
32589         
32590         window.addEventListener("resize", function() { _this.refresh(); } );
32591         
32592         this.fireEvent('initial', this);
32593     },
32594     
32595     renderProgressDialog : function()
32596     {
32597         var _this = this;
32598         
32599         this.progressDialog = new Roo.bootstrap.Modal({
32600             cls : 'roo-document-manager-progress-dialog',
32601             allow_close : false,
32602             animate : false,
32603             title : '',
32604             buttons : [
32605                 {
32606                     name  :'cancel',
32607                     weight : 'danger',
32608                     html : 'Cancel'
32609                 }
32610             ], 
32611             listeners : { 
32612                 btnclick : function() {
32613                     _this.uploadCancel();
32614                     this.hide();
32615                 }
32616             }
32617         });
32618          
32619         this.progressDialog.render(Roo.get(document.body));
32620          
32621         this.progress = new Roo.bootstrap.Progress({
32622             cls : 'roo-document-manager-progress',
32623             active : true,
32624             striped : true
32625         });
32626         
32627         this.progress.render(this.progressDialog.getChildContainer());
32628         
32629         this.progressBar = new Roo.bootstrap.ProgressBar({
32630             cls : 'roo-document-manager-progress-bar',
32631             aria_valuenow : 0,
32632             aria_valuemin : 0,
32633             aria_valuemax : 12,
32634             panel : 'success'
32635         });
32636         
32637         this.progressBar.render(this.progress.getChildContainer());
32638     },
32639     
32640     onUploaderClick : function(e)
32641     {
32642         e.preventDefault();
32643      
32644         if(this.fireEvent('beforeselectfile', this) != false){
32645             this.selectorEl.dom.click();
32646         }
32647         
32648     },
32649     
32650     onFileSelected : function(e)
32651     {
32652         e.preventDefault();
32653         
32654         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32655             return;
32656         }
32657         
32658         Roo.each(this.selectorEl.dom.files, function(file){
32659             if(this.fireEvent('inspect', this, file) != false){
32660                 this.files.push(file);
32661             }
32662         }, this);
32663         
32664         this.queue();
32665         
32666     },
32667     
32668     queue : function()
32669     {
32670         this.selectorEl.dom.value = '';
32671         
32672         if(!this.files || !this.files.length){
32673             return;
32674         }
32675         
32676         if(this.boxes > 0 && this.files.length > this.boxes){
32677             this.files = this.files.slice(0, this.boxes);
32678         }
32679         
32680         this.uploader.show();
32681         
32682         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32683             this.uploader.hide();
32684         }
32685         
32686         var _this = this;
32687         
32688         var files = [];
32689         
32690         var docs = [];
32691         
32692         Roo.each(this.files, function(file){
32693             
32694             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32695                 var f = this.renderPreview(file);
32696                 files.push(f);
32697                 return;
32698             }
32699             
32700             if(file.type.indexOf('image') != -1){
32701                 this.delegates.push(
32702                     (function(){
32703                         _this.process(file);
32704                     }).createDelegate(this)
32705                 );
32706         
32707                 return;
32708             }
32709             
32710             docs.push(
32711                 (function(){
32712                     _this.process(file);
32713                 }).createDelegate(this)
32714             );
32715             
32716         }, this);
32717         
32718         this.files = files;
32719         
32720         this.delegates = this.delegates.concat(docs);
32721         
32722         if(!this.delegates.length){
32723             this.refresh();
32724             return;
32725         }
32726         
32727         this.progressBar.aria_valuemax = this.delegates.length;
32728         
32729         this.arrange();
32730         
32731         return;
32732     },
32733     
32734     arrange : function()
32735     {
32736         if(!this.delegates.length){
32737             this.progressDialog.hide();
32738             this.refresh();
32739             return;
32740         }
32741         
32742         var delegate = this.delegates.shift();
32743         
32744         this.progressDialog.show();
32745         
32746         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32747         
32748         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32749         
32750         delegate();
32751     },
32752     
32753     refresh : function()
32754     {
32755         this.uploader.show();
32756         
32757         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32758             this.uploader.hide();
32759         }
32760         
32761         Roo.isTouch ? this.closable(false) : this.closable(true);
32762         
32763         this.fireEvent('refresh', this);
32764     },
32765     
32766     onRemove : function(e, el, o)
32767     {
32768         e.preventDefault();
32769         
32770         this.fireEvent('remove', this, o);
32771         
32772     },
32773     
32774     remove : function(o)
32775     {
32776         var files = [];
32777         
32778         Roo.each(this.files, function(file){
32779             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32780                 files.push(file);
32781                 return;
32782             }
32783
32784             o.target.remove();
32785
32786         }, this);
32787         
32788         this.files = files;
32789         
32790         this.refresh();
32791     },
32792     
32793     clear : function()
32794     {
32795         Roo.each(this.files, function(file){
32796             if(!file.target){
32797                 return;
32798             }
32799             
32800             file.target.remove();
32801
32802         }, this);
32803         
32804         this.files = [];
32805         
32806         this.refresh();
32807     },
32808     
32809     onClick : function(e, el, o)
32810     {
32811         e.preventDefault();
32812         
32813         this.fireEvent('click', this, o);
32814         
32815     },
32816     
32817     closable : function(closable)
32818     {
32819         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32820             
32821             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32822             
32823             if(closable){
32824                 el.show();
32825                 return;
32826             }
32827             
32828             el.hide();
32829             
32830         }, this);
32831     },
32832     
32833     xhrOnLoad : function(xhr)
32834     {
32835         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32836             el.remove();
32837         }, this);
32838         
32839         if (xhr.readyState !== 4) {
32840             this.arrange();
32841             this.fireEvent('exception', this, xhr);
32842             return;
32843         }
32844
32845         var response = Roo.decode(xhr.responseText);
32846         
32847         if(!response.success){
32848             this.arrange();
32849             this.fireEvent('exception', this, xhr);
32850             return;
32851         }
32852         
32853         var file = this.renderPreview(response.data);
32854         
32855         this.files.push(file);
32856         
32857         this.arrange();
32858         
32859         this.fireEvent('afterupload', this, xhr);
32860         
32861     },
32862     
32863     xhrOnError : function(xhr)
32864     {
32865         Roo.log('xhr on error');
32866         
32867         var response = Roo.decode(xhr.responseText);
32868           
32869         Roo.log(response);
32870         
32871         this.arrange();
32872     },
32873     
32874     process : function(file)
32875     {
32876         if(this.fireEvent('process', this, file) !== false){
32877             if(this.editable && file.type.indexOf('image') != -1){
32878                 this.fireEvent('edit', this, file);
32879                 return;
32880             }
32881
32882             this.uploadStart(file, false);
32883
32884             return;
32885         }
32886         
32887     },
32888     
32889     uploadStart : function(file, crop)
32890     {
32891         this.xhr = new XMLHttpRequest();
32892         
32893         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32894             this.arrange();
32895             return;
32896         }
32897         
32898         file.xhr = this.xhr;
32899             
32900         this.managerEl.createChild({
32901             tag : 'div',
32902             cls : 'roo-document-manager-loading',
32903             cn : [
32904                 {
32905                     tag : 'div',
32906                     tooltip : file.name,
32907                     cls : 'roo-document-manager-thumb',
32908                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32909                 }
32910             ]
32911
32912         });
32913
32914         this.xhr.open(this.method, this.url, true);
32915         
32916         var headers = {
32917             "Accept": "application/json",
32918             "Cache-Control": "no-cache",
32919             "X-Requested-With": "XMLHttpRequest"
32920         };
32921         
32922         for (var headerName in headers) {
32923             var headerValue = headers[headerName];
32924             if (headerValue) {
32925                 this.xhr.setRequestHeader(headerName, headerValue);
32926             }
32927         }
32928         
32929         var _this = this;
32930         
32931         this.xhr.onload = function()
32932         {
32933             _this.xhrOnLoad(_this.xhr);
32934         }
32935         
32936         this.xhr.onerror = function()
32937         {
32938             _this.xhrOnError(_this.xhr);
32939         }
32940         
32941         var formData = new FormData();
32942
32943         formData.append('returnHTML', 'NO');
32944         
32945         if(crop){
32946             formData.append('crop', crop);
32947         }
32948         
32949         formData.append(this.paramName, file, file.name);
32950         
32951         var options = {
32952             file : file, 
32953             manually : false
32954         };
32955         
32956         if(this.fireEvent('prepare', this, formData, options) != false){
32957             
32958             if(options.manually){
32959                 return;
32960             }
32961             
32962             this.xhr.send(formData);
32963             return;
32964         };
32965         
32966         this.uploadCancel();
32967     },
32968     
32969     uploadCancel : function()
32970     {
32971         if (this.xhr) {
32972             this.xhr.abort();
32973         }
32974         
32975         this.delegates = [];
32976         
32977         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32978             el.remove();
32979         }, this);
32980         
32981         this.arrange();
32982     },
32983     
32984     renderPreview : function(file)
32985     {
32986         if(typeof(file.target) != 'undefined' && file.target){
32987             return file;
32988         }
32989         
32990         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32991         
32992         var previewEl = this.managerEl.createChild({
32993             tag : 'div',
32994             cls : 'roo-document-manager-preview',
32995             cn : [
32996                 {
32997                     tag : 'div',
32998                     tooltip : file[this.toolTipName],
32999                     cls : 'roo-document-manager-thumb',
33000                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
33001                 },
33002                 {
33003                     tag : 'button',
33004                     cls : 'close',
33005                     html : '<i class="fa fa-times-circle"></i>'
33006                 }
33007             ]
33008         });
33009
33010         var close = previewEl.select('button.close', true).first();
33011
33012         close.on('click', this.onRemove, this, file);
33013
33014         file.target = previewEl;
33015
33016         var image = previewEl.select('img', true).first();
33017         
33018         var _this = this;
33019         
33020         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
33021         
33022         image.on('click', this.onClick, this, file);
33023         
33024         this.fireEvent('previewrendered', this, file);
33025         
33026         return file;
33027         
33028     },
33029     
33030     onPreviewLoad : function(file, image)
33031     {
33032         if(typeof(file.target) == 'undefined' || !file.target){
33033             return;
33034         }
33035         
33036         var width = image.dom.naturalWidth || image.dom.width;
33037         var height = image.dom.naturalHeight || image.dom.height;
33038         
33039         if(!this.previewResize) {
33040             return;
33041         }
33042         
33043         if(width > height){
33044             file.target.addClass('wide');
33045             return;
33046         }
33047         
33048         file.target.addClass('tall');
33049         return;
33050         
33051     },
33052     
33053     uploadFromSource : function(file, crop)
33054     {
33055         this.xhr = new XMLHttpRequest();
33056         
33057         this.managerEl.createChild({
33058             tag : 'div',
33059             cls : 'roo-document-manager-loading',
33060             cn : [
33061                 {
33062                     tag : 'div',
33063                     tooltip : file.name,
33064                     cls : 'roo-document-manager-thumb',
33065                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
33066                 }
33067             ]
33068
33069         });
33070
33071         this.xhr.open(this.method, this.url, true);
33072         
33073         var headers = {
33074             "Accept": "application/json",
33075             "Cache-Control": "no-cache",
33076             "X-Requested-With": "XMLHttpRequest"
33077         };
33078         
33079         for (var headerName in headers) {
33080             var headerValue = headers[headerName];
33081             if (headerValue) {
33082                 this.xhr.setRequestHeader(headerName, headerValue);
33083             }
33084         }
33085         
33086         var _this = this;
33087         
33088         this.xhr.onload = function()
33089         {
33090             _this.xhrOnLoad(_this.xhr);
33091         }
33092         
33093         this.xhr.onerror = function()
33094         {
33095             _this.xhrOnError(_this.xhr);
33096         }
33097         
33098         var formData = new FormData();
33099
33100         formData.append('returnHTML', 'NO');
33101         
33102         formData.append('crop', crop);
33103         
33104         if(typeof(file.filename) != 'undefined'){
33105             formData.append('filename', file.filename);
33106         }
33107         
33108         if(typeof(file.mimetype) != 'undefined'){
33109             formData.append('mimetype', file.mimetype);
33110         }
33111         
33112         Roo.log(formData);
33113         
33114         if(this.fireEvent('prepare', this, formData) != false){
33115             this.xhr.send(formData);
33116         };
33117     }
33118 });
33119
33120 /*
33121 * Licence: LGPL
33122 */
33123
33124 /**
33125  * @class Roo.bootstrap.DocumentViewer
33126  * @extends Roo.bootstrap.Component
33127  * Bootstrap DocumentViewer class
33128  * @cfg {Boolean} showDownload (true|false) show download button (default true)
33129  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
33130  * 
33131  * @constructor
33132  * Create a new DocumentViewer
33133  * @param {Object} config The config object
33134  */
33135
33136 Roo.bootstrap.DocumentViewer = function(config){
33137     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
33138     
33139     this.addEvents({
33140         /**
33141          * @event initial
33142          * Fire after initEvent
33143          * @param {Roo.bootstrap.DocumentViewer} this
33144          */
33145         "initial" : true,
33146         /**
33147          * @event click
33148          * Fire after click
33149          * @param {Roo.bootstrap.DocumentViewer} this
33150          */
33151         "click" : true,
33152         /**
33153          * @event download
33154          * Fire after download button
33155          * @param {Roo.bootstrap.DocumentViewer} this
33156          */
33157         "download" : true,
33158         /**
33159          * @event trash
33160          * Fire after trash button
33161          * @param {Roo.bootstrap.DocumentViewer} this
33162          */
33163         "trash" : true
33164         
33165     });
33166 };
33167
33168 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
33169     
33170     showDownload : true,
33171     
33172     showTrash : true,
33173     
33174     getAutoCreate : function()
33175     {
33176         var cfg = {
33177             tag : 'div',
33178             cls : 'roo-document-viewer',
33179             cn : [
33180                 {
33181                     tag : 'div',
33182                     cls : 'roo-document-viewer-body',
33183                     cn : [
33184                         {
33185                             tag : 'div',
33186                             cls : 'roo-document-viewer-thumb',
33187                             cn : [
33188                                 {
33189                                     tag : 'img',
33190                                     cls : 'roo-document-viewer-image'
33191                                 }
33192                             ]
33193                         }
33194                     ]
33195                 },
33196                 {
33197                     tag : 'div',
33198                     cls : 'roo-document-viewer-footer',
33199                     cn : {
33200                         tag : 'div',
33201                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
33202                         cn : [
33203                             {
33204                                 tag : 'div',
33205                                 cls : 'btn-group roo-document-viewer-download',
33206                                 cn : [
33207                                     {
33208                                         tag : 'button',
33209                                         cls : 'btn btn-default',
33210                                         html : '<i class="fa fa-download"></i>'
33211                                     }
33212                                 ]
33213                             },
33214                             {
33215                                 tag : 'div',
33216                                 cls : 'btn-group roo-document-viewer-trash',
33217                                 cn : [
33218                                     {
33219                                         tag : 'button',
33220                                         cls : 'btn btn-default',
33221                                         html : '<i class="fa fa-trash"></i>'
33222                                     }
33223                                 ]
33224                             }
33225                         ]
33226                     }
33227                 }
33228             ]
33229         };
33230         
33231         return cfg;
33232     },
33233     
33234     initEvents : function()
33235     {
33236         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
33237         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
33238         
33239         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
33240         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
33241         
33242         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
33243         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
33244         
33245         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
33246         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
33247         
33248         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
33249         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
33250         
33251         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
33252         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
33253         
33254         this.bodyEl.on('click', this.onClick, this);
33255         this.downloadBtn.on('click', this.onDownload, this);
33256         this.trashBtn.on('click', this.onTrash, this);
33257         
33258         this.downloadBtn.hide();
33259         this.trashBtn.hide();
33260         
33261         if(this.showDownload){
33262             this.downloadBtn.show();
33263         }
33264         
33265         if(this.showTrash){
33266             this.trashBtn.show();
33267         }
33268         
33269         if(!this.showDownload && !this.showTrash) {
33270             this.footerEl.hide();
33271         }
33272         
33273     },
33274     
33275     initial : function()
33276     {
33277         this.fireEvent('initial', this);
33278         
33279     },
33280     
33281     onClick : function(e)
33282     {
33283         e.preventDefault();
33284         
33285         this.fireEvent('click', this);
33286     },
33287     
33288     onDownload : function(e)
33289     {
33290         e.preventDefault();
33291         
33292         this.fireEvent('download', this);
33293     },
33294     
33295     onTrash : function(e)
33296     {
33297         e.preventDefault();
33298         
33299         this.fireEvent('trash', this);
33300     }
33301     
33302 });
33303 /*
33304  * - LGPL
33305  *
33306  * nav progress bar
33307  * 
33308  */
33309
33310 /**
33311  * @class Roo.bootstrap.NavProgressBar
33312  * @extends Roo.bootstrap.Component
33313  * Bootstrap NavProgressBar class
33314  * 
33315  * @constructor
33316  * Create a new nav progress bar
33317  * @param {Object} config The config object
33318  */
33319
33320 Roo.bootstrap.NavProgressBar = function(config){
33321     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
33322
33323     this.bullets = this.bullets || [];
33324    
33325 //    Roo.bootstrap.NavProgressBar.register(this);
33326      this.addEvents({
33327         /**
33328              * @event changed
33329              * Fires when the active item changes
33330              * @param {Roo.bootstrap.NavProgressBar} this
33331              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
33332              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
33333          */
33334         'changed': true
33335      });
33336     
33337 };
33338
33339 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
33340     
33341     bullets : [],
33342     barItems : [],
33343     
33344     getAutoCreate : function()
33345     {
33346         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
33347         
33348         cfg = {
33349             tag : 'div',
33350             cls : 'roo-navigation-bar-group',
33351             cn : [
33352                 {
33353                     tag : 'div',
33354                     cls : 'roo-navigation-top-bar'
33355                 },
33356                 {
33357                     tag : 'div',
33358                     cls : 'roo-navigation-bullets-bar',
33359                     cn : [
33360                         {
33361                             tag : 'ul',
33362                             cls : 'roo-navigation-bar'
33363                         }
33364                     ]
33365                 },
33366                 
33367                 {
33368                     tag : 'div',
33369                     cls : 'roo-navigation-bottom-bar'
33370                 }
33371             ]
33372             
33373         };
33374         
33375         return cfg;
33376         
33377     },
33378     
33379     initEvents: function() 
33380     {
33381         
33382     },
33383     
33384     onRender : function(ct, position) 
33385     {
33386         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33387         
33388         if(this.bullets.length){
33389             Roo.each(this.bullets, function(b){
33390                this.addItem(b);
33391             }, this);
33392         }
33393         
33394         this.format();
33395         
33396     },
33397     
33398     addItem : function(cfg)
33399     {
33400         var item = new Roo.bootstrap.NavProgressItem(cfg);
33401         
33402         item.parentId = this.id;
33403         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
33404         
33405         if(cfg.html){
33406             var top = new Roo.bootstrap.Element({
33407                 tag : 'div',
33408                 cls : 'roo-navigation-bar-text'
33409             });
33410             
33411             var bottom = new Roo.bootstrap.Element({
33412                 tag : 'div',
33413                 cls : 'roo-navigation-bar-text'
33414             });
33415             
33416             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
33417             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
33418             
33419             var topText = new Roo.bootstrap.Element({
33420                 tag : 'span',
33421                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
33422             });
33423             
33424             var bottomText = new Roo.bootstrap.Element({
33425                 tag : 'span',
33426                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
33427             });
33428             
33429             topText.onRender(top.el, null);
33430             bottomText.onRender(bottom.el, null);
33431             
33432             item.topEl = top;
33433             item.bottomEl = bottom;
33434         }
33435         
33436         this.barItems.push(item);
33437         
33438         return item;
33439     },
33440     
33441     getActive : function()
33442     {
33443         var active = false;
33444         
33445         Roo.each(this.barItems, function(v){
33446             
33447             if (!v.isActive()) {
33448                 return;
33449             }
33450             
33451             active = v;
33452             return false;
33453             
33454         });
33455         
33456         return active;
33457     },
33458     
33459     setActiveItem : function(item)
33460     {
33461         var prev = false;
33462         
33463         Roo.each(this.barItems, function(v){
33464             if (v.rid == item.rid) {
33465                 return ;
33466             }
33467             
33468             if (v.isActive()) {
33469                 v.setActive(false);
33470                 prev = v;
33471             }
33472         });
33473
33474         item.setActive(true);
33475         
33476         this.fireEvent('changed', this, item, prev);
33477     },
33478     
33479     getBarItem: function(rid)
33480     {
33481         var ret = false;
33482         
33483         Roo.each(this.barItems, function(e) {
33484             if (e.rid != rid) {
33485                 return;
33486             }
33487             
33488             ret =  e;
33489             return false;
33490         });
33491         
33492         return ret;
33493     },
33494     
33495     indexOfItem : function(item)
33496     {
33497         var index = false;
33498         
33499         Roo.each(this.barItems, function(v, i){
33500             
33501             if (v.rid != item.rid) {
33502                 return;
33503             }
33504             
33505             index = i;
33506             return false
33507         });
33508         
33509         return index;
33510     },
33511     
33512     setActiveNext : function()
33513     {
33514         var i = this.indexOfItem(this.getActive());
33515         
33516         if (i > this.barItems.length) {
33517             return;
33518         }
33519         
33520         this.setActiveItem(this.barItems[i+1]);
33521     },
33522     
33523     setActivePrev : function()
33524     {
33525         var i = this.indexOfItem(this.getActive());
33526         
33527         if (i  < 1) {
33528             return;
33529         }
33530         
33531         this.setActiveItem(this.barItems[i-1]);
33532     },
33533     
33534     format : function()
33535     {
33536         if(!this.barItems.length){
33537             return;
33538         }
33539      
33540         var width = 100 / this.barItems.length;
33541         
33542         Roo.each(this.barItems, function(i){
33543             i.el.setStyle('width', width + '%');
33544             i.topEl.el.setStyle('width', width + '%');
33545             i.bottomEl.el.setStyle('width', width + '%');
33546         }, this);
33547         
33548     }
33549     
33550 });
33551 /*
33552  * - LGPL
33553  *
33554  * Nav Progress Item
33555  * 
33556  */
33557
33558 /**
33559  * @class Roo.bootstrap.NavProgressItem
33560  * @extends Roo.bootstrap.Component
33561  * Bootstrap NavProgressItem class
33562  * @cfg {String} rid the reference id
33563  * @cfg {Boolean} active (true|false) Is item active default false
33564  * @cfg {Boolean} disabled (true|false) Is item active default false
33565  * @cfg {String} html
33566  * @cfg {String} position (top|bottom) text position default bottom
33567  * @cfg {String} icon show icon instead of number
33568  * 
33569  * @constructor
33570  * Create a new NavProgressItem
33571  * @param {Object} config The config object
33572  */
33573 Roo.bootstrap.NavProgressItem = function(config){
33574     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33575     this.addEvents({
33576         // raw events
33577         /**
33578          * @event click
33579          * The raw click event for the entire grid.
33580          * @param {Roo.bootstrap.NavProgressItem} this
33581          * @param {Roo.EventObject} e
33582          */
33583         "click" : true
33584     });
33585    
33586 };
33587
33588 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33589     
33590     rid : '',
33591     active : false,
33592     disabled : false,
33593     html : '',
33594     position : 'bottom',
33595     icon : false,
33596     
33597     getAutoCreate : function()
33598     {
33599         var iconCls = 'roo-navigation-bar-item-icon';
33600         
33601         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33602         
33603         var cfg = {
33604             tag: 'li',
33605             cls: 'roo-navigation-bar-item',
33606             cn : [
33607                 {
33608                     tag : 'i',
33609                     cls : iconCls
33610                 }
33611             ]
33612         };
33613         
33614         if(this.active){
33615             cfg.cls += ' active';
33616         }
33617         if(this.disabled){
33618             cfg.cls += ' disabled';
33619         }
33620         
33621         return cfg;
33622     },
33623     
33624     disable : function()
33625     {
33626         this.setDisabled(true);
33627     },
33628     
33629     enable : function()
33630     {
33631         this.setDisabled(false);
33632     },
33633     
33634     initEvents: function() 
33635     {
33636         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33637         
33638         this.iconEl.on('click', this.onClick, this);
33639     },
33640     
33641     onClick : function(e)
33642     {
33643         e.preventDefault();
33644         
33645         if(this.disabled){
33646             return;
33647         }
33648         
33649         if(this.fireEvent('click', this, e) === false){
33650             return;
33651         };
33652         
33653         this.parent().setActiveItem(this);
33654     },
33655     
33656     isActive: function () 
33657     {
33658         return this.active;
33659     },
33660     
33661     setActive : function(state)
33662     {
33663         if(this.active == state){
33664             return;
33665         }
33666         
33667         this.active = state;
33668         
33669         if (state) {
33670             this.el.addClass('active');
33671             return;
33672         }
33673         
33674         this.el.removeClass('active');
33675         
33676         return;
33677     },
33678     
33679     setDisabled : function(state)
33680     {
33681         if(this.disabled == state){
33682             return;
33683         }
33684         
33685         this.disabled = state;
33686         
33687         if (state) {
33688             this.el.addClass('disabled');
33689             return;
33690         }
33691         
33692         this.el.removeClass('disabled');
33693     },
33694     
33695     tooltipEl : function()
33696     {
33697         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33698     }
33699 });
33700  
33701
33702  /*
33703  * - LGPL
33704  *
33705  * FieldLabel
33706  * 
33707  */
33708
33709 /**
33710  * @class Roo.bootstrap.FieldLabel
33711  * @extends Roo.bootstrap.Component
33712  * Bootstrap FieldLabel class
33713  * @cfg {String} html contents of the element
33714  * @cfg {String} tag tag of the element default label
33715  * @cfg {String} cls class of the element
33716  * @cfg {String} target label target 
33717  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33718  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33719  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33720  * @cfg {String} iconTooltip default "This field is required"
33721  * @cfg {String} indicatorpos (left|right) default left
33722  * 
33723  * @constructor
33724  * Create a new FieldLabel
33725  * @param {Object} config The config object
33726  */
33727
33728 Roo.bootstrap.FieldLabel = function(config){
33729     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33730     
33731     this.addEvents({
33732             /**
33733              * @event invalid
33734              * Fires after the field has been marked as invalid.
33735              * @param {Roo.form.FieldLabel} this
33736              * @param {String} msg The validation message
33737              */
33738             invalid : true,
33739             /**
33740              * @event valid
33741              * Fires after the field has been validated with no errors.
33742              * @param {Roo.form.FieldLabel} this
33743              */
33744             valid : true
33745         });
33746 };
33747
33748 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33749     
33750     tag: 'label',
33751     cls: '',
33752     html: '',
33753     target: '',
33754     allowBlank : true,
33755     invalidClass : 'has-warning',
33756     validClass : 'has-success',
33757     iconTooltip : 'This field is required',
33758     indicatorpos : 'left',
33759     
33760     getAutoCreate : function(){
33761         
33762         var cls = "";
33763         if (!this.allowBlank) {
33764             cls  = "visible";
33765         }
33766         
33767         var cfg = {
33768             tag : this.tag,
33769             cls : 'roo-bootstrap-field-label ' + this.cls,
33770             for : this.target,
33771             cn : [
33772                 {
33773                     tag : 'i',
33774                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33775                     tooltip : this.iconTooltip
33776                 },
33777                 {
33778                     tag : 'span',
33779                     html : this.html
33780                 }
33781             ] 
33782         };
33783         
33784         if(this.indicatorpos == 'right'){
33785             var cfg = {
33786                 tag : this.tag,
33787                 cls : 'roo-bootstrap-field-label ' + this.cls,
33788                 for : this.target,
33789                 cn : [
33790                     {
33791                         tag : 'span',
33792                         html : this.html
33793                     },
33794                     {
33795                         tag : 'i',
33796                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33797                         tooltip : this.iconTooltip
33798                     }
33799                 ] 
33800             };
33801         }
33802         
33803         return cfg;
33804     },
33805     
33806     initEvents: function() 
33807     {
33808         Roo.bootstrap.Element.superclass.initEvents.call(this);
33809         
33810         this.indicator = this.indicatorEl();
33811         
33812         if(this.indicator){
33813             this.indicator.removeClass('visible');
33814             this.indicator.addClass('invisible');
33815         }
33816         
33817         Roo.bootstrap.FieldLabel.register(this);
33818     },
33819     
33820     indicatorEl : function()
33821     {
33822         var indicator = this.el.select('i.roo-required-indicator',true).first();
33823         
33824         if(!indicator){
33825             return false;
33826         }
33827         
33828         return indicator;
33829         
33830     },
33831     
33832     /**
33833      * Mark this field as valid
33834      */
33835     markValid : function()
33836     {
33837         if(this.indicator){
33838             this.indicator.removeClass('visible');
33839             this.indicator.addClass('invisible');
33840         }
33841         if (Roo.bootstrap.version == 3) {
33842             this.el.removeClass(this.invalidClass);
33843             this.el.addClass(this.validClass);
33844         } else {
33845             this.el.removeClass('is-invalid');
33846             this.el.addClass('is-valid');
33847         }
33848         
33849         
33850         this.fireEvent('valid', this);
33851     },
33852     
33853     /**
33854      * Mark this field as invalid
33855      * @param {String} msg The validation message
33856      */
33857     markInvalid : function(msg)
33858     {
33859         if(this.indicator){
33860             this.indicator.removeClass('invisible');
33861             this.indicator.addClass('visible');
33862         }
33863           if (Roo.bootstrap.version == 3) {
33864             this.el.removeClass(this.validClass);
33865             this.el.addClass(this.invalidClass);
33866         } else {
33867             this.el.removeClass('is-valid');
33868             this.el.addClass('is-invalid');
33869         }
33870         
33871         
33872         this.fireEvent('invalid', this, msg);
33873     }
33874     
33875    
33876 });
33877
33878 Roo.apply(Roo.bootstrap.FieldLabel, {
33879     
33880     groups: {},
33881     
33882      /**
33883     * register a FieldLabel Group
33884     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33885     */
33886     register : function(label)
33887     {
33888         if(this.groups.hasOwnProperty(label.target)){
33889             return;
33890         }
33891      
33892         this.groups[label.target] = label;
33893         
33894     },
33895     /**
33896     * fetch a FieldLabel Group based on the target
33897     * @param {string} target
33898     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33899     */
33900     get: function(target) {
33901         if (typeof(this.groups[target]) == 'undefined') {
33902             return false;
33903         }
33904         
33905         return this.groups[target] ;
33906     }
33907 });
33908
33909  
33910
33911  /*
33912  * - LGPL
33913  *
33914  * page DateSplitField.
33915  * 
33916  */
33917
33918
33919 /**
33920  * @class Roo.bootstrap.DateSplitField
33921  * @extends Roo.bootstrap.Component
33922  * Bootstrap DateSplitField class
33923  * @cfg {string} fieldLabel - the label associated
33924  * @cfg {Number} labelWidth set the width of label (0-12)
33925  * @cfg {String} labelAlign (top|left)
33926  * @cfg {Boolean} dayAllowBlank (true|false) default false
33927  * @cfg {Boolean} monthAllowBlank (true|false) default false
33928  * @cfg {Boolean} yearAllowBlank (true|false) default false
33929  * @cfg {string} dayPlaceholder 
33930  * @cfg {string} monthPlaceholder
33931  * @cfg {string} yearPlaceholder
33932  * @cfg {string} dayFormat default 'd'
33933  * @cfg {string} monthFormat default 'm'
33934  * @cfg {string} yearFormat default 'Y'
33935  * @cfg {Number} labellg set the width of label (1-12)
33936  * @cfg {Number} labelmd set the width of label (1-12)
33937  * @cfg {Number} labelsm set the width of label (1-12)
33938  * @cfg {Number} labelxs set the width of label (1-12)
33939
33940  *     
33941  * @constructor
33942  * Create a new DateSplitField
33943  * @param {Object} config The config object
33944  */
33945
33946 Roo.bootstrap.DateSplitField = function(config){
33947     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33948     
33949     this.addEvents({
33950         // raw events
33951          /**
33952          * @event years
33953          * getting the data of years
33954          * @param {Roo.bootstrap.DateSplitField} this
33955          * @param {Object} years
33956          */
33957         "years" : true,
33958         /**
33959          * @event days
33960          * getting the data of days
33961          * @param {Roo.bootstrap.DateSplitField} this
33962          * @param {Object} days
33963          */
33964         "days" : true,
33965         /**
33966          * @event invalid
33967          * Fires after the field has been marked as invalid.
33968          * @param {Roo.form.Field} this
33969          * @param {String} msg The validation message
33970          */
33971         invalid : true,
33972        /**
33973          * @event valid
33974          * Fires after the field has been validated with no errors.
33975          * @param {Roo.form.Field} this
33976          */
33977         valid : true
33978     });
33979 };
33980
33981 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33982     
33983     fieldLabel : '',
33984     labelAlign : 'top',
33985     labelWidth : 3,
33986     dayAllowBlank : false,
33987     monthAllowBlank : false,
33988     yearAllowBlank : false,
33989     dayPlaceholder : '',
33990     monthPlaceholder : '',
33991     yearPlaceholder : '',
33992     dayFormat : 'd',
33993     monthFormat : 'm',
33994     yearFormat : 'Y',
33995     isFormField : true,
33996     labellg : 0,
33997     labelmd : 0,
33998     labelsm : 0,
33999     labelxs : 0,
34000     
34001     getAutoCreate : function()
34002     {
34003         var cfg = {
34004             tag : 'div',
34005             cls : 'row roo-date-split-field-group',
34006             cn : [
34007                 {
34008                     tag : 'input',
34009                     type : 'hidden',
34010                     cls : 'form-hidden-field roo-date-split-field-group-value',
34011                     name : this.name
34012                 }
34013             ]
34014         };
34015         
34016         var labelCls = 'col-md-12';
34017         var contentCls = 'col-md-4';
34018         
34019         if(this.fieldLabel){
34020             
34021             var label = {
34022                 tag : 'div',
34023                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
34024                 cn : [
34025                     {
34026                         tag : 'label',
34027                         html : this.fieldLabel
34028                     }
34029                 ]
34030             };
34031             
34032             if(this.labelAlign == 'left'){
34033             
34034                 if(this.labelWidth > 12){
34035                     label.style = "width: " + this.labelWidth + 'px';
34036                 }
34037
34038                 if(this.labelWidth < 13 && this.labelmd == 0){
34039                     this.labelmd = this.labelWidth;
34040                 }
34041
34042                 if(this.labellg > 0){
34043                     labelCls = ' col-lg-' + this.labellg;
34044                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
34045                 }
34046
34047                 if(this.labelmd > 0){
34048                     labelCls = ' col-md-' + this.labelmd;
34049                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
34050                 }
34051
34052                 if(this.labelsm > 0){
34053                     labelCls = ' col-sm-' + this.labelsm;
34054                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
34055                 }
34056
34057                 if(this.labelxs > 0){
34058                     labelCls = ' col-xs-' + this.labelxs;
34059                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
34060                 }
34061             }
34062             
34063             label.cls += ' ' + labelCls;
34064             
34065             cfg.cn.push(label);
34066         }
34067         
34068         Roo.each(['day', 'month', 'year'], function(t){
34069             cfg.cn.push({
34070                 tag : 'div',
34071                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
34072             });
34073         }, this);
34074         
34075         return cfg;
34076     },
34077     
34078     inputEl: function ()
34079     {
34080         return this.el.select('.roo-date-split-field-group-value', true).first();
34081     },
34082     
34083     onRender : function(ct, position) 
34084     {
34085         var _this = this;
34086         
34087         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
34088         
34089         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
34090         
34091         this.dayField = new Roo.bootstrap.ComboBox({
34092             allowBlank : this.dayAllowBlank,
34093             alwaysQuery : true,
34094             displayField : 'value',
34095             editable : false,
34096             fieldLabel : '',
34097             forceSelection : true,
34098             mode : 'local',
34099             placeholder : this.dayPlaceholder,
34100             selectOnFocus : true,
34101             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34102             triggerAction : 'all',
34103             typeAhead : true,
34104             valueField : 'value',
34105             store : new Roo.data.SimpleStore({
34106                 data : (function() {    
34107                     var days = [];
34108                     _this.fireEvent('days', _this, days);
34109                     return days;
34110                 })(),
34111                 fields : [ 'value' ]
34112             }),
34113             listeners : {
34114                 select : function (_self, record, index)
34115                 {
34116                     _this.setValue(_this.getValue());
34117                 }
34118             }
34119         });
34120
34121         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
34122         
34123         this.monthField = new Roo.bootstrap.MonthField({
34124             after : '<i class=\"fa fa-calendar\"></i>',
34125             allowBlank : this.monthAllowBlank,
34126             placeholder : this.monthPlaceholder,
34127             readOnly : true,
34128             listeners : {
34129                 render : function (_self)
34130                 {
34131                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
34132                         e.preventDefault();
34133                         _self.focus();
34134                     });
34135                 },
34136                 select : function (_self, oldvalue, newvalue)
34137                 {
34138                     _this.setValue(_this.getValue());
34139                 }
34140             }
34141         });
34142         
34143         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
34144         
34145         this.yearField = new Roo.bootstrap.ComboBox({
34146             allowBlank : this.yearAllowBlank,
34147             alwaysQuery : true,
34148             displayField : 'value',
34149             editable : false,
34150             fieldLabel : '',
34151             forceSelection : true,
34152             mode : 'local',
34153             placeholder : this.yearPlaceholder,
34154             selectOnFocus : true,
34155             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
34156             triggerAction : 'all',
34157             typeAhead : true,
34158             valueField : 'value',
34159             store : new Roo.data.SimpleStore({
34160                 data : (function() {
34161                     var years = [];
34162                     _this.fireEvent('years', _this, years);
34163                     return years;
34164                 })(),
34165                 fields : [ 'value' ]
34166             }),
34167             listeners : {
34168                 select : function (_self, record, index)
34169                 {
34170                     _this.setValue(_this.getValue());
34171                 }
34172             }
34173         });
34174
34175         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
34176     },
34177     
34178     setValue : function(v, format)
34179     {
34180         this.inputEl.dom.value = v;
34181         
34182         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
34183         
34184         var d = Date.parseDate(v, f);
34185         
34186         if(!d){
34187             this.validate();
34188             return;
34189         }
34190         
34191         this.setDay(d.format(this.dayFormat));
34192         this.setMonth(d.format(this.monthFormat));
34193         this.setYear(d.format(this.yearFormat));
34194         
34195         this.validate();
34196         
34197         return;
34198     },
34199     
34200     setDay : function(v)
34201     {
34202         this.dayField.setValue(v);
34203         this.inputEl.dom.value = this.getValue();
34204         this.validate();
34205         return;
34206     },
34207     
34208     setMonth : function(v)
34209     {
34210         this.monthField.setValue(v, true);
34211         this.inputEl.dom.value = this.getValue();
34212         this.validate();
34213         return;
34214     },
34215     
34216     setYear : function(v)
34217     {
34218         this.yearField.setValue(v);
34219         this.inputEl.dom.value = this.getValue();
34220         this.validate();
34221         return;
34222     },
34223     
34224     getDay : function()
34225     {
34226         return this.dayField.getValue();
34227     },
34228     
34229     getMonth : function()
34230     {
34231         return this.monthField.getValue();
34232     },
34233     
34234     getYear : function()
34235     {
34236         return this.yearField.getValue();
34237     },
34238     
34239     getValue : function()
34240     {
34241         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
34242         
34243         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
34244         
34245         return date;
34246     },
34247     
34248     reset : function()
34249     {
34250         this.setDay('');
34251         this.setMonth('');
34252         this.setYear('');
34253         this.inputEl.dom.value = '';
34254         this.validate();
34255         return;
34256     },
34257     
34258     validate : function()
34259     {
34260         var d = this.dayField.validate();
34261         var m = this.monthField.validate();
34262         var y = this.yearField.validate();
34263         
34264         var valid = true;
34265         
34266         if(
34267                 (!this.dayAllowBlank && !d) ||
34268                 (!this.monthAllowBlank && !m) ||
34269                 (!this.yearAllowBlank && !y)
34270         ){
34271             valid = false;
34272         }
34273         
34274         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
34275             return valid;
34276         }
34277         
34278         if(valid){
34279             this.markValid();
34280             return valid;
34281         }
34282         
34283         this.markInvalid();
34284         
34285         return valid;
34286     },
34287     
34288     markValid : function()
34289     {
34290         
34291         var label = this.el.select('label', true).first();
34292         var icon = this.el.select('i.fa-star', true).first();
34293
34294         if(label && icon){
34295             icon.remove();
34296         }
34297         
34298         this.fireEvent('valid', this);
34299     },
34300     
34301      /**
34302      * Mark this field as invalid
34303      * @param {String} msg The validation message
34304      */
34305     markInvalid : function(msg)
34306     {
34307         
34308         var label = this.el.select('label', true).first();
34309         var icon = this.el.select('i.fa-star', true).first();
34310
34311         if(label && !icon){
34312             this.el.select('.roo-date-split-field-label', true).createChild({
34313                 tag : 'i',
34314                 cls : 'text-danger fa fa-lg fa-star',
34315                 tooltip : 'This field is required',
34316                 style : 'margin-right:5px;'
34317             }, label, true);
34318         }
34319         
34320         this.fireEvent('invalid', this, msg);
34321     },
34322     
34323     clearInvalid : function()
34324     {
34325         var label = this.el.select('label', true).first();
34326         var icon = this.el.select('i.fa-star', true).first();
34327
34328         if(label && icon){
34329             icon.remove();
34330         }
34331         
34332         this.fireEvent('valid', this);
34333     },
34334     
34335     getName: function()
34336     {
34337         return this.name;
34338     }
34339     
34340 });
34341
34342  /**
34343  *
34344  * This is based on 
34345  * http://masonry.desandro.com
34346  *
34347  * The idea is to render all the bricks based on vertical width...
34348  *
34349  * The original code extends 'outlayer' - we might need to use that....
34350  * 
34351  */
34352
34353
34354 /**
34355  * @class Roo.bootstrap.LayoutMasonry
34356  * @extends Roo.bootstrap.Component
34357  * Bootstrap Layout Masonry class
34358  * 
34359  * @constructor
34360  * Create a new Element
34361  * @param {Object} config The config object
34362  */
34363
34364 Roo.bootstrap.LayoutMasonry = function(config){
34365     
34366     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
34367     
34368     this.bricks = [];
34369     
34370     Roo.bootstrap.LayoutMasonry.register(this);
34371     
34372     this.addEvents({
34373         // raw events
34374         /**
34375          * @event layout
34376          * Fire after layout the items
34377          * @param {Roo.bootstrap.LayoutMasonry} this
34378          * @param {Roo.EventObject} e
34379          */
34380         "layout" : true
34381     });
34382     
34383 };
34384
34385 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
34386     
34387     /**
34388      * @cfg {Boolean} isLayoutInstant = no animation?
34389      */   
34390     isLayoutInstant : false, // needed?
34391    
34392     /**
34393      * @cfg {Number} boxWidth  width of the columns
34394      */   
34395     boxWidth : 450,
34396     
34397       /**
34398      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
34399      */   
34400     boxHeight : 0,
34401     
34402     /**
34403      * @cfg {Number} padWidth padding below box..
34404      */   
34405     padWidth : 10, 
34406     
34407     /**
34408      * @cfg {Number} gutter gutter width..
34409      */   
34410     gutter : 10,
34411     
34412      /**
34413      * @cfg {Number} maxCols maximum number of columns
34414      */   
34415     
34416     maxCols: 0,
34417     
34418     /**
34419      * @cfg {Boolean} isAutoInitial defalut true
34420      */   
34421     isAutoInitial : true, 
34422     
34423     containerWidth: 0,
34424     
34425     /**
34426      * @cfg {Boolean} isHorizontal defalut false
34427      */   
34428     isHorizontal : false, 
34429
34430     currentSize : null,
34431     
34432     tag: 'div',
34433     
34434     cls: '',
34435     
34436     bricks: null, //CompositeElement
34437     
34438     cols : 1,
34439     
34440     _isLayoutInited : false,
34441     
34442 //    isAlternative : false, // only use for vertical layout...
34443     
34444     /**
34445      * @cfg {Number} alternativePadWidth padding below box..
34446      */   
34447     alternativePadWidth : 50,
34448     
34449     selectedBrick : [],
34450     
34451     getAutoCreate : function(){
34452         
34453         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
34454         
34455         var cfg = {
34456             tag: this.tag,
34457             cls: 'blog-masonary-wrapper ' + this.cls,
34458             cn : {
34459                 cls : 'mas-boxes masonary'
34460             }
34461         };
34462         
34463         return cfg;
34464     },
34465     
34466     getChildContainer: function( )
34467     {
34468         if (this.boxesEl) {
34469             return this.boxesEl;
34470         }
34471         
34472         this.boxesEl = this.el.select('.mas-boxes').first();
34473         
34474         return this.boxesEl;
34475     },
34476     
34477     
34478     initEvents : function()
34479     {
34480         var _this = this;
34481         
34482         if(this.isAutoInitial){
34483             Roo.log('hook children rendered');
34484             this.on('childrenrendered', function() {
34485                 Roo.log('children rendered');
34486                 _this.initial();
34487             } ,this);
34488         }
34489     },
34490     
34491     initial : function()
34492     {
34493         this.selectedBrick = [];
34494         
34495         this.currentSize = this.el.getBox(true);
34496         
34497         Roo.EventManager.onWindowResize(this.resize, this); 
34498
34499         if(!this.isAutoInitial){
34500             this.layout();
34501             return;
34502         }
34503         
34504         this.layout();
34505         
34506         return;
34507         //this.layout.defer(500,this);
34508         
34509     },
34510     
34511     resize : function()
34512     {
34513         var cs = this.el.getBox(true);
34514         
34515         if (
34516                 this.currentSize.width == cs.width && 
34517                 this.currentSize.x == cs.x && 
34518                 this.currentSize.height == cs.height && 
34519                 this.currentSize.y == cs.y 
34520         ) {
34521             Roo.log("no change in with or X or Y");
34522             return;
34523         }
34524         
34525         this.currentSize = cs;
34526         
34527         this.layout();
34528         
34529     },
34530     
34531     layout : function()
34532     {   
34533         this._resetLayout();
34534         
34535         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34536         
34537         this.layoutItems( isInstant );
34538       
34539         this._isLayoutInited = true;
34540         
34541         this.fireEvent('layout', this);
34542         
34543     },
34544     
34545     _resetLayout : function()
34546     {
34547         if(this.isHorizontal){
34548             this.horizontalMeasureColumns();
34549             return;
34550         }
34551         
34552         this.verticalMeasureColumns();
34553         
34554     },
34555     
34556     verticalMeasureColumns : function()
34557     {
34558         this.getContainerWidth();
34559         
34560 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34561 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34562 //            return;
34563 //        }
34564         
34565         var boxWidth = this.boxWidth + this.padWidth;
34566         
34567         if(this.containerWidth < this.boxWidth){
34568             boxWidth = this.containerWidth
34569         }
34570         
34571         var containerWidth = this.containerWidth;
34572         
34573         var cols = Math.floor(containerWidth / boxWidth);
34574         
34575         this.cols = Math.max( cols, 1 );
34576         
34577         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34578         
34579         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34580         
34581         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34582         
34583         this.colWidth = boxWidth + avail - this.padWidth;
34584         
34585         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34586         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34587     },
34588     
34589     horizontalMeasureColumns : function()
34590     {
34591         this.getContainerWidth();
34592         
34593         var boxWidth = this.boxWidth;
34594         
34595         if(this.containerWidth < boxWidth){
34596             boxWidth = this.containerWidth;
34597         }
34598         
34599         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34600         
34601         this.el.setHeight(boxWidth);
34602         
34603     },
34604     
34605     getContainerWidth : function()
34606     {
34607         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34608     },
34609     
34610     layoutItems : function( isInstant )
34611     {
34612         Roo.log(this.bricks);
34613         
34614         var items = Roo.apply([], this.bricks);
34615         
34616         if(this.isHorizontal){
34617             this._horizontalLayoutItems( items , isInstant );
34618             return;
34619         }
34620         
34621 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34622 //            this._verticalAlternativeLayoutItems( items , isInstant );
34623 //            return;
34624 //        }
34625         
34626         this._verticalLayoutItems( items , isInstant );
34627         
34628     },
34629     
34630     _verticalLayoutItems : function ( items , isInstant)
34631     {
34632         if ( !items || !items.length ) {
34633             return;
34634         }
34635         
34636         var standard = [
34637             ['xs', 'xs', 'xs', 'tall'],
34638             ['xs', 'xs', 'tall'],
34639             ['xs', 'xs', 'sm'],
34640             ['xs', 'xs', 'xs'],
34641             ['xs', 'tall'],
34642             ['xs', 'sm'],
34643             ['xs', 'xs'],
34644             ['xs'],
34645             
34646             ['sm', 'xs', 'xs'],
34647             ['sm', 'xs'],
34648             ['sm'],
34649             
34650             ['tall', 'xs', 'xs', 'xs'],
34651             ['tall', 'xs', 'xs'],
34652             ['tall', 'xs'],
34653             ['tall']
34654             
34655         ];
34656         
34657         var queue = [];
34658         
34659         var boxes = [];
34660         
34661         var box = [];
34662         
34663         Roo.each(items, function(item, k){
34664             
34665             switch (item.size) {
34666                 // these layouts take up a full box,
34667                 case 'md' :
34668                 case 'md-left' :
34669                 case 'md-right' :
34670                 case 'wide' :
34671                     
34672                     if(box.length){
34673                         boxes.push(box);
34674                         box = [];
34675                     }
34676                     
34677                     boxes.push([item]);
34678                     
34679                     break;
34680                     
34681                 case 'xs' :
34682                 case 'sm' :
34683                 case 'tall' :
34684                     
34685                     box.push(item);
34686                     
34687                     break;
34688                 default :
34689                     break;
34690                     
34691             }
34692             
34693         }, this);
34694         
34695         if(box.length){
34696             boxes.push(box);
34697             box = [];
34698         }
34699         
34700         var filterPattern = function(box, length)
34701         {
34702             if(!box.length){
34703                 return;
34704             }
34705             
34706             var match = false;
34707             
34708             var pattern = box.slice(0, length);
34709             
34710             var format = [];
34711             
34712             Roo.each(pattern, function(i){
34713                 format.push(i.size);
34714             }, this);
34715             
34716             Roo.each(standard, function(s){
34717                 
34718                 if(String(s) != String(format)){
34719                     return;
34720                 }
34721                 
34722                 match = true;
34723                 return false;
34724                 
34725             }, this);
34726             
34727             if(!match && length == 1){
34728                 return;
34729             }
34730             
34731             if(!match){
34732                 filterPattern(box, length - 1);
34733                 return;
34734             }
34735                 
34736             queue.push(pattern);
34737
34738             box = box.slice(length, box.length);
34739
34740             filterPattern(box, 4);
34741
34742             return;
34743             
34744         }
34745         
34746         Roo.each(boxes, function(box, k){
34747             
34748             if(!box.length){
34749                 return;
34750             }
34751             
34752             if(box.length == 1){
34753                 queue.push(box);
34754                 return;
34755             }
34756             
34757             filterPattern(box, 4);
34758             
34759         }, this);
34760         
34761         this._processVerticalLayoutQueue( queue, isInstant );
34762         
34763     },
34764     
34765 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34766 //    {
34767 //        if ( !items || !items.length ) {
34768 //            return;
34769 //        }
34770 //
34771 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34772 //        
34773 //    },
34774     
34775     _horizontalLayoutItems : function ( items , isInstant)
34776     {
34777         if ( !items || !items.length || items.length < 3) {
34778             return;
34779         }
34780         
34781         items.reverse();
34782         
34783         var eItems = items.slice(0, 3);
34784         
34785         items = items.slice(3, items.length);
34786         
34787         var standard = [
34788             ['xs', 'xs', 'xs', 'wide'],
34789             ['xs', 'xs', 'wide'],
34790             ['xs', 'xs', 'sm'],
34791             ['xs', 'xs', 'xs'],
34792             ['xs', 'wide'],
34793             ['xs', 'sm'],
34794             ['xs', 'xs'],
34795             ['xs'],
34796             
34797             ['sm', 'xs', 'xs'],
34798             ['sm', 'xs'],
34799             ['sm'],
34800             
34801             ['wide', 'xs', 'xs', 'xs'],
34802             ['wide', 'xs', 'xs'],
34803             ['wide', 'xs'],
34804             ['wide'],
34805             
34806             ['wide-thin']
34807         ];
34808         
34809         var queue = [];
34810         
34811         var boxes = [];
34812         
34813         var box = [];
34814         
34815         Roo.each(items, function(item, k){
34816             
34817             switch (item.size) {
34818                 case 'md' :
34819                 case 'md-left' :
34820                 case 'md-right' :
34821                 case 'tall' :
34822                     
34823                     if(box.length){
34824                         boxes.push(box);
34825                         box = [];
34826                     }
34827                     
34828                     boxes.push([item]);
34829                     
34830                     break;
34831                     
34832                 case 'xs' :
34833                 case 'sm' :
34834                 case 'wide' :
34835                 case 'wide-thin' :
34836                     
34837                     box.push(item);
34838                     
34839                     break;
34840                 default :
34841                     break;
34842                     
34843             }
34844             
34845         }, this);
34846         
34847         if(box.length){
34848             boxes.push(box);
34849             box = [];
34850         }
34851         
34852         var filterPattern = function(box, length)
34853         {
34854             if(!box.length){
34855                 return;
34856             }
34857             
34858             var match = false;
34859             
34860             var pattern = box.slice(0, length);
34861             
34862             var format = [];
34863             
34864             Roo.each(pattern, function(i){
34865                 format.push(i.size);
34866             }, this);
34867             
34868             Roo.each(standard, function(s){
34869                 
34870                 if(String(s) != String(format)){
34871                     return;
34872                 }
34873                 
34874                 match = true;
34875                 return false;
34876                 
34877             }, this);
34878             
34879             if(!match && length == 1){
34880                 return;
34881             }
34882             
34883             if(!match){
34884                 filterPattern(box, length - 1);
34885                 return;
34886             }
34887                 
34888             queue.push(pattern);
34889
34890             box = box.slice(length, box.length);
34891
34892             filterPattern(box, 4);
34893
34894             return;
34895             
34896         }
34897         
34898         Roo.each(boxes, function(box, k){
34899             
34900             if(!box.length){
34901                 return;
34902             }
34903             
34904             if(box.length == 1){
34905                 queue.push(box);
34906                 return;
34907             }
34908             
34909             filterPattern(box, 4);
34910             
34911         }, this);
34912         
34913         
34914         var prune = [];
34915         
34916         var pos = this.el.getBox(true);
34917         
34918         var minX = pos.x;
34919         
34920         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34921         
34922         var hit_end = false;
34923         
34924         Roo.each(queue, function(box){
34925             
34926             if(hit_end){
34927                 
34928                 Roo.each(box, function(b){
34929                 
34930                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34931                     b.el.hide();
34932
34933                 }, this);
34934
34935                 return;
34936             }
34937             
34938             var mx = 0;
34939             
34940             Roo.each(box, function(b){
34941                 
34942                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34943                 b.el.show();
34944
34945                 mx = Math.max(mx, b.x);
34946                 
34947             }, this);
34948             
34949             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34950             
34951             if(maxX < minX){
34952                 
34953                 Roo.each(box, function(b){
34954                 
34955                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34956                     b.el.hide();
34957                     
34958                 }, this);
34959                 
34960                 hit_end = true;
34961                 
34962                 return;
34963             }
34964             
34965             prune.push(box);
34966             
34967         }, this);
34968         
34969         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34970     },
34971     
34972     /** Sets position of item in DOM
34973     * @param {Element} item
34974     * @param {Number} x - horizontal position
34975     * @param {Number} y - vertical position
34976     * @param {Boolean} isInstant - disables transitions
34977     */
34978     _processVerticalLayoutQueue : function( queue, isInstant )
34979     {
34980         var pos = this.el.getBox(true);
34981         var x = pos.x;
34982         var y = pos.y;
34983         var maxY = [];
34984         
34985         for (var i = 0; i < this.cols; i++){
34986             maxY[i] = pos.y;
34987         }
34988         
34989         Roo.each(queue, function(box, k){
34990             
34991             var col = k % this.cols;
34992             
34993             Roo.each(box, function(b,kk){
34994                 
34995                 b.el.position('absolute');
34996                 
34997                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34998                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34999                 
35000                 if(b.size == 'md-left' || b.size == 'md-right'){
35001                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35002                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35003                 }
35004                 
35005                 b.el.setWidth(width);
35006                 b.el.setHeight(height);
35007                 // iframe?
35008                 b.el.select('iframe',true).setSize(width,height);
35009                 
35010             }, this);
35011             
35012             for (var i = 0; i < this.cols; i++){
35013                 
35014                 if(maxY[i] < maxY[col]){
35015                     col = i;
35016                     continue;
35017                 }
35018                 
35019                 col = Math.min(col, i);
35020                 
35021             }
35022             
35023             x = pos.x + col * (this.colWidth + this.padWidth);
35024             
35025             y = maxY[col];
35026             
35027             var positions = [];
35028             
35029             switch (box.length){
35030                 case 1 :
35031                     positions = this.getVerticalOneBoxColPositions(x, y, box);
35032                     break;
35033                 case 2 :
35034                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
35035                     break;
35036                 case 3 :
35037                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
35038                     break;
35039                 case 4 :
35040                     positions = this.getVerticalFourBoxColPositions(x, y, box);
35041                     break;
35042                 default :
35043                     break;
35044             }
35045             
35046             Roo.each(box, function(b,kk){
35047                 
35048                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35049                 
35050                 var sz = b.el.getSize();
35051                 
35052                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
35053                 
35054             }, this);
35055             
35056         }, this);
35057         
35058         var mY = 0;
35059         
35060         for (var i = 0; i < this.cols; i++){
35061             mY = Math.max(mY, maxY[i]);
35062         }
35063         
35064         this.el.setHeight(mY - pos.y);
35065         
35066     },
35067     
35068 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
35069 //    {
35070 //        var pos = this.el.getBox(true);
35071 //        var x = pos.x;
35072 //        var y = pos.y;
35073 //        var maxX = pos.right;
35074 //        
35075 //        var maxHeight = 0;
35076 //        
35077 //        Roo.each(items, function(item, k){
35078 //            
35079 //            var c = k % 2;
35080 //            
35081 //            item.el.position('absolute');
35082 //                
35083 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
35084 //
35085 //            item.el.setWidth(width);
35086 //
35087 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
35088 //
35089 //            item.el.setHeight(height);
35090 //            
35091 //            if(c == 0){
35092 //                item.el.setXY([x, y], isInstant ? false : true);
35093 //            } else {
35094 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
35095 //            }
35096 //            
35097 //            y = y + height + this.alternativePadWidth;
35098 //            
35099 //            maxHeight = maxHeight + height + this.alternativePadWidth;
35100 //            
35101 //        }, this);
35102 //        
35103 //        this.el.setHeight(maxHeight);
35104 //        
35105 //    },
35106     
35107     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
35108     {
35109         var pos = this.el.getBox(true);
35110         
35111         var minX = pos.x;
35112         var minY = pos.y;
35113         
35114         var maxX = pos.right;
35115         
35116         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
35117         
35118         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
35119         
35120         Roo.each(queue, function(box, k){
35121             
35122             Roo.each(box, function(b, kk){
35123                 
35124                 b.el.position('absolute');
35125                 
35126                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35127                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35128                 
35129                 if(b.size == 'md-left' || b.size == 'md-right'){
35130                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
35131                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
35132                 }
35133                 
35134                 b.el.setWidth(width);
35135                 b.el.setHeight(height);
35136                 
35137             }, this);
35138             
35139             if(!box.length){
35140                 return;
35141             }
35142             
35143             var positions = [];
35144             
35145             switch (box.length){
35146                 case 1 :
35147                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
35148                     break;
35149                 case 2 :
35150                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
35151                     break;
35152                 case 3 :
35153                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
35154                     break;
35155                 case 4 :
35156                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
35157                     break;
35158                 default :
35159                     break;
35160             }
35161             
35162             Roo.each(box, function(b,kk){
35163                 
35164                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
35165                 
35166                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
35167                 
35168             }, this);
35169             
35170         }, this);
35171         
35172     },
35173     
35174     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
35175     {
35176         Roo.each(eItems, function(b,k){
35177             
35178             b.size = (k == 0) ? 'sm' : 'xs';
35179             b.x = (k == 0) ? 2 : 1;
35180             b.y = (k == 0) ? 2 : 1;
35181             
35182             b.el.position('absolute');
35183             
35184             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
35185                 
35186             b.el.setWidth(width);
35187             
35188             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
35189             
35190             b.el.setHeight(height);
35191             
35192         }, this);
35193
35194         var positions = [];
35195         
35196         positions.push({
35197             x : maxX - this.unitWidth * 2 - this.gutter,
35198             y : minY
35199         });
35200         
35201         positions.push({
35202             x : maxX - this.unitWidth,
35203             y : minY + (this.unitWidth + this.gutter) * 2
35204         });
35205         
35206         positions.push({
35207             x : maxX - this.unitWidth * 3 - this.gutter * 2,
35208             y : minY
35209         });
35210         
35211         Roo.each(eItems, function(b,k){
35212             
35213             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
35214
35215         }, this);
35216         
35217     },
35218     
35219     getVerticalOneBoxColPositions : function(x, y, box)
35220     {
35221         var pos = [];
35222         
35223         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
35224         
35225         if(box[0].size == 'md-left'){
35226             rand = 0;
35227         }
35228         
35229         if(box[0].size == 'md-right'){
35230             rand = 1;
35231         }
35232         
35233         pos.push({
35234             x : x + (this.unitWidth + this.gutter) * rand,
35235             y : y
35236         });
35237         
35238         return pos;
35239     },
35240     
35241     getVerticalTwoBoxColPositions : function(x, y, box)
35242     {
35243         var pos = [];
35244         
35245         if(box[0].size == 'xs'){
35246             
35247             pos.push({
35248                 x : x,
35249                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
35250             });
35251
35252             pos.push({
35253                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
35254                 y : y
35255             });
35256             
35257             return pos;
35258             
35259         }
35260         
35261         pos.push({
35262             x : x,
35263             y : y
35264         });
35265
35266         pos.push({
35267             x : x + (this.unitWidth + this.gutter) * 2,
35268             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
35269         });
35270         
35271         return pos;
35272         
35273     },
35274     
35275     getVerticalThreeBoxColPositions : function(x, y, box)
35276     {
35277         var pos = [];
35278         
35279         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35280             
35281             pos.push({
35282                 x : x,
35283                 y : y
35284             });
35285
35286             pos.push({
35287                 x : x + (this.unitWidth + this.gutter) * 1,
35288                 y : y
35289             });
35290             
35291             pos.push({
35292                 x : x + (this.unitWidth + this.gutter) * 2,
35293                 y : y
35294             });
35295             
35296             return pos;
35297             
35298         }
35299         
35300         if(box[0].size == 'xs' && box[1].size == 'xs'){
35301             
35302             pos.push({
35303                 x : x,
35304                 y : y
35305             });
35306
35307             pos.push({
35308                 x : x,
35309                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
35310             });
35311             
35312             pos.push({
35313                 x : x + (this.unitWidth + this.gutter) * 1,
35314                 y : y
35315             });
35316             
35317             return pos;
35318             
35319         }
35320         
35321         pos.push({
35322             x : x,
35323             y : y
35324         });
35325
35326         pos.push({
35327             x : x + (this.unitWidth + this.gutter) * 2,
35328             y : y
35329         });
35330
35331         pos.push({
35332             x : x + (this.unitWidth + this.gutter) * 2,
35333             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
35334         });
35335             
35336         return pos;
35337         
35338     },
35339     
35340     getVerticalFourBoxColPositions : function(x, y, box)
35341     {
35342         var pos = [];
35343         
35344         if(box[0].size == 'xs'){
35345             
35346             pos.push({
35347                 x : x,
35348                 y : y
35349             });
35350
35351             pos.push({
35352                 x : x,
35353                 y : y + (this.unitHeight + this.gutter) * 1
35354             });
35355             
35356             pos.push({
35357                 x : x,
35358                 y : y + (this.unitHeight + this.gutter) * 2
35359             });
35360             
35361             pos.push({
35362                 x : x + (this.unitWidth + this.gutter) * 1,
35363                 y : y
35364             });
35365             
35366             return pos;
35367             
35368         }
35369         
35370         pos.push({
35371             x : x,
35372             y : y
35373         });
35374
35375         pos.push({
35376             x : x + (this.unitWidth + this.gutter) * 2,
35377             y : y
35378         });
35379
35380         pos.push({
35381             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
35382             y : y + (this.unitHeight + this.gutter) * 1
35383         });
35384
35385         pos.push({
35386             x : x + (this.unitWidth + this.gutter) * 2,
35387             y : y + (this.unitWidth + this.gutter) * 2
35388         });
35389
35390         return pos;
35391         
35392     },
35393     
35394     getHorizontalOneBoxColPositions : function(maxX, minY, box)
35395     {
35396         var pos = [];
35397         
35398         if(box[0].size == 'md-left'){
35399             pos.push({
35400                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35401                 y : minY
35402             });
35403             
35404             return pos;
35405         }
35406         
35407         if(box[0].size == 'md-right'){
35408             pos.push({
35409                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
35410                 y : minY + (this.unitWidth + this.gutter) * 1
35411             });
35412             
35413             return pos;
35414         }
35415         
35416         var rand = Math.floor(Math.random() * (4 - box[0].y));
35417         
35418         pos.push({
35419             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35420             y : minY + (this.unitWidth + this.gutter) * rand
35421         });
35422         
35423         return pos;
35424         
35425     },
35426     
35427     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
35428     {
35429         var pos = [];
35430         
35431         if(box[0].size == 'xs'){
35432             
35433             pos.push({
35434                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35435                 y : minY
35436             });
35437
35438             pos.push({
35439                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35440                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
35441             });
35442             
35443             return pos;
35444             
35445         }
35446         
35447         pos.push({
35448             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35449             y : minY
35450         });
35451
35452         pos.push({
35453             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35454             y : minY + (this.unitWidth + this.gutter) * 2
35455         });
35456         
35457         return pos;
35458         
35459     },
35460     
35461     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
35462     {
35463         var pos = [];
35464         
35465         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
35466             
35467             pos.push({
35468                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35469                 y : minY
35470             });
35471
35472             pos.push({
35473                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35474                 y : minY + (this.unitWidth + this.gutter) * 1
35475             });
35476             
35477             pos.push({
35478                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35479                 y : minY + (this.unitWidth + this.gutter) * 2
35480             });
35481             
35482             return pos;
35483             
35484         }
35485         
35486         if(box[0].size == 'xs' && box[1].size == 'xs'){
35487             
35488             pos.push({
35489                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35490                 y : minY
35491             });
35492
35493             pos.push({
35494                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35495                 y : minY
35496             });
35497             
35498             pos.push({
35499                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35500                 y : minY + (this.unitWidth + this.gutter) * 1
35501             });
35502             
35503             return pos;
35504             
35505         }
35506         
35507         pos.push({
35508             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35509             y : minY
35510         });
35511
35512         pos.push({
35513             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35514             y : minY + (this.unitWidth + this.gutter) * 2
35515         });
35516
35517         pos.push({
35518             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35519             y : minY + (this.unitWidth + this.gutter) * 2
35520         });
35521             
35522         return pos;
35523         
35524     },
35525     
35526     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35527     {
35528         var pos = [];
35529         
35530         if(box[0].size == 'xs'){
35531             
35532             pos.push({
35533                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35534                 y : minY
35535             });
35536
35537             pos.push({
35538                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35539                 y : minY
35540             });
35541             
35542             pos.push({
35543                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35544                 y : minY
35545             });
35546             
35547             pos.push({
35548                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35549                 y : minY + (this.unitWidth + this.gutter) * 1
35550             });
35551             
35552             return pos;
35553             
35554         }
35555         
35556         pos.push({
35557             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35558             y : minY
35559         });
35560         
35561         pos.push({
35562             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35563             y : minY + (this.unitWidth + this.gutter) * 2
35564         });
35565         
35566         pos.push({
35567             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35568             y : minY + (this.unitWidth + this.gutter) * 2
35569         });
35570         
35571         pos.push({
35572             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1) - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35573             y : minY + (this.unitWidth + this.gutter) * 2
35574         });
35575
35576         return pos;
35577         
35578     },
35579     
35580     /**
35581     * remove a Masonry Brick
35582     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35583     */
35584     removeBrick : function(brick_id)
35585     {
35586         if (!brick_id) {
35587             return;
35588         }
35589         
35590         for (var i = 0; i<this.bricks.length; i++) {
35591             if (this.bricks[i].id == brick_id) {
35592                 this.bricks.splice(i,1);
35593                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35594                 this.initial();
35595             }
35596         }
35597     },
35598     
35599     /**
35600     * adds a Masonry Brick
35601     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35602     */
35603     addBrick : function(cfg)
35604     {
35605         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35606         //this.register(cn);
35607         cn.parentId = this.id;
35608         cn.render(this.el);
35609         return cn;
35610     },
35611     
35612     /**
35613     * register a Masonry Brick
35614     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35615     */
35616     
35617     register : function(brick)
35618     {
35619         this.bricks.push(brick);
35620         brick.masonryId = this.id;
35621     },
35622     
35623     /**
35624     * clear all the Masonry Brick
35625     */
35626     clearAll : function()
35627     {
35628         this.bricks = [];
35629         //this.getChildContainer().dom.innerHTML = "";
35630         this.el.dom.innerHTML = '';
35631     },
35632     
35633     getSelected : function()
35634     {
35635         if (!this.selectedBrick) {
35636             return false;
35637         }
35638         
35639         return this.selectedBrick;
35640     }
35641 });
35642
35643 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35644     
35645     groups: {},
35646      /**
35647     * register a Masonry Layout
35648     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35649     */
35650     
35651     register : function(layout)
35652     {
35653         this.groups[layout.id] = layout;
35654     },
35655     /**
35656     * fetch a  Masonry Layout based on the masonry layout ID
35657     * @param {string} the masonry layout to add
35658     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35659     */
35660     
35661     get: function(layout_id) {
35662         if (typeof(this.groups[layout_id]) == 'undefined') {
35663             return false;
35664         }
35665         return this.groups[layout_id] ;
35666     }
35667     
35668     
35669     
35670 });
35671
35672  
35673
35674  /**
35675  *
35676  * This is based on 
35677  * http://masonry.desandro.com
35678  *
35679  * The idea is to render all the bricks based on vertical width...
35680  *
35681  * The original code extends 'outlayer' - we might need to use that....
35682  * 
35683  */
35684
35685
35686 /**
35687  * @class Roo.bootstrap.LayoutMasonryAuto
35688  * @extends Roo.bootstrap.Component
35689  * Bootstrap Layout Masonry class
35690  * 
35691  * @constructor
35692  * Create a new Element
35693  * @param {Object} config The config object
35694  */
35695
35696 Roo.bootstrap.LayoutMasonryAuto = function(config){
35697     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35698 };
35699
35700 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35701     
35702       /**
35703      * @cfg {Boolean} isFitWidth  - resize the width..
35704      */   
35705     isFitWidth : false,  // options..
35706     /**
35707      * @cfg {Boolean} isOriginLeft = left align?
35708      */   
35709     isOriginLeft : true,
35710     /**
35711      * @cfg {Boolean} isOriginTop = top align?
35712      */   
35713     isOriginTop : false,
35714     /**
35715      * @cfg {Boolean} isLayoutInstant = no animation?
35716      */   
35717     isLayoutInstant : false, // needed?
35718     /**
35719      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35720      */   
35721     isResizingContainer : true,
35722     /**
35723      * @cfg {Number} columnWidth  width of the columns 
35724      */   
35725     
35726     columnWidth : 0,
35727     
35728     /**
35729      * @cfg {Number} maxCols maximum number of columns
35730      */   
35731     
35732     maxCols: 0,
35733     /**
35734      * @cfg {Number} padHeight padding below box..
35735      */   
35736     
35737     padHeight : 10, 
35738     
35739     /**
35740      * @cfg {Boolean} isAutoInitial defalut true
35741      */   
35742     
35743     isAutoInitial : true, 
35744     
35745     // private?
35746     gutter : 0,
35747     
35748     containerWidth: 0,
35749     initialColumnWidth : 0,
35750     currentSize : null,
35751     
35752     colYs : null, // array.
35753     maxY : 0,
35754     padWidth: 10,
35755     
35756     
35757     tag: 'div',
35758     cls: '',
35759     bricks: null, //CompositeElement
35760     cols : 0, // array?
35761     // element : null, // wrapped now this.el
35762     _isLayoutInited : null, 
35763     
35764     
35765     getAutoCreate : function(){
35766         
35767         var cfg = {
35768             tag: this.tag,
35769             cls: 'blog-masonary-wrapper ' + this.cls,
35770             cn : {
35771                 cls : 'mas-boxes masonary'
35772             }
35773         };
35774         
35775         return cfg;
35776     },
35777     
35778     getChildContainer: function( )
35779     {
35780         if (this.boxesEl) {
35781             return this.boxesEl;
35782         }
35783         
35784         this.boxesEl = this.el.select('.mas-boxes').first();
35785         
35786         return this.boxesEl;
35787     },
35788     
35789     
35790     initEvents : function()
35791     {
35792         var _this = this;
35793         
35794         if(this.isAutoInitial){
35795             Roo.log('hook children rendered');
35796             this.on('childrenrendered', function() {
35797                 Roo.log('children rendered');
35798                 _this.initial();
35799             } ,this);
35800         }
35801         
35802     },
35803     
35804     initial : function()
35805     {
35806         this.reloadItems();
35807
35808         this.currentSize = this.el.getBox(true);
35809
35810         /// was window resize... - let's see if this works..
35811         Roo.EventManager.onWindowResize(this.resize, this); 
35812
35813         if(!this.isAutoInitial){
35814             this.layout();
35815             return;
35816         }
35817         
35818         this.layout.defer(500,this);
35819     },
35820     
35821     reloadItems: function()
35822     {
35823         this.bricks = this.el.select('.masonry-brick', true);
35824         
35825         this.bricks.each(function(b) {
35826             //Roo.log(b.getSize());
35827             if (!b.attr('originalwidth')) {
35828                 b.attr('originalwidth',  b.getSize().width);
35829             }
35830             
35831         });
35832         
35833         Roo.log(this.bricks.elements.length);
35834     },
35835     
35836     resize : function()
35837     {
35838         Roo.log('resize');
35839         var cs = this.el.getBox(true);
35840         
35841         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35842             Roo.log("no change in with or X");
35843             return;
35844         }
35845         this.currentSize = cs;
35846         this.layout();
35847     },
35848     
35849     layout : function()
35850     {
35851          Roo.log('layout');
35852         this._resetLayout();
35853         //this._manageStamps();
35854       
35855         // don't animate first layout
35856         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35857         this.layoutItems( isInstant );
35858       
35859         // flag for initalized
35860         this._isLayoutInited = true;
35861     },
35862     
35863     layoutItems : function( isInstant )
35864     {
35865         //var items = this._getItemsForLayout( this.items );
35866         // original code supports filtering layout items.. we just ignore it..
35867         
35868         this._layoutItems( this.bricks , isInstant );
35869       
35870         this._postLayout();
35871     },
35872     _layoutItems : function ( items , isInstant)
35873     {
35874        //this.fireEvent( 'layout', this, items );
35875     
35876
35877         if ( !items || !items.elements.length ) {
35878           // no items, emit event with empty array
35879             return;
35880         }
35881
35882         var queue = [];
35883         items.each(function(item) {
35884             Roo.log("layout item");
35885             Roo.log(item);
35886             // get x/y object from method
35887             var position = this._getItemLayoutPosition( item );
35888             // enqueue
35889             position.item = item;
35890             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35891             queue.push( position );
35892         }, this);
35893       
35894         this._processLayoutQueue( queue );
35895     },
35896     /** Sets position of item in DOM
35897     * @param {Element} item
35898     * @param {Number} x - horizontal position
35899     * @param {Number} y - vertical position
35900     * @param {Boolean} isInstant - disables transitions
35901     */
35902     _processLayoutQueue : function( queue )
35903     {
35904         for ( var i=0, len = queue.length; i < len; i++ ) {
35905             var obj = queue[i];
35906             obj.item.position('absolute');
35907             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35908         }
35909     },
35910       
35911     
35912     /**
35913     * Any logic you want to do after each layout,
35914     * i.e. size the container
35915     */
35916     _postLayout : function()
35917     {
35918         this.resizeContainer();
35919     },
35920     
35921     resizeContainer : function()
35922     {
35923         if ( !this.isResizingContainer ) {
35924             return;
35925         }
35926         var size = this._getContainerSize();
35927         if ( size ) {
35928             this.el.setSize(size.width,size.height);
35929             this.boxesEl.setSize(size.width,size.height);
35930         }
35931     },
35932     
35933     
35934     
35935     _resetLayout : function()
35936     {
35937         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35938         this.colWidth = this.el.getWidth();
35939         //this.gutter = this.el.getWidth(); 
35940         
35941         this.measureColumns();
35942
35943         // reset column Y
35944         var i = this.cols;
35945         this.colYs = [];
35946         while (i--) {
35947             this.colYs.push( 0 );
35948         }
35949     
35950         this.maxY = 0;
35951     },
35952
35953     measureColumns : function()
35954     {
35955         this.getContainerWidth();
35956       // if columnWidth is 0, default to outerWidth of first item
35957         if ( !this.columnWidth ) {
35958             var firstItem = this.bricks.first();
35959             Roo.log(firstItem);
35960             this.columnWidth  = this.containerWidth;
35961             if (firstItem && firstItem.attr('originalwidth') ) {
35962                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35963             }
35964             // columnWidth fall back to item of first element
35965             Roo.log("set column width?");
35966                         this.initialColumnWidth = this.columnWidth  ;
35967
35968             // if first elem has no width, default to size of container
35969             
35970         }
35971         
35972         
35973         if (this.initialColumnWidth) {
35974             this.columnWidth = this.initialColumnWidth;
35975         }
35976         
35977         
35978             
35979         // column width is fixed at the top - however if container width get's smaller we should
35980         // reduce it...
35981         
35982         // this bit calcs how man columns..
35983             
35984         var columnWidth = this.columnWidth += this.gutter;
35985       
35986         // calculate columns
35987         var containerWidth = this.containerWidth + this.gutter;
35988         
35989         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35990         // fix rounding errors, typically with gutters
35991         var excess = columnWidth - containerWidth % columnWidth;
35992         
35993         
35994         // if overshoot is less than a pixel, round up, otherwise floor it
35995         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35996         cols = Math[ mathMethod ]( cols );
35997         this.cols = Math.max( cols, 1 );
35998         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35999         
36000          // padding positioning..
36001         var totalColWidth = this.cols * this.columnWidth;
36002         var padavail = this.containerWidth - totalColWidth;
36003         // so for 2 columns - we need 3 'pads'
36004         
36005         var padNeeded = (1+this.cols) * this.padWidth;
36006         
36007         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
36008         
36009         this.columnWidth += padExtra
36010         //this.padWidth = Math.floor(padavail /  ( this.cols));
36011         
36012         // adjust colum width so that padding is fixed??
36013         
36014         // we have 3 columns ... total = width * 3
36015         // we have X left over... that should be used by 
36016         
36017         //if (this.expandC) {
36018             
36019         //}
36020         
36021         
36022         
36023     },
36024     
36025     getContainerWidth : function()
36026     {
36027        /* // container is parent if fit width
36028         var container = this.isFitWidth ? this.element.parentNode : this.element;
36029         // check that this.size and size are there
36030         // IE8 triggers resize on body size change, so they might not be
36031         
36032         var size = getSize( container );  //FIXME
36033         this.containerWidth = size && size.innerWidth; //FIXME
36034         */
36035          
36036         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
36037         
36038     },
36039     
36040     _getItemLayoutPosition : function( item )  // what is item?
36041     {
36042         // we resize the item to our columnWidth..
36043       
36044         item.setWidth(this.columnWidth);
36045         item.autoBoxAdjust  = false;
36046         
36047         var sz = item.getSize();
36048  
36049         // how many columns does this brick span
36050         var remainder = this.containerWidth % this.columnWidth;
36051         
36052         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
36053         // round if off by 1 pixel, otherwise use ceil
36054         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
36055         colSpan = Math.min( colSpan, this.cols );
36056         
36057         // normally this should be '1' as we dont' currently allow multi width columns..
36058         
36059         var colGroup = this._getColGroup( colSpan );
36060         // get the minimum Y value from the columns
36061         var minimumY = Math.min.apply( Math, colGroup );
36062         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36063         
36064         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
36065          
36066         // position the brick
36067         var position = {
36068             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
36069             y: this.currentSize.y + minimumY + this.padHeight
36070         };
36071         
36072         Roo.log(position);
36073         // apply setHeight to necessary columns
36074         var setHeight = minimumY + sz.height + this.padHeight;
36075         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
36076         
36077         var setSpan = this.cols + 1 - colGroup.length;
36078         for ( var i = 0; i < setSpan; i++ ) {
36079           this.colYs[ shortColIndex + i ] = setHeight ;
36080         }
36081       
36082         return position;
36083     },
36084     
36085     /**
36086      * @param {Number} colSpan - number of columns the element spans
36087      * @returns {Array} colGroup
36088      */
36089     _getColGroup : function( colSpan )
36090     {
36091         if ( colSpan < 2 ) {
36092           // if brick spans only one column, use all the column Ys
36093           return this.colYs;
36094         }
36095       
36096         var colGroup = [];
36097         // how many different places could this brick fit horizontally
36098         var groupCount = this.cols + 1 - colSpan;
36099         // for each group potential horizontal position
36100         for ( var i = 0; i < groupCount; i++ ) {
36101           // make an array of colY values for that one group
36102           var groupColYs = this.colYs.slice( i, i + colSpan );
36103           // and get the max value of the array
36104           colGroup[i] = Math.max.apply( Math, groupColYs );
36105         }
36106         return colGroup;
36107     },
36108     /*
36109     _manageStamp : function( stamp )
36110     {
36111         var stampSize =  stamp.getSize();
36112         var offset = stamp.getBox();
36113         // get the columns that this stamp affects
36114         var firstX = this.isOriginLeft ? offset.x : offset.right;
36115         var lastX = firstX + stampSize.width;
36116         var firstCol = Math.floor( firstX / this.columnWidth );
36117         firstCol = Math.max( 0, firstCol );
36118         
36119         var lastCol = Math.floor( lastX / this.columnWidth );
36120         // lastCol should not go over if multiple of columnWidth #425
36121         lastCol -= lastX % this.columnWidth ? 0 : 1;
36122         lastCol = Math.min( this.cols - 1, lastCol );
36123         
36124         // set colYs to bottom of the stamp
36125         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
36126             stampSize.height;
36127             
36128         for ( var i = firstCol; i <= lastCol; i++ ) {
36129           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
36130         }
36131     },
36132     */
36133     
36134     _getContainerSize : function()
36135     {
36136         this.maxY = Math.max.apply( Math, this.colYs );
36137         var size = {
36138             height: this.maxY
36139         };
36140       
36141         if ( this.isFitWidth ) {
36142             size.width = this._getContainerFitWidth();
36143         }
36144       
36145         return size;
36146     },
36147     
36148     _getContainerFitWidth : function()
36149     {
36150         var unusedCols = 0;
36151         // count unused columns
36152         var i = this.cols;
36153         while ( --i ) {
36154           if ( this.colYs[i] !== 0 ) {
36155             break;
36156           }
36157           unusedCols++;
36158         }
36159         // fit container to columns that have been used
36160         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
36161     },
36162     
36163     needsResizeLayout : function()
36164     {
36165         var previousWidth = this.containerWidth;
36166         this.getContainerWidth();
36167         return previousWidth !== this.containerWidth;
36168     }
36169  
36170 });
36171
36172  
36173
36174  /*
36175  * - LGPL
36176  *
36177  * element
36178  * 
36179  */
36180
36181 /**
36182  * @class Roo.bootstrap.MasonryBrick
36183  * @extends Roo.bootstrap.Component
36184  * Bootstrap MasonryBrick class
36185  * 
36186  * @constructor
36187  * Create a new MasonryBrick
36188  * @param {Object} config The config object
36189  */
36190
36191 Roo.bootstrap.MasonryBrick = function(config){
36192     
36193     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
36194     
36195     Roo.bootstrap.MasonryBrick.register(this);
36196     
36197     this.addEvents({
36198         // raw events
36199         /**
36200          * @event click
36201          * When a MasonryBrick is clcik
36202          * @param {Roo.bootstrap.MasonryBrick} this
36203          * @param {Roo.EventObject} e
36204          */
36205         "click" : true
36206     });
36207 };
36208
36209 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
36210     
36211     /**
36212      * @cfg {String} title
36213      */   
36214     title : '',
36215     /**
36216      * @cfg {String} html
36217      */   
36218     html : '',
36219     /**
36220      * @cfg {String} bgimage
36221      */   
36222     bgimage : '',
36223     /**
36224      * @cfg {String} videourl
36225      */   
36226     videourl : '',
36227     /**
36228      * @cfg {String} cls
36229      */   
36230     cls : '',
36231     /**
36232      * @cfg {String} href
36233      */   
36234     href : '',
36235     /**
36236      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
36237      */   
36238     size : 'xs',
36239     
36240     /**
36241      * @cfg {String} placetitle (center|bottom)
36242      */   
36243     placetitle : '',
36244     
36245     /**
36246      * @cfg {Boolean} isFitContainer defalut true
36247      */   
36248     isFitContainer : true, 
36249     
36250     /**
36251      * @cfg {Boolean} preventDefault defalut false
36252      */   
36253     preventDefault : false, 
36254     
36255     /**
36256      * @cfg {Boolean} inverse defalut false
36257      */   
36258     maskInverse : false, 
36259     
36260     getAutoCreate : function()
36261     {
36262         if(!this.isFitContainer){
36263             return this.getSplitAutoCreate();
36264         }
36265         
36266         var cls = 'masonry-brick masonry-brick-full';
36267         
36268         if(this.href.length){
36269             cls += ' masonry-brick-link';
36270         }
36271         
36272         if(this.bgimage.length){
36273             cls += ' masonry-brick-image';
36274         }
36275         
36276         if(this.maskInverse){
36277             cls += ' mask-inverse';
36278         }
36279         
36280         if(!this.html.length && !this.maskInverse && !this.videourl.length){
36281             cls += ' enable-mask';
36282         }
36283         
36284         if(this.size){
36285             cls += ' masonry-' + this.size + '-brick';
36286         }
36287         
36288         if(this.placetitle.length){
36289             
36290             switch (this.placetitle) {
36291                 case 'center' :
36292                     cls += ' masonry-center-title';
36293                     break;
36294                 case 'bottom' :
36295                     cls += ' masonry-bottom-title';
36296                     break;
36297                 default:
36298                     break;
36299             }
36300             
36301         } else {
36302             if(!this.html.length && !this.bgimage.length){
36303                 cls += ' masonry-center-title';
36304             }
36305
36306             if(!this.html.length && this.bgimage.length){
36307                 cls += ' masonry-bottom-title';
36308             }
36309         }
36310         
36311         if(this.cls){
36312             cls += ' ' + this.cls;
36313         }
36314         
36315         var cfg = {
36316             tag: (this.href.length) ? 'a' : 'div',
36317             cls: cls,
36318             cn: [
36319                 {
36320                     tag: 'div',
36321                     cls: 'masonry-brick-mask'
36322                 },
36323                 {
36324                     tag: 'div',
36325                     cls: 'masonry-brick-paragraph',
36326                     cn: []
36327                 }
36328             ]
36329         };
36330         
36331         if(this.href.length){
36332             cfg.href = this.href;
36333         }
36334         
36335         var cn = cfg.cn[1].cn;
36336         
36337         if(this.title.length){
36338             cn.push({
36339                 tag: 'h4',
36340                 cls: 'masonry-brick-title',
36341                 html: this.title
36342             });
36343         }
36344         
36345         if(this.html.length){
36346             cn.push({
36347                 tag: 'p',
36348                 cls: 'masonry-brick-text',
36349                 html: this.html
36350             });
36351         }
36352         
36353         if (!this.title.length && !this.html.length) {
36354             cfg.cn[1].cls += ' hide';
36355         }
36356         
36357         if(this.bgimage.length){
36358             cfg.cn.push({
36359                 tag: 'img',
36360                 cls: 'masonry-brick-image-view',
36361                 src: this.bgimage
36362             });
36363         }
36364         
36365         if(this.videourl.length){
36366             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36367             // youtube support only?
36368             cfg.cn.push({
36369                 tag: 'iframe',
36370                 cls: 'masonry-brick-image-view',
36371                 src: vurl,
36372                 frameborder : 0,
36373                 allowfullscreen : true
36374             });
36375         }
36376         
36377         return cfg;
36378         
36379     },
36380     
36381     getSplitAutoCreate : function()
36382     {
36383         var cls = 'masonry-brick masonry-brick-split';
36384         
36385         if(this.href.length){
36386             cls += ' masonry-brick-link';
36387         }
36388         
36389         if(this.bgimage.length){
36390             cls += ' masonry-brick-image';
36391         }
36392         
36393         if(this.size){
36394             cls += ' masonry-' + this.size + '-brick';
36395         }
36396         
36397         switch (this.placetitle) {
36398             case 'center' :
36399                 cls += ' masonry-center-title';
36400                 break;
36401             case 'bottom' :
36402                 cls += ' masonry-bottom-title';
36403                 break;
36404             default:
36405                 if(!this.bgimage.length){
36406                     cls += ' masonry-center-title';
36407                 }
36408
36409                 if(this.bgimage.length){
36410                     cls += ' masonry-bottom-title';
36411                 }
36412                 break;
36413         }
36414         
36415         if(this.cls){
36416             cls += ' ' + this.cls;
36417         }
36418         
36419         var cfg = {
36420             tag: (this.href.length) ? 'a' : 'div',
36421             cls: cls,
36422             cn: [
36423                 {
36424                     tag: 'div',
36425                     cls: 'masonry-brick-split-head',
36426                     cn: [
36427                         {
36428                             tag: 'div',
36429                             cls: 'masonry-brick-paragraph',
36430                             cn: []
36431                         }
36432                     ]
36433                 },
36434                 {
36435                     tag: 'div',
36436                     cls: 'masonry-brick-split-body',
36437                     cn: []
36438                 }
36439             ]
36440         };
36441         
36442         if(this.href.length){
36443             cfg.href = this.href;
36444         }
36445         
36446         if(this.title.length){
36447             cfg.cn[0].cn[0].cn.push({
36448                 tag: 'h4',
36449                 cls: 'masonry-brick-title',
36450                 html: this.title
36451             });
36452         }
36453         
36454         if(this.html.length){
36455             cfg.cn[1].cn.push({
36456                 tag: 'p',
36457                 cls: 'masonry-brick-text',
36458                 html: this.html
36459             });
36460         }
36461
36462         if(this.bgimage.length){
36463             cfg.cn[0].cn.push({
36464                 tag: 'img',
36465                 cls: 'masonry-brick-image-view',
36466                 src: this.bgimage
36467             });
36468         }
36469         
36470         if(this.videourl.length){
36471             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36472             // youtube support only?
36473             cfg.cn[0].cn.cn.push({
36474                 tag: 'iframe',
36475                 cls: 'masonry-brick-image-view',
36476                 src: vurl,
36477                 frameborder : 0,
36478                 allowfullscreen : true
36479             });
36480         }
36481         
36482         return cfg;
36483     },
36484     
36485     initEvents: function() 
36486     {
36487         switch (this.size) {
36488             case 'xs' :
36489                 this.x = 1;
36490                 this.y = 1;
36491                 break;
36492             case 'sm' :
36493                 this.x = 2;
36494                 this.y = 2;
36495                 break;
36496             case 'md' :
36497             case 'md-left' :
36498             case 'md-right' :
36499                 this.x = 3;
36500                 this.y = 3;
36501                 break;
36502             case 'tall' :
36503                 this.x = 2;
36504                 this.y = 3;
36505                 break;
36506             case 'wide' :
36507                 this.x = 3;
36508                 this.y = 2;
36509                 break;
36510             case 'wide-thin' :
36511                 this.x = 3;
36512                 this.y = 1;
36513                 break;
36514                         
36515             default :
36516                 break;
36517         }
36518         
36519         if(Roo.isTouch){
36520             this.el.on('touchstart', this.onTouchStart, this);
36521             this.el.on('touchmove', this.onTouchMove, this);
36522             this.el.on('touchend', this.onTouchEnd, this);
36523             this.el.on('contextmenu', this.onContextMenu, this);
36524         } else {
36525             this.el.on('mouseenter'  ,this.enter, this);
36526             this.el.on('mouseleave', this.leave, this);
36527             this.el.on('click', this.onClick, this);
36528         }
36529         
36530         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36531             this.parent().bricks.push(this);   
36532         }
36533         
36534     },
36535     
36536     onClick: function(e, el)
36537     {
36538         var time = this.endTimer - this.startTimer;
36539         // Roo.log(e.preventDefault());
36540         if(Roo.isTouch){
36541             if(time > 1000){
36542                 e.preventDefault();
36543                 return;
36544             }
36545         }
36546         
36547         if(!this.preventDefault){
36548             return;
36549         }
36550         
36551         e.preventDefault();
36552         
36553         if (this.activeClass != '') {
36554             this.selectBrick();
36555         }
36556         
36557         this.fireEvent('click', this, e);
36558     },
36559     
36560     enter: 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.9, true);
36570         }
36571     },
36572     
36573     leave: function(e, el)
36574     {
36575         e.preventDefault();
36576         
36577         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36578             return;
36579         }
36580         
36581         if(this.bgimage.length && this.html.length){
36582             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36583         }
36584     },
36585     
36586     onTouchStart: function(e, el)
36587     {
36588 //        e.preventDefault();
36589         
36590         this.touchmoved = false;
36591         
36592         if(!this.isFitContainer){
36593             return;
36594         }
36595         
36596         if(!this.bgimage.length || !this.html.length){
36597             return;
36598         }
36599         
36600         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36601         
36602         this.timer = new Date().getTime();
36603         
36604     },
36605     
36606     onTouchMove: function(e, el)
36607     {
36608         this.touchmoved = true;
36609     },
36610     
36611     onContextMenu : function(e,el)
36612     {
36613         e.preventDefault();
36614         e.stopPropagation();
36615         return false;
36616     },
36617     
36618     onTouchEnd: function(e, el)
36619     {
36620 //        e.preventDefault();
36621         
36622         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36623         
36624             this.leave(e,el);
36625             
36626             return;
36627         }
36628         
36629         if(!this.bgimage.length || !this.html.length){
36630             
36631             if(this.href.length){
36632                 window.location.href = this.href;
36633             }
36634             
36635             return;
36636         }
36637         
36638         if(!this.isFitContainer){
36639             return;
36640         }
36641         
36642         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36643         
36644         window.location.href = this.href;
36645     },
36646     
36647     //selection on single brick only
36648     selectBrick : function() {
36649         
36650         if (!this.parentId) {
36651             return;
36652         }
36653         
36654         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36655         var index = m.selectedBrick.indexOf(this.id);
36656         
36657         if ( index > -1) {
36658             m.selectedBrick.splice(index,1);
36659             this.el.removeClass(this.activeClass);
36660             return;
36661         }
36662         
36663         for(var i = 0; i < m.selectedBrick.length; i++) {
36664             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36665             b.el.removeClass(b.activeClass);
36666         }
36667         
36668         m.selectedBrick = [];
36669         
36670         m.selectedBrick.push(this.id);
36671         this.el.addClass(this.activeClass);
36672         return;
36673     },
36674     
36675     isSelected : function(){
36676         return this.el.hasClass(this.activeClass);
36677         
36678     }
36679 });
36680
36681 Roo.apply(Roo.bootstrap.MasonryBrick, {
36682     
36683     //groups: {},
36684     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36685      /**
36686     * register a Masonry Brick
36687     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36688     */
36689     
36690     register : function(brick)
36691     {
36692         //this.groups[brick.id] = brick;
36693         this.groups.add(brick.id, brick);
36694     },
36695     /**
36696     * fetch a  masonry brick based on the masonry brick ID
36697     * @param {string} the masonry brick to add
36698     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36699     */
36700     
36701     get: function(brick_id) 
36702     {
36703         // if (typeof(this.groups[brick_id]) == 'undefined') {
36704         //     return false;
36705         // }
36706         // return this.groups[brick_id] ;
36707         
36708         if(this.groups.key(brick_id)) {
36709             return this.groups.key(brick_id);
36710         }
36711         
36712         return false;
36713     }
36714     
36715     
36716     
36717 });
36718
36719  /*
36720  * - LGPL
36721  *
36722  * element
36723  * 
36724  */
36725
36726 /**
36727  * @class Roo.bootstrap.Brick
36728  * @extends Roo.bootstrap.Component
36729  * Bootstrap Brick class
36730  * 
36731  * @constructor
36732  * Create a new Brick
36733  * @param {Object} config The config object
36734  */
36735
36736 Roo.bootstrap.Brick = function(config){
36737     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36738     
36739     this.addEvents({
36740         // raw events
36741         /**
36742          * @event click
36743          * When a Brick is click
36744          * @param {Roo.bootstrap.Brick} this
36745          * @param {Roo.EventObject} e
36746          */
36747         "click" : true
36748     });
36749 };
36750
36751 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36752     
36753     /**
36754      * @cfg {String} title
36755      */   
36756     title : '',
36757     /**
36758      * @cfg {String} html
36759      */   
36760     html : '',
36761     /**
36762      * @cfg {String} bgimage
36763      */   
36764     bgimage : '',
36765     /**
36766      * @cfg {String} cls
36767      */   
36768     cls : '',
36769     /**
36770      * @cfg {String} href
36771      */   
36772     href : '',
36773     /**
36774      * @cfg {String} video
36775      */   
36776     video : '',
36777     /**
36778      * @cfg {Boolean} square
36779      */   
36780     square : true,
36781     
36782     getAutoCreate : function()
36783     {
36784         var cls = 'roo-brick';
36785         
36786         if(this.href.length){
36787             cls += ' roo-brick-link';
36788         }
36789         
36790         if(this.bgimage.length){
36791             cls += ' roo-brick-image';
36792         }
36793         
36794         if(!this.html.length && !this.bgimage.length){
36795             cls += ' roo-brick-center-title';
36796         }
36797         
36798         if(!this.html.length && this.bgimage.length){
36799             cls += ' roo-brick-bottom-title';
36800         }
36801         
36802         if(this.cls){
36803             cls += ' ' + this.cls;
36804         }
36805         
36806         var cfg = {
36807             tag: (this.href.length) ? 'a' : 'div',
36808             cls: cls,
36809             cn: [
36810                 {
36811                     tag: 'div',
36812                     cls: 'roo-brick-paragraph',
36813                     cn: []
36814                 }
36815             ]
36816         };
36817         
36818         if(this.href.length){
36819             cfg.href = this.href;
36820         }
36821         
36822         var cn = cfg.cn[0].cn;
36823         
36824         if(this.title.length){
36825             cn.push({
36826                 tag: 'h4',
36827                 cls: 'roo-brick-title',
36828                 html: this.title
36829             });
36830         }
36831         
36832         if(this.html.length){
36833             cn.push({
36834                 tag: 'p',
36835                 cls: 'roo-brick-text',
36836                 html: this.html
36837             });
36838         } else {
36839             cn.cls += ' hide';
36840         }
36841         
36842         if(this.bgimage.length){
36843             cfg.cn.push({
36844                 tag: 'img',
36845                 cls: 'roo-brick-image-view',
36846                 src: this.bgimage
36847             });
36848         }
36849         
36850         return cfg;
36851     },
36852     
36853     initEvents: function() 
36854     {
36855         if(this.title.length || this.html.length){
36856             this.el.on('mouseenter'  ,this.enter, this);
36857             this.el.on('mouseleave', this.leave, this);
36858         }
36859         
36860         Roo.EventManager.onWindowResize(this.resize, this); 
36861         
36862         if(this.bgimage.length){
36863             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36864             this.imageEl.on('load', this.onImageLoad, this);
36865             return;
36866         }
36867         
36868         this.resize();
36869     },
36870     
36871     onImageLoad : function()
36872     {
36873         this.resize();
36874     },
36875     
36876     resize : function()
36877     {
36878         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36879         
36880         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36881         
36882         if(this.bgimage.length){
36883             var image = this.el.select('.roo-brick-image-view', true).first();
36884             
36885             image.setWidth(paragraph.getWidth());
36886             
36887             if(this.square){
36888                 image.setHeight(paragraph.getWidth());
36889             }
36890             
36891             this.el.setHeight(image.getHeight());
36892             paragraph.setHeight(image.getHeight());
36893             
36894         }
36895         
36896     },
36897     
36898     enter: function(e, el)
36899     {
36900         e.preventDefault();
36901         
36902         if(this.bgimage.length){
36903             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36904             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36905         }
36906     },
36907     
36908     leave: function(e, el)
36909     {
36910         e.preventDefault();
36911         
36912         if(this.bgimage.length){
36913             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36914             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36915         }
36916     }
36917     
36918 });
36919
36920  
36921
36922  /*
36923  * - LGPL
36924  *
36925  * Number field 
36926  */
36927
36928 /**
36929  * @class Roo.bootstrap.NumberField
36930  * @extends Roo.bootstrap.Input
36931  * Bootstrap NumberField class
36932  * 
36933  * 
36934  * 
36935  * 
36936  * @constructor
36937  * Create a new NumberField
36938  * @param {Object} config The config object
36939  */
36940
36941 Roo.bootstrap.NumberField = function(config){
36942     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36943 };
36944
36945 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36946     
36947     /**
36948      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36949      */
36950     allowDecimals : true,
36951     /**
36952      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36953      */
36954     decimalSeparator : ".",
36955     /**
36956      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36957      */
36958     decimalPrecision : 2,
36959     /**
36960      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36961      */
36962     allowNegative : true,
36963     
36964     /**
36965      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36966      */
36967     allowZero: true,
36968     /**
36969      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36970      */
36971     minValue : Number.NEGATIVE_INFINITY,
36972     /**
36973      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36974      */
36975     maxValue : Number.MAX_VALUE,
36976     /**
36977      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36978      */
36979     minText : "The minimum value for this field is {0}",
36980     /**
36981      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36982      */
36983     maxText : "The maximum value for this field is {0}",
36984     /**
36985      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36986      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36987      */
36988     nanText : "{0} is not a valid number",
36989     /**
36990      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36991      */
36992     thousandsDelimiter : false,
36993     /**
36994      * @cfg {String} valueAlign alignment of value
36995      */
36996     valueAlign : "left",
36997
36998     getAutoCreate : function()
36999     {
37000         var hiddenInput = {
37001             tag: 'input',
37002             type: 'hidden',
37003             id: Roo.id(),
37004             cls: 'hidden-number-input'
37005         };
37006         
37007         if (this.name) {
37008             hiddenInput.name = this.name;
37009         }
37010         
37011         this.name = '';
37012         
37013         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
37014         
37015         this.name = hiddenInput.name;
37016         
37017         if(cfg.cn.length > 0) {
37018             cfg.cn.push(hiddenInput);
37019         }
37020         
37021         return cfg;
37022     },
37023
37024     // private
37025     initEvents : function()
37026     {   
37027         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
37028         
37029         var allowed = "0123456789";
37030         
37031         if(this.allowDecimals){
37032             allowed += this.decimalSeparator;
37033         }
37034         
37035         if(this.allowNegative){
37036             allowed += "-";
37037         }
37038         
37039         if(this.thousandsDelimiter) {
37040             allowed += ",";
37041         }
37042         
37043         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
37044         
37045         var keyPress = function(e){
37046             
37047             var k = e.getKey();
37048             
37049             var c = e.getCharCode();
37050             
37051             if(
37052                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
37053                     allowed.indexOf(String.fromCharCode(c)) === -1
37054             ){
37055                 e.stopEvent();
37056                 return;
37057             }
37058             
37059             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
37060                 return;
37061             }
37062             
37063             if(allowed.indexOf(String.fromCharCode(c)) === -1){
37064                 e.stopEvent();
37065             }
37066         };
37067         
37068         this.el.on("keypress", keyPress, this);
37069     },
37070     
37071     validateValue : function(value)
37072     {
37073         
37074         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
37075             return false;
37076         }
37077         
37078         var num = this.parseValue(value);
37079         
37080         if(isNaN(num)){
37081             this.markInvalid(String.format(this.nanText, value));
37082             return false;
37083         }
37084         
37085         if(num < this.minValue){
37086             this.markInvalid(String.format(this.minText, this.minValue));
37087             return false;
37088         }
37089         
37090         if(num > this.maxValue){
37091             this.markInvalid(String.format(this.maxText, this.maxValue));
37092             return false;
37093         }
37094         
37095         return true;
37096     },
37097
37098     getValue : function()
37099     {
37100         var v = this.hiddenEl().getValue();
37101         
37102         return this.fixPrecision(this.parseValue(v));
37103     },
37104
37105     parseValue : function(value)
37106     {
37107         if(this.thousandsDelimiter) {
37108             value += "";
37109             r = new RegExp(",", "g");
37110             value = value.replace(r, "");
37111         }
37112         
37113         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
37114         return isNaN(value) ? '' : value;
37115     },
37116
37117     fixPrecision : function(value)
37118     {
37119         if(this.thousandsDelimiter) {
37120             value += "";
37121             r = new RegExp(",", "g");
37122             value = value.replace(r, "");
37123         }
37124         
37125         var nan = isNaN(value);
37126         
37127         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
37128             return nan ? '' : value;
37129         }
37130         return parseFloat(value).toFixed(this.decimalPrecision);
37131     },
37132
37133     setValue : function(v)
37134     {
37135         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
37136         
37137         this.value = v;
37138         
37139         if(this.rendered){
37140             
37141             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
37142             
37143             this.inputEl().dom.value = (v == '') ? '' :
37144                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
37145             
37146             if(!this.allowZero && v === '0') {
37147                 this.hiddenEl().dom.value = '';
37148                 this.inputEl().dom.value = '';
37149             }
37150             
37151             this.validate();
37152         }
37153     },
37154
37155     decimalPrecisionFcn : function(v)
37156     {
37157         return Math.floor(v);
37158     },
37159
37160     beforeBlur : function()
37161     {
37162         var v = this.parseValue(this.getRawValue());
37163         
37164         if(v || v === 0 || v === ''){
37165             this.setValue(v);
37166         }
37167     },
37168     
37169     hiddenEl : function()
37170     {
37171         return this.el.select('input.hidden-number-input',true).first();
37172     }
37173     
37174 });
37175
37176  
37177
37178 /*
37179 * Licence: LGPL
37180 */
37181
37182 /**
37183  * @class Roo.bootstrap.DocumentSlider
37184  * @extends Roo.bootstrap.Component
37185  * Bootstrap DocumentSlider class
37186  * 
37187  * @constructor
37188  * Create a new DocumentViewer
37189  * @param {Object} config The config object
37190  */
37191
37192 Roo.bootstrap.DocumentSlider = function(config){
37193     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
37194     
37195     this.files = [];
37196     
37197     this.addEvents({
37198         /**
37199          * @event initial
37200          * Fire after initEvent
37201          * @param {Roo.bootstrap.DocumentSlider} this
37202          */
37203         "initial" : true,
37204         /**
37205          * @event update
37206          * Fire after update
37207          * @param {Roo.bootstrap.DocumentSlider} this
37208          */
37209         "update" : true,
37210         /**
37211          * @event click
37212          * Fire after click
37213          * @param {Roo.bootstrap.DocumentSlider} this
37214          */
37215         "click" : true
37216     });
37217 };
37218
37219 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
37220     
37221     files : false,
37222     
37223     indicator : 0,
37224     
37225     getAutoCreate : function()
37226     {
37227         var cfg = {
37228             tag : 'div',
37229             cls : 'roo-document-slider',
37230             cn : [
37231                 {
37232                     tag : 'div',
37233                     cls : 'roo-document-slider-header',
37234                     cn : [
37235                         {
37236                             tag : 'div',
37237                             cls : 'roo-document-slider-header-title'
37238                         }
37239                     ]
37240                 },
37241                 {
37242                     tag : 'div',
37243                     cls : 'roo-document-slider-body',
37244                     cn : [
37245                         {
37246                             tag : 'div',
37247                             cls : 'roo-document-slider-prev',
37248                             cn : [
37249                                 {
37250                                     tag : 'i',
37251                                     cls : 'fa fa-chevron-left'
37252                                 }
37253                             ]
37254                         },
37255                         {
37256                             tag : 'div',
37257                             cls : 'roo-document-slider-thumb',
37258                             cn : [
37259                                 {
37260                                     tag : 'img',
37261                                     cls : 'roo-document-slider-image'
37262                                 }
37263                             ]
37264                         },
37265                         {
37266                             tag : 'div',
37267                             cls : 'roo-document-slider-next',
37268                             cn : [
37269                                 {
37270                                     tag : 'i',
37271                                     cls : 'fa fa-chevron-right'
37272                                 }
37273                             ]
37274                         }
37275                     ]
37276                 }
37277             ]
37278         };
37279         
37280         return cfg;
37281     },
37282     
37283     initEvents : function()
37284     {
37285         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
37286         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
37287         
37288         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
37289         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
37290         
37291         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
37292         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
37293         
37294         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
37295         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
37296         
37297         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
37298         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
37299         
37300         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
37301         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37302         
37303         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
37304         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
37305         
37306         this.thumbEl.on('click', this.onClick, this);
37307         
37308         this.prevIndicator.on('click', this.prev, this);
37309         
37310         this.nextIndicator.on('click', this.next, this);
37311         
37312     },
37313     
37314     initial : function()
37315     {
37316         if(this.files.length){
37317             this.indicator = 1;
37318             this.update()
37319         }
37320         
37321         this.fireEvent('initial', this);
37322     },
37323     
37324     update : function()
37325     {
37326         this.imageEl.attr('src', this.files[this.indicator - 1]);
37327         
37328         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
37329         
37330         this.prevIndicator.show();
37331         
37332         if(this.indicator == 1){
37333             this.prevIndicator.hide();
37334         }
37335         
37336         this.nextIndicator.show();
37337         
37338         if(this.indicator == this.files.length){
37339             this.nextIndicator.hide();
37340         }
37341         
37342         this.thumbEl.scrollTo('top');
37343         
37344         this.fireEvent('update', this);
37345     },
37346     
37347     onClick : function(e)
37348     {
37349         e.preventDefault();
37350         
37351         this.fireEvent('click', this);
37352     },
37353     
37354     prev : function(e)
37355     {
37356         e.preventDefault();
37357         
37358         this.indicator = Math.max(1, this.indicator - 1);
37359         
37360         this.update();
37361     },
37362     
37363     next : function(e)
37364     {
37365         e.preventDefault();
37366         
37367         this.indicator = Math.min(this.files.length, this.indicator + 1);
37368         
37369         this.update();
37370     }
37371 });
37372 /*
37373  * - LGPL
37374  *
37375  * RadioSet
37376  *
37377  *
37378  */
37379
37380 /**
37381  * @class Roo.bootstrap.RadioSet
37382  * @extends Roo.bootstrap.Input
37383  * Bootstrap RadioSet class
37384  * @cfg {String} indicatorpos (left|right) default left
37385  * @cfg {Boolean} inline (true|false) inline the element (default true)
37386  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
37387  * @constructor
37388  * Create a new RadioSet
37389  * @param {Object} config The config object
37390  */
37391
37392 Roo.bootstrap.RadioSet = function(config){
37393     
37394     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
37395     
37396     this.radioes = [];
37397     
37398     Roo.bootstrap.RadioSet.register(this);
37399     
37400     this.addEvents({
37401         /**
37402         * @event check
37403         * Fires when the element is checked or unchecked.
37404         * @param {Roo.bootstrap.RadioSet} this This radio
37405         * @param {Roo.bootstrap.Radio} item The checked item
37406         */
37407        check : true,
37408        /**
37409         * @event click
37410         * Fires when the element is click.
37411         * @param {Roo.bootstrap.RadioSet} this This radio set
37412         * @param {Roo.bootstrap.Radio} item The checked item
37413         * @param {Roo.EventObject} e The event object
37414         */
37415        click : true
37416     });
37417     
37418 };
37419
37420 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
37421
37422     radioes : false,
37423     
37424     inline : true,
37425     
37426     weight : '',
37427     
37428     indicatorpos : 'left',
37429     
37430     getAutoCreate : function()
37431     {
37432         var label = {
37433             tag : 'label',
37434             cls : 'roo-radio-set-label',
37435             cn : [
37436                 {
37437                     tag : 'span',
37438                     html : this.fieldLabel
37439                 }
37440             ]
37441         };
37442         if (Roo.bootstrap.version == 3) {
37443             
37444             
37445             if(this.indicatorpos == 'left'){
37446                 label.cn.unshift({
37447                     tag : 'i',
37448                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
37449                     tooltip : 'This field is required'
37450                 });
37451             } else {
37452                 label.cn.push({
37453                     tag : 'i',
37454                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
37455                     tooltip : 'This field is required'
37456                 });
37457             }
37458         }
37459         var items = {
37460             tag : 'div',
37461             cls : 'roo-radio-set-items'
37462         };
37463         
37464         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
37465         
37466         if (align === 'left' && this.fieldLabel.length) {
37467             
37468             items = {
37469                 cls : "roo-radio-set-right", 
37470                 cn: [
37471                     items
37472                 ]
37473             };
37474             
37475             if(this.labelWidth > 12){
37476                 label.style = "width: " + this.labelWidth + 'px';
37477             }
37478             
37479             if(this.labelWidth < 13 && this.labelmd == 0){
37480                 this.labelmd = this.labelWidth;
37481             }
37482             
37483             if(this.labellg > 0){
37484                 label.cls += ' col-lg-' + this.labellg;
37485                 items.cls += ' col-lg-' + (12 - this.labellg);
37486             }
37487             
37488             if(this.labelmd > 0){
37489                 label.cls += ' col-md-' + this.labelmd;
37490                 items.cls += ' col-md-' + (12 - this.labelmd);
37491             }
37492             
37493             if(this.labelsm > 0){
37494                 label.cls += ' col-sm-' + this.labelsm;
37495                 items.cls += ' col-sm-' + (12 - this.labelsm);
37496             }
37497             
37498             if(this.labelxs > 0){
37499                 label.cls += ' col-xs-' + this.labelxs;
37500                 items.cls += ' col-xs-' + (12 - this.labelxs);
37501             }
37502         }
37503         
37504         var cfg = {
37505             tag : 'div',
37506             cls : 'roo-radio-set',
37507             cn : [
37508                 {
37509                     tag : 'input',
37510                     cls : 'roo-radio-set-input',
37511                     type : 'hidden',
37512                     name : this.name,
37513                     value : this.value ? this.value :  ''
37514                 },
37515                 label,
37516                 items
37517             ]
37518         };
37519         
37520         if(this.weight.length){
37521             cfg.cls += ' roo-radio-' + this.weight;
37522         }
37523         
37524         if(this.inline) {
37525             cfg.cls += ' roo-radio-set-inline';
37526         }
37527         
37528         var settings=this;
37529         ['xs','sm','md','lg'].map(function(size){
37530             if (settings[size]) {
37531                 cfg.cls += ' col-' + size + '-' + settings[size];
37532             }
37533         });
37534         
37535         return cfg;
37536         
37537     },
37538
37539     initEvents : function()
37540     {
37541         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37542         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37543         
37544         if(!this.fieldLabel.length){
37545             this.labelEl.hide();
37546         }
37547         
37548         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37549         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37550         
37551         this.indicator = this.indicatorEl();
37552         
37553         if(this.indicator){
37554             this.indicator.addClass('invisible');
37555         }
37556         
37557         this.originalValue = this.getValue();
37558         
37559     },
37560     
37561     inputEl: function ()
37562     {
37563         return this.el.select('.roo-radio-set-input', true).first();
37564     },
37565     
37566     getChildContainer : function()
37567     {
37568         return this.itemsEl;
37569     },
37570     
37571     register : function(item)
37572     {
37573         this.radioes.push(item);
37574         
37575     },
37576     
37577     validate : function()
37578     {   
37579         if(this.getVisibilityEl().hasClass('hidden')){
37580             return true;
37581         }
37582         
37583         var valid = false;
37584         
37585         Roo.each(this.radioes, function(i){
37586             if(!i.checked){
37587                 return;
37588             }
37589             
37590             valid = true;
37591             return false;
37592         });
37593         
37594         if(this.allowBlank) {
37595             return true;
37596         }
37597         
37598         if(this.disabled || valid){
37599             this.markValid();
37600             return true;
37601         }
37602         
37603         this.markInvalid();
37604         return false;
37605         
37606     },
37607     
37608     markValid : function()
37609     {
37610         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37611             this.indicatorEl().removeClass('visible');
37612             this.indicatorEl().addClass('invisible');
37613         }
37614         
37615         
37616         if (Roo.bootstrap.version == 3) {
37617             this.el.removeClass([this.invalidClass, this.validClass]);
37618             this.el.addClass(this.validClass);
37619         } else {
37620             this.el.removeClass(['is-invalid','is-valid']);
37621             this.el.addClass(['is-valid']);
37622         }
37623         this.fireEvent('valid', this);
37624     },
37625     
37626     markInvalid : function(msg)
37627     {
37628         if(this.allowBlank || this.disabled){
37629             return;
37630         }
37631         
37632         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37633             this.indicatorEl().removeClass('invisible');
37634             this.indicatorEl().addClass('visible');
37635         }
37636         if (Roo.bootstrap.version == 3) {
37637             this.el.removeClass([this.invalidClass, this.validClass]);
37638             this.el.addClass(this.invalidClass);
37639         } else {
37640             this.el.removeClass(['is-invalid','is-valid']);
37641             this.el.addClass(['is-invalid']);
37642         }
37643         
37644         this.fireEvent('invalid', this, msg);
37645         
37646     },
37647     
37648     setValue : function(v, suppressEvent)
37649     {   
37650         if(this.value === v){
37651             return;
37652         }
37653         
37654         this.value = v;
37655         
37656         if(this.rendered){
37657             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37658         }
37659         
37660         Roo.each(this.radioes, function(i){
37661             i.checked = false;
37662             i.el.removeClass('checked');
37663         });
37664         
37665         Roo.each(this.radioes, function(i){
37666             
37667             if(i.value === v || i.value.toString() === v.toString()){
37668                 i.checked = true;
37669                 i.el.addClass('checked');
37670                 
37671                 if(suppressEvent !== true){
37672                     this.fireEvent('check', this, i);
37673                 }
37674                 
37675                 return false;
37676             }
37677             
37678         }, this);
37679         
37680         this.validate();
37681     },
37682     
37683     clearInvalid : function(){
37684         
37685         if(!this.el || this.preventMark){
37686             return;
37687         }
37688         
37689         this.el.removeClass([this.invalidClass]);
37690         
37691         this.fireEvent('valid', this);
37692     }
37693     
37694 });
37695
37696 Roo.apply(Roo.bootstrap.RadioSet, {
37697     
37698     groups: {},
37699     
37700     register : function(set)
37701     {
37702         this.groups[set.name] = set;
37703     },
37704     
37705     get: function(name) 
37706     {
37707         if (typeof(this.groups[name]) == 'undefined') {
37708             return false;
37709         }
37710         
37711         return this.groups[name] ;
37712     }
37713     
37714 });
37715 /*
37716  * Based on:
37717  * Ext JS Library 1.1.1
37718  * Copyright(c) 2006-2007, Ext JS, LLC.
37719  *
37720  * Originally Released Under LGPL - original licence link has changed is not relivant.
37721  *
37722  * Fork - LGPL
37723  * <script type="text/javascript">
37724  */
37725
37726
37727 /**
37728  * @class Roo.bootstrap.SplitBar
37729  * @extends Roo.util.Observable
37730  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37731  * <br><br>
37732  * Usage:
37733  * <pre><code>
37734 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37735                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37736 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37737 split.minSize = 100;
37738 split.maxSize = 600;
37739 split.animate = true;
37740 split.on('moved', splitterMoved);
37741 </code></pre>
37742  * @constructor
37743  * Create a new SplitBar
37744  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37745  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37746  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37747  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37748                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37749                         position of the SplitBar).
37750  */
37751 Roo.bootstrap.SplitBar = function(cfg){
37752     
37753     /** @private */
37754     
37755     //{
37756     //  dragElement : elm
37757     //  resizingElement: el,
37758         // optional..
37759     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37760     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37761         // existingProxy ???
37762     //}
37763     
37764     this.el = Roo.get(cfg.dragElement, true);
37765     this.el.dom.unselectable = "on";
37766     /** @private */
37767     this.resizingEl = Roo.get(cfg.resizingElement, true);
37768
37769     /**
37770      * @private
37771      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37772      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37773      * @type Number
37774      */
37775     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37776     
37777     /**
37778      * The minimum size of the resizing element. (Defaults to 0)
37779      * @type Number
37780      */
37781     this.minSize = 0;
37782     
37783     /**
37784      * The maximum size of the resizing element. (Defaults to 2000)
37785      * @type Number
37786      */
37787     this.maxSize = 2000;
37788     
37789     /**
37790      * Whether to animate the transition to the new size
37791      * @type Boolean
37792      */
37793     this.animate = false;
37794     
37795     /**
37796      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37797      * @type Boolean
37798      */
37799     this.useShim = false;
37800     
37801     /** @private */
37802     this.shim = null;
37803     
37804     if(!cfg.existingProxy){
37805         /** @private */
37806         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37807     }else{
37808         this.proxy = Roo.get(cfg.existingProxy).dom;
37809     }
37810     /** @private */
37811     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37812     
37813     /** @private */
37814     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37815     
37816     /** @private */
37817     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37818     
37819     /** @private */
37820     this.dragSpecs = {};
37821     
37822     /**
37823      * @private The adapter to use to positon and resize elements
37824      */
37825     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37826     this.adapter.init(this);
37827     
37828     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37829         /** @private */
37830         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37831         this.el.addClass("roo-splitbar-h");
37832     }else{
37833         /** @private */
37834         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37835         this.el.addClass("roo-splitbar-v");
37836     }
37837     
37838     this.addEvents({
37839         /**
37840          * @event resize
37841          * Fires when the splitter is moved (alias for {@link #event-moved})
37842          * @param {Roo.bootstrap.SplitBar} this
37843          * @param {Number} newSize the new width or height
37844          */
37845         "resize" : true,
37846         /**
37847          * @event moved
37848          * Fires when the splitter is moved
37849          * @param {Roo.bootstrap.SplitBar} this
37850          * @param {Number} newSize the new width or height
37851          */
37852         "moved" : true,
37853         /**
37854          * @event beforeresize
37855          * Fires before the splitter is dragged
37856          * @param {Roo.bootstrap.SplitBar} this
37857          */
37858         "beforeresize" : true,
37859
37860         "beforeapply" : true
37861     });
37862
37863     Roo.util.Observable.call(this);
37864 };
37865
37866 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37867     onStartProxyDrag : function(x, y){
37868         this.fireEvent("beforeresize", this);
37869         if(!this.overlay){
37870             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37871             o.unselectable();
37872             o.enableDisplayMode("block");
37873             // all splitbars share the same overlay
37874             Roo.bootstrap.SplitBar.prototype.overlay = o;
37875         }
37876         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37877         this.overlay.show();
37878         Roo.get(this.proxy).setDisplayed("block");
37879         var size = this.adapter.getElementSize(this);
37880         this.activeMinSize = this.getMinimumSize();;
37881         this.activeMaxSize = this.getMaximumSize();;
37882         var c1 = size - this.activeMinSize;
37883         var c2 = Math.max(this.activeMaxSize - size, 0);
37884         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37885             this.dd.resetConstraints();
37886             this.dd.setXConstraint(
37887                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37888                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37889             );
37890             this.dd.setYConstraint(0, 0);
37891         }else{
37892             this.dd.resetConstraints();
37893             this.dd.setXConstraint(0, 0);
37894             this.dd.setYConstraint(
37895                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37896                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37897             );
37898          }
37899         this.dragSpecs.startSize = size;
37900         this.dragSpecs.startPoint = [x, y];
37901         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37902     },
37903     
37904     /** 
37905      * @private Called after the drag operation by the DDProxy
37906      */
37907     onEndProxyDrag : function(e){
37908         Roo.get(this.proxy).setDisplayed(false);
37909         var endPoint = Roo.lib.Event.getXY(e);
37910         if(this.overlay){
37911             this.overlay.hide();
37912         }
37913         var newSize;
37914         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37915             newSize = this.dragSpecs.startSize + 
37916                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37917                     endPoint[0] - this.dragSpecs.startPoint[0] :
37918                     this.dragSpecs.startPoint[0] - endPoint[0]
37919                 );
37920         }else{
37921             newSize = this.dragSpecs.startSize + 
37922                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37923                     endPoint[1] - this.dragSpecs.startPoint[1] :
37924                     this.dragSpecs.startPoint[1] - endPoint[1]
37925                 );
37926         }
37927         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37928         if(newSize != this.dragSpecs.startSize){
37929             if(this.fireEvent('beforeapply', this, newSize) !== false){
37930                 this.adapter.setElementSize(this, newSize);
37931                 this.fireEvent("moved", this, newSize);
37932                 this.fireEvent("resize", this, newSize);
37933             }
37934         }
37935     },
37936     
37937     /**
37938      * Get the adapter this SplitBar uses
37939      * @return The adapter object
37940      */
37941     getAdapter : function(){
37942         return this.adapter;
37943     },
37944     
37945     /**
37946      * Set the adapter this SplitBar uses
37947      * @param {Object} adapter A SplitBar adapter object
37948      */
37949     setAdapter : function(adapter){
37950         this.adapter = adapter;
37951         this.adapter.init(this);
37952     },
37953     
37954     /**
37955      * Gets the minimum size for the resizing element
37956      * @return {Number} The minimum size
37957      */
37958     getMinimumSize : function(){
37959         return this.minSize;
37960     },
37961     
37962     /**
37963      * Sets the minimum size for the resizing element
37964      * @param {Number} minSize The minimum size
37965      */
37966     setMinimumSize : function(minSize){
37967         this.minSize = minSize;
37968     },
37969     
37970     /**
37971      * Gets the maximum size for the resizing element
37972      * @return {Number} The maximum size
37973      */
37974     getMaximumSize : function(){
37975         return this.maxSize;
37976     },
37977     
37978     /**
37979      * Sets the maximum size for the resizing element
37980      * @param {Number} maxSize The maximum size
37981      */
37982     setMaximumSize : function(maxSize){
37983         this.maxSize = maxSize;
37984     },
37985     
37986     /**
37987      * Sets the initialize size for the resizing element
37988      * @param {Number} size The initial size
37989      */
37990     setCurrentSize : function(size){
37991         var oldAnimate = this.animate;
37992         this.animate = false;
37993         this.adapter.setElementSize(this, size);
37994         this.animate = oldAnimate;
37995     },
37996     
37997     /**
37998      * Destroy this splitbar. 
37999      * @param {Boolean} removeEl True to remove the element
38000      */
38001     destroy : function(removeEl){
38002         if(this.shim){
38003             this.shim.remove();
38004         }
38005         this.dd.unreg();
38006         this.proxy.parentNode.removeChild(this.proxy);
38007         if(removeEl){
38008             this.el.remove();
38009         }
38010     }
38011 });
38012
38013 /**
38014  * @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.
38015  */
38016 Roo.bootstrap.SplitBar.createProxy = function(dir){
38017     var proxy = new Roo.Element(document.createElement("div"));
38018     proxy.unselectable();
38019     var cls = 'roo-splitbar-proxy';
38020     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
38021     document.body.appendChild(proxy.dom);
38022     return proxy.dom;
38023 };
38024
38025 /** 
38026  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
38027  * Default Adapter. It assumes the splitter and resizing element are not positioned
38028  * elements and only gets/sets the width of the element. Generally used for table based layouts.
38029  */
38030 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
38031 };
38032
38033 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
38034     // do nothing for now
38035     init : function(s){
38036     
38037     },
38038     /**
38039      * Called before drag operations to get the current size of the resizing element. 
38040      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38041      */
38042      getElementSize : function(s){
38043         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38044             return s.resizingEl.getWidth();
38045         }else{
38046             return s.resizingEl.getHeight();
38047         }
38048     },
38049     
38050     /**
38051      * Called after drag operations to set the size of the resizing element.
38052      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
38053      * @param {Number} newSize The new size to set
38054      * @param {Function} onComplete A function to be invoked when resizing is complete
38055      */
38056     setElementSize : function(s, newSize, onComplete){
38057         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
38058             if(!s.animate){
38059                 s.resizingEl.setWidth(newSize);
38060                 if(onComplete){
38061                     onComplete(s, newSize);
38062                 }
38063             }else{
38064                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
38065             }
38066         }else{
38067             
38068             if(!s.animate){
38069                 s.resizingEl.setHeight(newSize);
38070                 if(onComplete){
38071                     onComplete(s, newSize);
38072                 }
38073             }else{
38074                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
38075             }
38076         }
38077     }
38078 };
38079
38080 /** 
38081  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
38082  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
38083  * Adapter that  moves the splitter element to align with the resized sizing element. 
38084  * Used with an absolute positioned SplitBar.
38085  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
38086  * document.body, make sure you assign an id to the body element.
38087  */
38088 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
38089     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
38090     this.container = Roo.get(container);
38091 };
38092
38093 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
38094     init : function(s){
38095         this.basic.init(s);
38096     },
38097     
38098     getElementSize : function(s){
38099         return this.basic.getElementSize(s);
38100     },
38101     
38102     setElementSize : function(s, newSize, onComplete){
38103         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
38104     },
38105     
38106     moveSplitter : function(s){
38107         var yes = Roo.bootstrap.SplitBar;
38108         switch(s.placement){
38109             case yes.LEFT:
38110                 s.el.setX(s.resizingEl.getRight());
38111                 break;
38112             case yes.RIGHT:
38113                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
38114                 break;
38115             case yes.TOP:
38116                 s.el.setY(s.resizingEl.getBottom());
38117                 break;
38118             case yes.BOTTOM:
38119                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
38120                 break;
38121         }
38122     }
38123 };
38124
38125 /**
38126  * Orientation constant - Create a vertical SplitBar
38127  * @static
38128  * @type Number
38129  */
38130 Roo.bootstrap.SplitBar.VERTICAL = 1;
38131
38132 /**
38133  * Orientation constant - Create a horizontal SplitBar
38134  * @static
38135  * @type Number
38136  */
38137 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
38138
38139 /**
38140  * Placement constant - The resizing element is to the left of the splitter element
38141  * @static
38142  * @type Number
38143  */
38144 Roo.bootstrap.SplitBar.LEFT = 1;
38145
38146 /**
38147  * Placement constant - The resizing element is to the right of the splitter element
38148  * @static
38149  * @type Number
38150  */
38151 Roo.bootstrap.SplitBar.RIGHT = 2;
38152
38153 /**
38154  * Placement constant - The resizing element is positioned above the splitter element
38155  * @static
38156  * @type Number
38157  */
38158 Roo.bootstrap.SplitBar.TOP = 3;
38159
38160 /**
38161  * Placement constant - The resizing element is positioned under splitter element
38162  * @static
38163  * @type Number
38164  */
38165 Roo.bootstrap.SplitBar.BOTTOM = 4;
38166 Roo.namespace("Roo.bootstrap.layout");/*
38167  * Based on:
38168  * Ext JS Library 1.1.1
38169  * Copyright(c) 2006-2007, Ext JS, LLC.
38170  *
38171  * Originally Released Under LGPL - original licence link has changed is not relivant.
38172  *
38173  * Fork - LGPL
38174  * <script type="text/javascript">
38175  */
38176
38177 /**
38178  * @class Roo.bootstrap.layout.Manager
38179  * @extends Roo.bootstrap.Component
38180  * Base class for layout managers.
38181  */
38182 Roo.bootstrap.layout.Manager = function(config)
38183 {
38184     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
38185
38186
38187
38188
38189
38190     /** false to disable window resize monitoring @type Boolean */
38191     this.monitorWindowResize = true;
38192     this.regions = {};
38193     this.addEvents({
38194         /**
38195          * @event layout
38196          * Fires when a layout is performed.
38197          * @param {Roo.LayoutManager} this
38198          */
38199         "layout" : true,
38200         /**
38201          * @event regionresized
38202          * Fires when the user resizes a region.
38203          * @param {Roo.LayoutRegion} region The resized region
38204          * @param {Number} newSize The new size (width for east/west, height for north/south)
38205          */
38206         "regionresized" : true,
38207         /**
38208          * @event regioncollapsed
38209          * Fires when a region is collapsed.
38210          * @param {Roo.LayoutRegion} region The collapsed region
38211          */
38212         "regioncollapsed" : true,
38213         /**
38214          * @event regionexpanded
38215          * Fires when a region is expanded.
38216          * @param {Roo.LayoutRegion} region The expanded region
38217          */
38218         "regionexpanded" : true
38219     });
38220     this.updating = false;
38221
38222     if (config.el) {
38223         this.el = Roo.get(config.el);
38224         this.initEvents();
38225     }
38226
38227 };
38228
38229 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
38230
38231
38232     regions : null,
38233
38234     monitorWindowResize : true,
38235
38236
38237     updating : false,
38238
38239
38240     onRender : function(ct, position)
38241     {
38242         if(!this.el){
38243             this.el = Roo.get(ct);
38244             this.initEvents();
38245         }
38246         //this.fireEvent('render',this);
38247     },
38248
38249
38250     initEvents: function()
38251     {
38252
38253
38254         // ie scrollbar fix
38255         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
38256             document.body.scroll = "no";
38257         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
38258             this.el.position('relative');
38259         }
38260         this.id = this.el.id;
38261         this.el.addClass("roo-layout-container");
38262         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
38263         if(this.el.dom != document.body ) {
38264             this.el.on('resize', this.layout,this);
38265             this.el.on('show', this.layout,this);
38266         }
38267
38268     },
38269
38270     /**
38271      * Returns true if this layout is currently being updated
38272      * @return {Boolean}
38273      */
38274     isUpdating : function(){
38275         return this.updating;
38276     },
38277
38278     /**
38279      * Suspend the LayoutManager from doing auto-layouts while
38280      * making multiple add or remove calls
38281      */
38282     beginUpdate : function(){
38283         this.updating = true;
38284     },
38285
38286     /**
38287      * Restore auto-layouts and optionally disable the manager from performing a layout
38288      * @param {Boolean} noLayout true to disable a layout update
38289      */
38290     endUpdate : function(noLayout){
38291         this.updating = false;
38292         if(!noLayout){
38293             this.layout();
38294         }
38295     },
38296
38297     layout: function(){
38298         // abstract...
38299     },
38300
38301     onRegionResized : function(region, newSize){
38302         this.fireEvent("regionresized", region, newSize);
38303         this.layout();
38304     },
38305
38306     onRegionCollapsed : function(region){
38307         this.fireEvent("regioncollapsed", region);
38308     },
38309
38310     onRegionExpanded : function(region){
38311         this.fireEvent("regionexpanded", region);
38312     },
38313
38314     /**
38315      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
38316      * performs box-model adjustments.
38317      * @return {Object} The size as an object {width: (the width), height: (the height)}
38318      */
38319     getViewSize : function()
38320     {
38321         var size;
38322         if(this.el.dom != document.body){
38323             size = this.el.getSize();
38324         }else{
38325             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
38326         }
38327         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
38328         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
38329         return size;
38330     },
38331
38332     /**
38333      * Returns the Element this layout is bound to.
38334      * @return {Roo.Element}
38335      */
38336     getEl : function(){
38337         return this.el;
38338     },
38339
38340     /**
38341      * Returns the specified region.
38342      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
38343      * @return {Roo.LayoutRegion}
38344      */
38345     getRegion : function(target){
38346         return this.regions[target.toLowerCase()];
38347     },
38348
38349     onWindowResize : function(){
38350         if(this.monitorWindowResize){
38351             this.layout();
38352         }
38353     }
38354 });
38355 /*
38356  * Based on:
38357  * Ext JS Library 1.1.1
38358  * Copyright(c) 2006-2007, Ext JS, LLC.
38359  *
38360  * Originally Released Under LGPL - original licence link has changed is not relivant.
38361  *
38362  * Fork - LGPL
38363  * <script type="text/javascript">
38364  */
38365 /**
38366  * @class Roo.bootstrap.layout.Border
38367  * @extends Roo.bootstrap.layout.Manager
38368  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
38369  * please see: examples/bootstrap/nested.html<br><br>
38370  
38371 <b>The container the layout is rendered into can be either the body element or any other element.
38372 If it is not the body element, the container needs to either be an absolute positioned element,
38373 or you will need to add "position:relative" to the css of the container.  You will also need to specify
38374 the container size if it is not the body element.</b>
38375
38376 * @constructor
38377 * Create a new Border
38378 * @param {Object} config Configuration options
38379  */
38380 Roo.bootstrap.layout.Border = function(config){
38381     config = config || {};
38382     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
38383     
38384     
38385     
38386     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38387         if(config[region]){
38388             config[region].region = region;
38389             this.addRegion(config[region]);
38390         }
38391     },this);
38392     
38393 };
38394
38395 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
38396
38397 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
38398     
38399     parent : false, // this might point to a 'nest' or a ???
38400     
38401     /**
38402      * Creates and adds a new region if it doesn't already exist.
38403      * @param {String} target The target region key (north, south, east, west or center).
38404      * @param {Object} config The regions config object
38405      * @return {BorderLayoutRegion} The new region
38406      */
38407     addRegion : function(config)
38408     {
38409         if(!this.regions[config.region]){
38410             var r = this.factory(config);
38411             this.bindRegion(r);
38412         }
38413         return this.regions[config.region];
38414     },
38415
38416     // private (kinda)
38417     bindRegion : function(r){
38418         this.regions[r.config.region] = r;
38419         
38420         r.on("visibilitychange",    this.layout, this);
38421         r.on("paneladded",          this.layout, this);
38422         r.on("panelremoved",        this.layout, this);
38423         r.on("invalidated",         this.layout, this);
38424         r.on("resized",             this.onRegionResized, this);
38425         r.on("collapsed",           this.onRegionCollapsed, this);
38426         r.on("expanded",            this.onRegionExpanded, this);
38427     },
38428
38429     /**
38430      * Performs a layout update.
38431      */
38432     layout : function()
38433     {
38434         if(this.updating) {
38435             return;
38436         }
38437         
38438         // render all the rebions if they have not been done alreayd?
38439         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
38440             if(this.regions[region] && !this.regions[region].bodyEl){
38441                 this.regions[region].onRender(this.el)
38442             }
38443         },this);
38444         
38445         var size = this.getViewSize();
38446         var w = size.width;
38447         var h = size.height;
38448         var centerW = w;
38449         var centerH = h;
38450         var centerY = 0;
38451         var centerX = 0;
38452         //var x = 0, y = 0;
38453
38454         var rs = this.regions;
38455         var north = rs["north"];
38456         var south = rs["south"]; 
38457         var west = rs["west"];
38458         var east = rs["east"];
38459         var center = rs["center"];
38460         //if(this.hideOnLayout){ // not supported anymore
38461             //c.el.setStyle("display", "none");
38462         //}
38463         if(north && north.isVisible()){
38464             var b = north.getBox();
38465             var m = north.getMargins();
38466             b.width = w - (m.left+m.right);
38467             b.x = m.left;
38468             b.y = m.top;
38469             centerY = b.height + b.y + m.bottom;
38470             centerH -= centerY;
38471             north.updateBox(this.safeBox(b));
38472         }
38473         if(south && south.isVisible()){
38474             var b = south.getBox();
38475             var m = south.getMargins();
38476             b.width = w - (m.left+m.right);
38477             b.x = m.left;
38478             var totalHeight = (b.height + m.top + m.bottom);
38479             b.y = h - totalHeight + m.top;
38480             centerH -= totalHeight;
38481             south.updateBox(this.safeBox(b));
38482         }
38483         if(west && west.isVisible()){
38484             var b = west.getBox();
38485             var m = west.getMargins();
38486             b.height = centerH - (m.top+m.bottom);
38487             b.x = m.left;
38488             b.y = centerY + m.top;
38489             var totalWidth = (b.width + m.left + m.right);
38490             centerX += totalWidth;
38491             centerW -= totalWidth;
38492             west.updateBox(this.safeBox(b));
38493         }
38494         if(east && east.isVisible()){
38495             var b = east.getBox();
38496             var m = east.getMargins();
38497             b.height = centerH - (m.top+m.bottom);
38498             var totalWidth = (b.width + m.left + m.right);
38499             b.x = w - totalWidth + m.left;
38500             b.y = centerY + m.top;
38501             centerW -= totalWidth;
38502             east.updateBox(this.safeBox(b));
38503         }
38504         if(center){
38505             var m = center.getMargins();
38506             var centerBox = {
38507                 x: centerX + m.left,
38508                 y: centerY + m.top,
38509                 width: centerW - (m.left+m.right),
38510                 height: centerH - (m.top+m.bottom)
38511             };
38512             //if(this.hideOnLayout){
38513                 //center.el.setStyle("display", "block");
38514             //}
38515             center.updateBox(this.safeBox(centerBox));
38516         }
38517         this.el.repaint();
38518         this.fireEvent("layout", this);
38519     },
38520
38521     // private
38522     safeBox : function(box){
38523         box.width = Math.max(0, box.width);
38524         box.height = Math.max(0, box.height);
38525         return box;
38526     },
38527
38528     /**
38529      * Adds a ContentPanel (or subclass) to this layout.
38530      * @param {String} target The target region key (north, south, east, west or center).
38531      * @param {Roo.ContentPanel} panel The panel to add
38532      * @return {Roo.ContentPanel} The added panel
38533      */
38534     add : function(target, panel){
38535          
38536         target = target.toLowerCase();
38537         return this.regions[target].add(panel);
38538     },
38539
38540     /**
38541      * Remove a ContentPanel (or subclass) to this layout.
38542      * @param {String} target The target region key (north, south, east, west or center).
38543      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38544      * @return {Roo.ContentPanel} The removed panel
38545      */
38546     remove : function(target, panel){
38547         target = target.toLowerCase();
38548         return this.regions[target].remove(panel);
38549     },
38550
38551     /**
38552      * Searches all regions for a panel with the specified id
38553      * @param {String} panelId
38554      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38555      */
38556     findPanel : function(panelId){
38557         var rs = this.regions;
38558         for(var target in rs){
38559             if(typeof rs[target] != "function"){
38560                 var p = rs[target].getPanel(panelId);
38561                 if(p){
38562                     return p;
38563                 }
38564             }
38565         }
38566         return null;
38567     },
38568
38569     /**
38570      * Searches all regions for a panel with the specified id and activates (shows) it.
38571      * @param {String/ContentPanel} panelId The panels id or the panel itself
38572      * @return {Roo.ContentPanel} The shown panel or null
38573      */
38574     showPanel : function(panelId) {
38575       var rs = this.regions;
38576       for(var target in rs){
38577          var r = rs[target];
38578          if(typeof r != "function"){
38579             if(r.hasPanel(panelId)){
38580                return r.showPanel(panelId);
38581             }
38582          }
38583       }
38584       return null;
38585    },
38586
38587    /**
38588      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38589      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38590      */
38591    /*
38592     restoreState : function(provider){
38593         if(!provider){
38594             provider = Roo.state.Manager;
38595         }
38596         var sm = new Roo.LayoutStateManager();
38597         sm.init(this, provider);
38598     },
38599 */
38600  
38601  
38602     /**
38603      * Adds a xtype elements to the layout.
38604      * <pre><code>
38605
38606 layout.addxtype({
38607        xtype : 'ContentPanel',
38608        region: 'west',
38609        items: [ .... ]
38610    }
38611 );
38612
38613 layout.addxtype({
38614         xtype : 'NestedLayoutPanel',
38615         region: 'west',
38616         layout: {
38617            center: { },
38618            west: { }   
38619         },
38620         items : [ ... list of content panels or nested layout panels.. ]
38621    }
38622 );
38623 </code></pre>
38624      * @param {Object} cfg Xtype definition of item to add.
38625      */
38626     addxtype : function(cfg)
38627     {
38628         // basically accepts a pannel...
38629         // can accept a layout region..!?!?
38630         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38631         
38632         
38633         // theory?  children can only be panels??
38634         
38635         //if (!cfg.xtype.match(/Panel$/)) {
38636         //    return false;
38637         //}
38638         var ret = false;
38639         
38640         if (typeof(cfg.region) == 'undefined') {
38641             Roo.log("Failed to add Panel, region was not set");
38642             Roo.log(cfg);
38643             return false;
38644         }
38645         var region = cfg.region;
38646         delete cfg.region;
38647         
38648           
38649         var xitems = [];
38650         if (cfg.items) {
38651             xitems = cfg.items;
38652             delete cfg.items;
38653         }
38654         var nb = false;
38655         
38656         if ( region == 'center') {
38657             Roo.log("Center: " + cfg.title);
38658         }
38659         
38660         
38661         switch(cfg.xtype) 
38662         {
38663             case 'Content':  // ContentPanel (el, cfg)
38664             case 'Scroll':  // ContentPanel (el, cfg)
38665             case 'View': 
38666                 cfg.autoCreate = cfg.autoCreate || true;
38667                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38668                 //} else {
38669                 //    var el = this.el.createChild();
38670                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38671                 //}
38672                 
38673                 this.add(region, ret);
38674                 break;
38675             
38676             /*
38677             case 'TreePanel': // our new panel!
38678                 cfg.el = this.el.createChild();
38679                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38680                 this.add(region, ret);
38681                 break;
38682             */
38683             
38684             case 'Nest': 
38685                 // create a new Layout (which is  a Border Layout...
38686                 
38687                 var clayout = cfg.layout;
38688                 clayout.el  = this.el.createChild();
38689                 clayout.items   = clayout.items  || [];
38690                 
38691                 delete cfg.layout;
38692                 
38693                 // replace this exitems with the clayout ones..
38694                 xitems = clayout.items;
38695                  
38696                 // force background off if it's in center...
38697                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38698                     cfg.background = false;
38699                 }
38700                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38701                 
38702                 
38703                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38704                 //console.log('adding nested layout panel '  + cfg.toSource());
38705                 this.add(region, ret);
38706                 nb = {}; /// find first...
38707                 break;
38708             
38709             case 'Grid':
38710                 
38711                 // needs grid and region
38712                 
38713                 //var el = this.getRegion(region).el.createChild();
38714                 /*
38715                  *var el = this.el.createChild();
38716                 // create the grid first...
38717                 cfg.grid.container = el;
38718                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38719                 */
38720                 
38721                 if (region == 'center' && this.active ) {
38722                     cfg.background = false;
38723                 }
38724                 
38725                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38726                 
38727                 this.add(region, ret);
38728                 /*
38729                 if (cfg.background) {
38730                     // render grid on panel activation (if panel background)
38731                     ret.on('activate', function(gp) {
38732                         if (!gp.grid.rendered) {
38733                     //        gp.grid.render(el);
38734                         }
38735                     });
38736                 } else {
38737                   //  cfg.grid.render(el);
38738                 }
38739                 */
38740                 break;
38741            
38742            
38743             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38744                 // it was the old xcomponent building that caused this before.
38745                 // espeically if border is the top element in the tree.
38746                 ret = this;
38747                 break; 
38748                 
38749                     
38750                 
38751                 
38752                 
38753             default:
38754                 /*
38755                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38756                     
38757                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38758                     this.add(region, ret);
38759                 } else {
38760                 */
38761                     Roo.log(cfg);
38762                     throw "Can not add '" + cfg.xtype + "' to Border";
38763                     return null;
38764              
38765                                 
38766              
38767         }
38768         this.beginUpdate();
38769         // add children..
38770         var region = '';
38771         var abn = {};
38772         Roo.each(xitems, function(i)  {
38773             region = nb && i.region ? i.region : false;
38774             
38775             var add = ret.addxtype(i);
38776            
38777             if (region) {
38778                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38779                 if (!i.background) {
38780                     abn[region] = nb[region] ;
38781                 }
38782             }
38783             
38784         });
38785         this.endUpdate();
38786
38787         // make the last non-background panel active..
38788         //if (nb) { Roo.log(abn); }
38789         if (nb) {
38790             
38791             for(var r in abn) {
38792                 region = this.getRegion(r);
38793                 if (region) {
38794                     // tried using nb[r], but it does not work..
38795                      
38796                     region.showPanel(abn[r]);
38797                    
38798                 }
38799             }
38800         }
38801         return ret;
38802         
38803     },
38804     
38805     
38806 // private
38807     factory : function(cfg)
38808     {
38809         
38810         var validRegions = Roo.bootstrap.layout.Border.regions;
38811
38812         var target = cfg.region;
38813         cfg.mgr = this;
38814         
38815         var r = Roo.bootstrap.layout;
38816         Roo.log(target);
38817         switch(target){
38818             case "north":
38819                 return new r.North(cfg);
38820             case "south":
38821                 return new r.South(cfg);
38822             case "east":
38823                 return new r.East(cfg);
38824             case "west":
38825                 return new r.West(cfg);
38826             case "center":
38827                 return new r.Center(cfg);
38828         }
38829         throw 'Layout region "'+target+'" not supported.';
38830     }
38831     
38832     
38833 });
38834  /*
38835  * Based on:
38836  * Ext JS Library 1.1.1
38837  * Copyright(c) 2006-2007, Ext JS, LLC.
38838  *
38839  * Originally Released Under LGPL - original licence link has changed is not relivant.
38840  *
38841  * Fork - LGPL
38842  * <script type="text/javascript">
38843  */
38844  
38845 /**
38846  * @class Roo.bootstrap.layout.Basic
38847  * @extends Roo.util.Observable
38848  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38849  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38850  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38851  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38852  * @cfg {string}   region  the region that it inhabits..
38853  * @cfg {bool}   skipConfig skip config?
38854  * 
38855
38856  */
38857 Roo.bootstrap.layout.Basic = function(config){
38858     
38859     this.mgr = config.mgr;
38860     
38861     this.position = config.region;
38862     
38863     var skipConfig = config.skipConfig;
38864     
38865     this.events = {
38866         /**
38867          * @scope Roo.BasicLayoutRegion
38868          */
38869         
38870         /**
38871          * @event beforeremove
38872          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38873          * @param {Roo.LayoutRegion} this
38874          * @param {Roo.ContentPanel} panel The panel
38875          * @param {Object} e The cancel event object
38876          */
38877         "beforeremove" : true,
38878         /**
38879          * @event invalidated
38880          * Fires when the layout for this region is changed.
38881          * @param {Roo.LayoutRegion} this
38882          */
38883         "invalidated" : true,
38884         /**
38885          * @event visibilitychange
38886          * Fires when this region is shown or hidden 
38887          * @param {Roo.LayoutRegion} this
38888          * @param {Boolean} visibility true or false
38889          */
38890         "visibilitychange" : true,
38891         /**
38892          * @event paneladded
38893          * Fires when a panel is added. 
38894          * @param {Roo.LayoutRegion} this
38895          * @param {Roo.ContentPanel} panel The panel
38896          */
38897         "paneladded" : true,
38898         /**
38899          * @event panelremoved
38900          * Fires when a panel is removed. 
38901          * @param {Roo.LayoutRegion} this
38902          * @param {Roo.ContentPanel} panel The panel
38903          */
38904         "panelremoved" : true,
38905         /**
38906          * @event beforecollapse
38907          * Fires when this region before collapse.
38908          * @param {Roo.LayoutRegion} this
38909          */
38910         "beforecollapse" : true,
38911         /**
38912          * @event collapsed
38913          * Fires when this region is collapsed.
38914          * @param {Roo.LayoutRegion} this
38915          */
38916         "collapsed" : true,
38917         /**
38918          * @event expanded
38919          * Fires when this region is expanded.
38920          * @param {Roo.LayoutRegion} this
38921          */
38922         "expanded" : true,
38923         /**
38924          * @event slideshow
38925          * Fires when this region is slid into view.
38926          * @param {Roo.LayoutRegion} this
38927          */
38928         "slideshow" : true,
38929         /**
38930          * @event slidehide
38931          * Fires when this region slides out of view. 
38932          * @param {Roo.LayoutRegion} this
38933          */
38934         "slidehide" : true,
38935         /**
38936          * @event panelactivated
38937          * Fires when a panel is activated. 
38938          * @param {Roo.LayoutRegion} this
38939          * @param {Roo.ContentPanel} panel The activated panel
38940          */
38941         "panelactivated" : true,
38942         /**
38943          * @event resized
38944          * Fires when the user resizes this region. 
38945          * @param {Roo.LayoutRegion} this
38946          * @param {Number} newSize The new size (width for east/west, height for north/south)
38947          */
38948         "resized" : true
38949     };
38950     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38951     this.panels = new Roo.util.MixedCollection();
38952     this.panels.getKey = this.getPanelId.createDelegate(this);
38953     this.box = null;
38954     this.activePanel = null;
38955     // ensure listeners are added...
38956     
38957     if (config.listeners || config.events) {
38958         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38959             listeners : config.listeners || {},
38960             events : config.events || {}
38961         });
38962     }
38963     
38964     if(skipConfig !== true){
38965         this.applyConfig(config);
38966     }
38967 };
38968
38969 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38970 {
38971     getPanelId : function(p){
38972         return p.getId();
38973     },
38974     
38975     applyConfig : function(config){
38976         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38977         this.config = config;
38978         
38979     },
38980     
38981     /**
38982      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38983      * the width, for horizontal (north, south) the height.
38984      * @param {Number} newSize The new width or height
38985      */
38986     resizeTo : function(newSize){
38987         var el = this.el ? this.el :
38988                  (this.activePanel ? this.activePanel.getEl() : null);
38989         if(el){
38990             switch(this.position){
38991                 case "east":
38992                 case "west":
38993                     el.setWidth(newSize);
38994                     this.fireEvent("resized", this, newSize);
38995                 break;
38996                 case "north":
38997                 case "south":
38998                     el.setHeight(newSize);
38999                     this.fireEvent("resized", this, newSize);
39000                 break;                
39001             }
39002         }
39003     },
39004     
39005     getBox : function(){
39006         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
39007     },
39008     
39009     getMargins : function(){
39010         return this.margins;
39011     },
39012     
39013     updateBox : function(box){
39014         this.box = box;
39015         var el = this.activePanel.getEl();
39016         el.dom.style.left = box.x + "px";
39017         el.dom.style.top = box.y + "px";
39018         this.activePanel.setSize(box.width, box.height);
39019     },
39020     
39021     /**
39022      * Returns the container element for this region.
39023      * @return {Roo.Element}
39024      */
39025     getEl : function(){
39026         return this.activePanel;
39027     },
39028     
39029     /**
39030      * Returns true if this region is currently visible.
39031      * @return {Boolean}
39032      */
39033     isVisible : function(){
39034         return this.activePanel ? true : false;
39035     },
39036     
39037     setActivePanel : function(panel){
39038         panel = this.getPanel(panel);
39039         if(this.activePanel && this.activePanel != panel){
39040             this.activePanel.setActiveState(false);
39041             this.activePanel.getEl().setLeftTop(-10000,-10000);
39042         }
39043         this.activePanel = panel;
39044         panel.setActiveState(true);
39045         if(this.box){
39046             panel.setSize(this.box.width, this.box.height);
39047         }
39048         this.fireEvent("panelactivated", this, panel);
39049         this.fireEvent("invalidated");
39050     },
39051     
39052     /**
39053      * Show the specified panel.
39054      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
39055      * @return {Roo.ContentPanel} The shown panel or null
39056      */
39057     showPanel : function(panel){
39058         panel = this.getPanel(panel);
39059         if(panel){
39060             this.setActivePanel(panel);
39061         }
39062         return panel;
39063     },
39064     
39065     /**
39066      * Get the active panel for this region.
39067      * @return {Roo.ContentPanel} The active panel or null
39068      */
39069     getActivePanel : function(){
39070         return this.activePanel;
39071     },
39072     
39073     /**
39074      * Add the passed ContentPanel(s)
39075      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39076      * @return {Roo.ContentPanel} The panel added (if only one was added)
39077      */
39078     add : function(panel){
39079         if(arguments.length > 1){
39080             for(var i = 0, len = arguments.length; i < len; i++) {
39081                 this.add(arguments[i]);
39082             }
39083             return null;
39084         }
39085         if(this.hasPanel(panel)){
39086             this.showPanel(panel);
39087             return panel;
39088         }
39089         var el = panel.getEl();
39090         if(el.dom.parentNode != this.mgr.el.dom){
39091             this.mgr.el.dom.appendChild(el.dom);
39092         }
39093         if(panel.setRegion){
39094             panel.setRegion(this);
39095         }
39096         this.panels.add(panel);
39097         el.setStyle("position", "absolute");
39098         if(!panel.background){
39099             this.setActivePanel(panel);
39100             if(this.config.initialSize && this.panels.getCount()==1){
39101                 this.resizeTo(this.config.initialSize);
39102             }
39103         }
39104         this.fireEvent("paneladded", this, panel);
39105         return panel;
39106     },
39107     
39108     /**
39109      * Returns true if the panel is in this region.
39110      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39111      * @return {Boolean}
39112      */
39113     hasPanel : function(panel){
39114         if(typeof panel == "object"){ // must be panel obj
39115             panel = panel.getId();
39116         }
39117         return this.getPanel(panel) ? true : false;
39118     },
39119     
39120     /**
39121      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39122      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39123      * @param {Boolean} preservePanel Overrides the config preservePanel option
39124      * @return {Roo.ContentPanel} The panel that was removed
39125      */
39126     remove : function(panel, preservePanel){
39127         panel = this.getPanel(panel);
39128         if(!panel){
39129             return null;
39130         }
39131         var e = {};
39132         this.fireEvent("beforeremove", this, panel, e);
39133         if(e.cancel === true){
39134             return null;
39135         }
39136         var panelId = panel.getId();
39137         this.panels.removeKey(panelId);
39138         return panel;
39139     },
39140     
39141     /**
39142      * Returns the panel specified or null if it's not in this region.
39143      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
39144      * @return {Roo.ContentPanel}
39145      */
39146     getPanel : function(id){
39147         if(typeof id == "object"){ // must be panel obj
39148             return id;
39149         }
39150         return this.panels.get(id);
39151     },
39152     
39153     /**
39154      * Returns this regions position (north/south/east/west/center).
39155      * @return {String} 
39156      */
39157     getPosition: function(){
39158         return this.position;    
39159     }
39160 });/*
39161  * Based on:
39162  * Ext JS Library 1.1.1
39163  * Copyright(c) 2006-2007, Ext JS, LLC.
39164  *
39165  * Originally Released Under LGPL - original licence link has changed is not relivant.
39166  *
39167  * Fork - LGPL
39168  * <script type="text/javascript">
39169  */
39170  
39171 /**
39172  * @class Roo.bootstrap.layout.Region
39173  * @extends Roo.bootstrap.layout.Basic
39174  * This class represents a region in a layout manager.
39175  
39176  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
39177  * @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})
39178  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
39179  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
39180  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
39181  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
39182  * @cfg {String}    title           The title for the region (overrides panel titles)
39183  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
39184  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
39185  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
39186  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
39187  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
39188  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
39189  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
39190  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
39191  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
39192  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
39193
39194  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
39195  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
39196  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
39197  * @cfg {Number}    width           For East/West panels
39198  * @cfg {Number}    height          For North/South panels
39199  * @cfg {Boolean}   split           To show the splitter
39200  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
39201  * 
39202  * @cfg {string}   cls             Extra CSS classes to add to region
39203  * 
39204  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
39205  * @cfg {string}   region  the region that it inhabits..
39206  *
39207
39208  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
39209  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
39210
39211  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
39212  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
39213  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
39214  */
39215 Roo.bootstrap.layout.Region = function(config)
39216 {
39217     this.applyConfig(config);
39218
39219     var mgr = config.mgr;
39220     var pos = config.region;
39221     config.skipConfig = true;
39222     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
39223     
39224     if (mgr.el) {
39225         this.onRender(mgr.el);   
39226     }
39227      
39228     this.visible = true;
39229     this.collapsed = false;
39230     this.unrendered_panels = [];
39231 };
39232
39233 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
39234
39235     position: '', // set by wrapper (eg. north/south etc..)
39236     unrendered_panels : null,  // unrendered panels.
39237     
39238     tabPosition : false,
39239     
39240     mgr: false, // points to 'Border'
39241     
39242     
39243     createBody : function(){
39244         /** This region's body element 
39245         * @type Roo.Element */
39246         this.bodyEl = this.el.createChild({
39247                 tag: "div",
39248                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
39249         });
39250     },
39251
39252     onRender: function(ctr, pos)
39253     {
39254         var dh = Roo.DomHelper;
39255         /** This region's container element 
39256         * @type Roo.Element */
39257         this.el = dh.append(ctr.dom, {
39258                 tag: "div",
39259                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
39260             }, true);
39261         /** This region's title element 
39262         * @type Roo.Element */
39263     
39264         this.titleEl = dh.append(this.el.dom,  {
39265                 tag: "div",
39266                 unselectable: "on",
39267                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
39268                 children:[
39269                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
39270                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
39271                 ]
39272             }, true);
39273         
39274         this.titleEl.enableDisplayMode();
39275         /** This region's title text element 
39276         * @type HTMLElement */
39277         this.titleTextEl = this.titleEl.dom.firstChild;
39278         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
39279         /*
39280         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
39281         this.closeBtn.enableDisplayMode();
39282         this.closeBtn.on("click", this.closeClicked, this);
39283         this.closeBtn.hide();
39284     */
39285         this.createBody(this.config);
39286         if(this.config.hideWhenEmpty){
39287             this.hide();
39288             this.on("paneladded", this.validateVisibility, this);
39289             this.on("panelremoved", this.validateVisibility, this);
39290         }
39291         if(this.autoScroll){
39292             this.bodyEl.setStyle("overflow", "auto");
39293         }else{
39294             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
39295         }
39296         //if(c.titlebar !== false){
39297             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
39298                 this.titleEl.hide();
39299             }else{
39300                 this.titleEl.show();
39301                 if(this.config.title){
39302                     this.titleTextEl.innerHTML = this.config.title;
39303                 }
39304             }
39305         //}
39306         if(this.config.collapsed){
39307             this.collapse(true);
39308         }
39309         if(this.config.hidden){
39310             this.hide();
39311         }
39312         
39313         if (this.unrendered_panels && this.unrendered_panels.length) {
39314             for (var i =0;i< this.unrendered_panels.length; i++) {
39315                 this.add(this.unrendered_panels[i]);
39316             }
39317             this.unrendered_panels = null;
39318             
39319         }
39320         
39321     },
39322     
39323     applyConfig : function(c)
39324     {
39325         /*
39326          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
39327             var dh = Roo.DomHelper;
39328             if(c.titlebar !== false){
39329                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
39330                 this.collapseBtn.on("click", this.collapse, this);
39331                 this.collapseBtn.enableDisplayMode();
39332                 /*
39333                 if(c.showPin === true || this.showPin){
39334                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
39335                     this.stickBtn.enableDisplayMode();
39336                     this.stickBtn.on("click", this.expand, this);
39337                     this.stickBtn.hide();
39338                 }
39339                 
39340             }
39341             */
39342             /** This region's collapsed element
39343             * @type Roo.Element */
39344             /*
39345              *
39346             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
39347                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
39348             ]}, true);
39349             
39350             if(c.floatable !== false){
39351                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
39352                this.collapsedEl.on("click", this.collapseClick, this);
39353             }
39354
39355             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
39356                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
39357                    id: "message", unselectable: "on", style:{"float":"left"}});
39358                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
39359              }
39360             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
39361             this.expandBtn.on("click", this.expand, this);
39362             
39363         }
39364         
39365         if(this.collapseBtn){
39366             this.collapseBtn.setVisible(c.collapsible == true);
39367         }
39368         
39369         this.cmargins = c.cmargins || this.cmargins ||
39370                          (this.position == "west" || this.position == "east" ?
39371                              {top: 0, left: 2, right:2, bottom: 0} :
39372                              {top: 2, left: 0, right:0, bottom: 2});
39373         */
39374         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
39375         
39376         
39377         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
39378         
39379         this.autoScroll = c.autoScroll || false;
39380         
39381         
39382        
39383         
39384         this.duration = c.duration || .30;
39385         this.slideDuration = c.slideDuration || .45;
39386         this.config = c;
39387        
39388     },
39389     /**
39390      * Returns true if this region is currently visible.
39391      * @return {Boolean}
39392      */
39393     isVisible : function(){
39394         return this.visible;
39395     },
39396
39397     /**
39398      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
39399      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
39400      */
39401     //setCollapsedTitle : function(title){
39402     //    title = title || "&#160;";
39403      //   if(this.collapsedTitleTextEl){
39404       //      this.collapsedTitleTextEl.innerHTML = title;
39405        // }
39406     //},
39407
39408     getBox : function(){
39409         var b;
39410       //  if(!this.collapsed){
39411             b = this.el.getBox(false, true);
39412        // }else{
39413           //  b = this.collapsedEl.getBox(false, true);
39414         //}
39415         return b;
39416     },
39417
39418     getMargins : function(){
39419         return this.margins;
39420         //return this.collapsed ? this.cmargins : this.margins;
39421     },
39422 /*
39423     highlight : function(){
39424         this.el.addClass("x-layout-panel-dragover");
39425     },
39426
39427     unhighlight : function(){
39428         this.el.removeClass("x-layout-panel-dragover");
39429     },
39430 */
39431     updateBox : function(box)
39432     {
39433         if (!this.bodyEl) {
39434             return; // not rendered yet..
39435         }
39436         
39437         this.box = box;
39438         if(!this.collapsed){
39439             this.el.dom.style.left = box.x + "px";
39440             this.el.dom.style.top = box.y + "px";
39441             this.updateBody(box.width, box.height);
39442         }else{
39443             this.collapsedEl.dom.style.left = box.x + "px";
39444             this.collapsedEl.dom.style.top = box.y + "px";
39445             this.collapsedEl.setSize(box.width, box.height);
39446         }
39447         if(this.tabs){
39448             this.tabs.autoSizeTabs();
39449         }
39450     },
39451
39452     updateBody : function(w, h)
39453     {
39454         if(w !== null){
39455             this.el.setWidth(w);
39456             w -= this.el.getBorderWidth("rl");
39457             if(this.config.adjustments){
39458                 w += this.config.adjustments[0];
39459             }
39460         }
39461         if(h !== null && h > 0){
39462             this.el.setHeight(h);
39463             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
39464             h -= this.el.getBorderWidth("tb");
39465             if(this.config.adjustments){
39466                 h += this.config.adjustments[1];
39467             }
39468             this.bodyEl.setHeight(h);
39469             if(this.tabs){
39470                 h = this.tabs.syncHeight(h);
39471             }
39472         }
39473         if(this.panelSize){
39474             w = w !== null ? w : this.panelSize.width;
39475             h = h !== null ? h : this.panelSize.height;
39476         }
39477         if(this.activePanel){
39478             var el = this.activePanel.getEl();
39479             w = w !== null ? w : el.getWidth();
39480             h = h !== null ? h : el.getHeight();
39481             this.panelSize = {width: w, height: h};
39482             this.activePanel.setSize(w, h);
39483         }
39484         if(Roo.isIE && this.tabs){
39485             this.tabs.el.repaint();
39486         }
39487     },
39488
39489     /**
39490      * Returns the container element for this region.
39491      * @return {Roo.Element}
39492      */
39493     getEl : function(){
39494         return this.el;
39495     },
39496
39497     /**
39498      * Hides this region.
39499      */
39500     hide : function(){
39501         //if(!this.collapsed){
39502             this.el.dom.style.left = "-2000px";
39503             this.el.hide();
39504         //}else{
39505          //   this.collapsedEl.dom.style.left = "-2000px";
39506          //   this.collapsedEl.hide();
39507        // }
39508         this.visible = false;
39509         this.fireEvent("visibilitychange", this, false);
39510     },
39511
39512     /**
39513      * Shows this region if it was previously hidden.
39514      */
39515     show : function(){
39516         //if(!this.collapsed){
39517             this.el.show();
39518         //}else{
39519         //    this.collapsedEl.show();
39520        // }
39521         this.visible = true;
39522         this.fireEvent("visibilitychange", this, true);
39523     },
39524 /*
39525     closeClicked : function(){
39526         if(this.activePanel){
39527             this.remove(this.activePanel);
39528         }
39529     },
39530
39531     collapseClick : function(e){
39532         if(this.isSlid){
39533            e.stopPropagation();
39534            this.slideIn();
39535         }else{
39536            e.stopPropagation();
39537            this.slideOut();
39538         }
39539     },
39540 */
39541     /**
39542      * Collapses this region.
39543      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39544      */
39545     /*
39546     collapse : function(skipAnim, skipCheck = false){
39547         if(this.collapsed) {
39548             return;
39549         }
39550         
39551         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39552             
39553             this.collapsed = true;
39554             if(this.split){
39555                 this.split.el.hide();
39556             }
39557             if(this.config.animate && skipAnim !== true){
39558                 this.fireEvent("invalidated", this);
39559                 this.animateCollapse();
39560             }else{
39561                 this.el.setLocation(-20000,-20000);
39562                 this.el.hide();
39563                 this.collapsedEl.show();
39564                 this.fireEvent("collapsed", this);
39565                 this.fireEvent("invalidated", this);
39566             }
39567         }
39568         
39569     },
39570 */
39571     animateCollapse : function(){
39572         // overridden
39573     },
39574
39575     /**
39576      * Expands this region if it was previously collapsed.
39577      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39578      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39579      */
39580     /*
39581     expand : function(e, skipAnim){
39582         if(e) {
39583             e.stopPropagation();
39584         }
39585         if(!this.collapsed || this.el.hasActiveFx()) {
39586             return;
39587         }
39588         if(this.isSlid){
39589             this.afterSlideIn();
39590             skipAnim = true;
39591         }
39592         this.collapsed = false;
39593         if(this.config.animate && skipAnim !== true){
39594             this.animateExpand();
39595         }else{
39596             this.el.show();
39597             if(this.split){
39598                 this.split.el.show();
39599             }
39600             this.collapsedEl.setLocation(-2000,-2000);
39601             this.collapsedEl.hide();
39602             this.fireEvent("invalidated", this);
39603             this.fireEvent("expanded", this);
39604         }
39605     },
39606 */
39607     animateExpand : function(){
39608         // overridden
39609     },
39610
39611     initTabs : function()
39612     {
39613         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39614         
39615         var ts = new Roo.bootstrap.panel.Tabs({
39616             el: this.bodyEl.dom,
39617             region : this,
39618             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39619             disableTooltips: this.config.disableTabTips,
39620             toolbar : this.config.toolbar
39621         });
39622         
39623         if(this.config.hideTabs){
39624             ts.stripWrap.setDisplayed(false);
39625         }
39626         this.tabs = ts;
39627         ts.resizeTabs = this.config.resizeTabs === true;
39628         ts.minTabWidth = this.config.minTabWidth || 40;
39629         ts.maxTabWidth = this.config.maxTabWidth || 250;
39630         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39631         ts.monitorResize = false;
39632         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39633         ts.bodyEl.addClass('roo-layout-tabs-body');
39634         this.panels.each(this.initPanelAsTab, this);
39635     },
39636
39637     initPanelAsTab : function(panel){
39638         var ti = this.tabs.addTab(
39639             panel.getEl().id,
39640             panel.getTitle(),
39641             null,
39642             this.config.closeOnTab && panel.isClosable(),
39643             panel.tpl
39644         );
39645         if(panel.tabTip !== undefined){
39646             ti.setTooltip(panel.tabTip);
39647         }
39648         ti.on("activate", function(){
39649               this.setActivePanel(panel);
39650         }, this);
39651         
39652         if(this.config.closeOnTab){
39653             ti.on("beforeclose", function(t, e){
39654                 e.cancel = true;
39655                 this.remove(panel);
39656             }, this);
39657         }
39658         
39659         panel.tabItem = ti;
39660         
39661         return ti;
39662     },
39663
39664     updatePanelTitle : function(panel, title)
39665     {
39666         if(this.activePanel == panel){
39667             this.updateTitle(title);
39668         }
39669         if(this.tabs){
39670             var ti = this.tabs.getTab(panel.getEl().id);
39671             ti.setText(title);
39672             if(panel.tabTip !== undefined){
39673                 ti.setTooltip(panel.tabTip);
39674             }
39675         }
39676     },
39677
39678     updateTitle : function(title){
39679         if(this.titleTextEl && !this.config.title){
39680             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39681         }
39682     },
39683
39684     setActivePanel : function(panel)
39685     {
39686         panel = this.getPanel(panel);
39687         if(this.activePanel && this.activePanel != panel){
39688             if(this.activePanel.setActiveState(false) === false){
39689                 return;
39690             }
39691         }
39692         this.activePanel = panel;
39693         panel.setActiveState(true);
39694         if(this.panelSize){
39695             panel.setSize(this.panelSize.width, this.panelSize.height);
39696         }
39697         if(this.closeBtn){
39698             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39699         }
39700         this.updateTitle(panel.getTitle());
39701         if(this.tabs){
39702             this.fireEvent("invalidated", this);
39703         }
39704         this.fireEvent("panelactivated", this, panel);
39705     },
39706
39707     /**
39708      * Shows the specified panel.
39709      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39710      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39711      */
39712     showPanel : function(panel)
39713     {
39714         panel = this.getPanel(panel);
39715         if(panel){
39716             if(this.tabs){
39717                 var tab = this.tabs.getTab(panel.getEl().id);
39718                 if(tab.isHidden()){
39719                     this.tabs.unhideTab(tab.id);
39720                 }
39721                 tab.activate();
39722             }else{
39723                 this.setActivePanel(panel);
39724             }
39725         }
39726         return panel;
39727     },
39728
39729     /**
39730      * Get the active panel for this region.
39731      * @return {Roo.ContentPanel} The active panel or null
39732      */
39733     getActivePanel : function(){
39734         return this.activePanel;
39735     },
39736
39737     validateVisibility : function(){
39738         if(this.panels.getCount() < 1){
39739             this.updateTitle("&#160;");
39740             this.closeBtn.hide();
39741             this.hide();
39742         }else{
39743             if(!this.isVisible()){
39744                 this.show();
39745             }
39746         }
39747     },
39748
39749     /**
39750      * Adds the passed ContentPanel(s) to this region.
39751      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39752      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39753      */
39754     add : function(panel)
39755     {
39756         if(arguments.length > 1){
39757             for(var i = 0, len = arguments.length; i < len; i++) {
39758                 this.add(arguments[i]);
39759             }
39760             return null;
39761         }
39762         
39763         // if we have not been rendered yet, then we can not really do much of this..
39764         if (!this.bodyEl) {
39765             this.unrendered_panels.push(panel);
39766             return panel;
39767         }
39768         
39769         
39770         
39771         
39772         if(this.hasPanel(panel)){
39773             this.showPanel(panel);
39774             return panel;
39775         }
39776         panel.setRegion(this);
39777         this.panels.add(panel);
39778        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39779             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39780             // and hide them... ???
39781             this.bodyEl.dom.appendChild(panel.getEl().dom);
39782             if(panel.background !== true){
39783                 this.setActivePanel(panel);
39784             }
39785             this.fireEvent("paneladded", this, panel);
39786             return panel;
39787         }
39788         */
39789         if(!this.tabs){
39790             this.initTabs();
39791         }else{
39792             this.initPanelAsTab(panel);
39793         }
39794         
39795         
39796         if(panel.background !== true){
39797             this.tabs.activate(panel.getEl().id);
39798         }
39799         this.fireEvent("paneladded", this, panel);
39800         return panel;
39801     },
39802
39803     /**
39804      * Hides the tab for the specified panel.
39805      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39806      */
39807     hidePanel : function(panel){
39808         if(this.tabs && (panel = this.getPanel(panel))){
39809             this.tabs.hideTab(panel.getEl().id);
39810         }
39811     },
39812
39813     /**
39814      * Unhides the tab for a previously hidden panel.
39815      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39816      */
39817     unhidePanel : function(panel){
39818         if(this.tabs && (panel = this.getPanel(panel))){
39819             this.tabs.unhideTab(panel.getEl().id);
39820         }
39821     },
39822
39823     clearPanels : function(){
39824         while(this.panels.getCount() > 0){
39825              this.remove(this.panels.first());
39826         }
39827     },
39828
39829     /**
39830      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39831      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39832      * @param {Boolean} preservePanel Overrides the config preservePanel option
39833      * @return {Roo.ContentPanel} The panel that was removed
39834      */
39835     remove : function(panel, preservePanel)
39836     {
39837         panel = this.getPanel(panel);
39838         if(!panel){
39839             return null;
39840         }
39841         var e = {};
39842         this.fireEvent("beforeremove", this, panel, e);
39843         if(e.cancel === true){
39844             return null;
39845         }
39846         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39847         var panelId = panel.getId();
39848         this.panels.removeKey(panelId);
39849         if(preservePanel){
39850             document.body.appendChild(panel.getEl().dom);
39851         }
39852         if(this.tabs){
39853             this.tabs.removeTab(panel.getEl().id);
39854         }else if (!preservePanel){
39855             this.bodyEl.dom.removeChild(panel.getEl().dom);
39856         }
39857         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39858             var p = this.panels.first();
39859             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39860             tempEl.appendChild(p.getEl().dom);
39861             this.bodyEl.update("");
39862             this.bodyEl.dom.appendChild(p.getEl().dom);
39863             tempEl = null;
39864             this.updateTitle(p.getTitle());
39865             this.tabs = null;
39866             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39867             this.setActivePanel(p);
39868         }
39869         panel.setRegion(null);
39870         if(this.activePanel == panel){
39871             this.activePanel = null;
39872         }
39873         if(this.config.autoDestroy !== false && preservePanel !== true){
39874             try{panel.destroy();}catch(e){}
39875         }
39876         this.fireEvent("panelremoved", this, panel);
39877         return panel;
39878     },
39879
39880     /**
39881      * Returns the TabPanel component used by this region
39882      * @return {Roo.TabPanel}
39883      */
39884     getTabs : function(){
39885         return this.tabs;
39886     },
39887
39888     createTool : function(parentEl, className){
39889         var btn = Roo.DomHelper.append(parentEl, {
39890             tag: "div",
39891             cls: "x-layout-tools-button",
39892             children: [ {
39893                 tag: "div",
39894                 cls: "roo-layout-tools-button-inner " + className,
39895                 html: "&#160;"
39896             }]
39897         }, true);
39898         btn.addClassOnOver("roo-layout-tools-button-over");
39899         return btn;
39900     }
39901 });/*
39902  * Based on:
39903  * Ext JS Library 1.1.1
39904  * Copyright(c) 2006-2007, Ext JS, LLC.
39905  *
39906  * Originally Released Under LGPL - original licence link has changed is not relivant.
39907  *
39908  * Fork - LGPL
39909  * <script type="text/javascript">
39910  */
39911  
39912
39913
39914 /**
39915  * @class Roo.SplitLayoutRegion
39916  * @extends Roo.LayoutRegion
39917  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39918  */
39919 Roo.bootstrap.layout.Split = function(config){
39920     this.cursor = config.cursor;
39921     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39922 };
39923
39924 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39925 {
39926     splitTip : "Drag to resize.",
39927     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39928     useSplitTips : false,
39929
39930     applyConfig : function(config){
39931         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39932     },
39933     
39934     onRender : function(ctr,pos) {
39935         
39936         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39937         if(!this.config.split){
39938             return;
39939         }
39940         if(!this.split){
39941             
39942             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39943                             tag: "div",
39944                             id: this.el.id + "-split",
39945                             cls: "roo-layout-split roo-layout-split-"+this.position,
39946                             html: "&#160;"
39947             });
39948             /** The SplitBar for this region 
39949             * @type Roo.SplitBar */
39950             // does not exist yet...
39951             Roo.log([this.position, this.orientation]);
39952             
39953             this.split = new Roo.bootstrap.SplitBar({
39954                 dragElement : splitEl,
39955                 resizingElement: this.el,
39956                 orientation : this.orientation
39957             });
39958             
39959             this.split.on("moved", this.onSplitMove, this);
39960             this.split.useShim = this.config.useShim === true;
39961             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39962             if(this.useSplitTips){
39963                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39964             }
39965             //if(config.collapsible){
39966             //    this.split.el.on("dblclick", this.collapse,  this);
39967             //}
39968         }
39969         if(typeof this.config.minSize != "undefined"){
39970             this.split.minSize = this.config.minSize;
39971         }
39972         if(typeof this.config.maxSize != "undefined"){
39973             this.split.maxSize = this.config.maxSize;
39974         }
39975         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39976             this.hideSplitter();
39977         }
39978         
39979     },
39980
39981     getHMaxSize : function(){
39982          var cmax = this.config.maxSize || 10000;
39983          var center = this.mgr.getRegion("center");
39984          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39985     },
39986
39987     getVMaxSize : function(){
39988          var cmax = this.config.maxSize || 10000;
39989          var center = this.mgr.getRegion("center");
39990          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39991     },
39992
39993     onSplitMove : function(split, newSize){
39994         this.fireEvent("resized", this, newSize);
39995     },
39996     
39997     /** 
39998      * Returns the {@link Roo.SplitBar} for this region.
39999      * @return {Roo.SplitBar}
40000      */
40001     getSplitBar : function(){
40002         return this.split;
40003     },
40004     
40005     hide : function(){
40006         this.hideSplitter();
40007         Roo.bootstrap.layout.Split.superclass.hide.call(this);
40008     },
40009
40010     hideSplitter : function(){
40011         if(this.split){
40012             this.split.el.setLocation(-2000,-2000);
40013             this.split.el.hide();
40014         }
40015     },
40016
40017     show : function(){
40018         if(this.split){
40019             this.split.el.show();
40020         }
40021         Roo.bootstrap.layout.Split.superclass.show.call(this);
40022     },
40023     
40024     beforeSlide: function(){
40025         if(Roo.isGecko){// firefox overflow auto bug workaround
40026             this.bodyEl.clip();
40027             if(this.tabs) {
40028                 this.tabs.bodyEl.clip();
40029             }
40030             if(this.activePanel){
40031                 this.activePanel.getEl().clip();
40032                 
40033                 if(this.activePanel.beforeSlide){
40034                     this.activePanel.beforeSlide();
40035                 }
40036             }
40037         }
40038     },
40039     
40040     afterSlide : function(){
40041         if(Roo.isGecko){// firefox overflow auto bug workaround
40042             this.bodyEl.unclip();
40043             if(this.tabs) {
40044                 this.tabs.bodyEl.unclip();
40045             }
40046             if(this.activePanel){
40047                 this.activePanel.getEl().unclip();
40048                 if(this.activePanel.afterSlide){
40049                     this.activePanel.afterSlide();
40050                 }
40051             }
40052         }
40053     },
40054
40055     initAutoHide : function(){
40056         if(this.autoHide !== false){
40057             if(!this.autoHideHd){
40058                 var st = new Roo.util.DelayedTask(this.slideIn, this);
40059                 this.autoHideHd = {
40060                     "mouseout": function(e){
40061                         if(!e.within(this.el, true)){
40062                             st.delay(500);
40063                         }
40064                     },
40065                     "mouseover" : function(e){
40066                         st.cancel();
40067                     },
40068                     scope : this
40069                 };
40070             }
40071             this.el.on(this.autoHideHd);
40072         }
40073     },
40074
40075     clearAutoHide : function(){
40076         if(this.autoHide !== false){
40077             this.el.un("mouseout", this.autoHideHd.mouseout);
40078             this.el.un("mouseover", this.autoHideHd.mouseover);
40079         }
40080     },
40081
40082     clearMonitor : function(){
40083         Roo.get(document).un("click", this.slideInIf, this);
40084     },
40085
40086     // these names are backwards but not changed for compat
40087     slideOut : function(){
40088         if(this.isSlid || this.el.hasActiveFx()){
40089             return;
40090         }
40091         this.isSlid = true;
40092         if(this.collapseBtn){
40093             this.collapseBtn.hide();
40094         }
40095         this.closeBtnState = this.closeBtn.getStyle('display');
40096         this.closeBtn.hide();
40097         if(this.stickBtn){
40098             this.stickBtn.show();
40099         }
40100         this.el.show();
40101         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
40102         this.beforeSlide();
40103         this.el.setStyle("z-index", 10001);
40104         this.el.slideIn(this.getSlideAnchor(), {
40105             callback: function(){
40106                 this.afterSlide();
40107                 this.initAutoHide();
40108                 Roo.get(document).on("click", this.slideInIf, this);
40109                 this.fireEvent("slideshow", this);
40110             },
40111             scope: this,
40112             block: true
40113         });
40114     },
40115
40116     afterSlideIn : function(){
40117         this.clearAutoHide();
40118         this.isSlid = false;
40119         this.clearMonitor();
40120         this.el.setStyle("z-index", "");
40121         if(this.collapseBtn){
40122             this.collapseBtn.show();
40123         }
40124         this.closeBtn.setStyle('display', this.closeBtnState);
40125         if(this.stickBtn){
40126             this.stickBtn.hide();
40127         }
40128         this.fireEvent("slidehide", this);
40129     },
40130
40131     slideIn : function(cb){
40132         if(!this.isSlid || this.el.hasActiveFx()){
40133             Roo.callback(cb);
40134             return;
40135         }
40136         this.isSlid = false;
40137         this.beforeSlide();
40138         this.el.slideOut(this.getSlideAnchor(), {
40139             callback: function(){
40140                 this.el.setLeftTop(-10000, -10000);
40141                 this.afterSlide();
40142                 this.afterSlideIn();
40143                 Roo.callback(cb);
40144             },
40145             scope: this,
40146             block: true
40147         });
40148     },
40149     
40150     slideInIf : function(e){
40151         if(!e.within(this.el)){
40152             this.slideIn();
40153         }
40154     },
40155
40156     animateCollapse : function(){
40157         this.beforeSlide();
40158         this.el.setStyle("z-index", 20000);
40159         var anchor = this.getSlideAnchor();
40160         this.el.slideOut(anchor, {
40161             callback : function(){
40162                 this.el.setStyle("z-index", "");
40163                 this.collapsedEl.slideIn(anchor, {duration:.3});
40164                 this.afterSlide();
40165                 this.el.setLocation(-10000,-10000);
40166                 this.el.hide();
40167                 this.fireEvent("collapsed", this);
40168             },
40169             scope: this,
40170             block: true
40171         });
40172     },
40173
40174     animateExpand : function(){
40175         this.beforeSlide();
40176         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
40177         this.el.setStyle("z-index", 20000);
40178         this.collapsedEl.hide({
40179             duration:.1
40180         });
40181         this.el.slideIn(this.getSlideAnchor(), {
40182             callback : function(){
40183                 this.el.setStyle("z-index", "");
40184                 this.afterSlide();
40185                 if(this.split){
40186                     this.split.el.show();
40187                 }
40188                 this.fireEvent("invalidated", this);
40189                 this.fireEvent("expanded", this);
40190             },
40191             scope: this,
40192             block: true
40193         });
40194     },
40195
40196     anchors : {
40197         "west" : "left",
40198         "east" : "right",
40199         "north" : "top",
40200         "south" : "bottom"
40201     },
40202
40203     sanchors : {
40204         "west" : "l",
40205         "east" : "r",
40206         "north" : "t",
40207         "south" : "b"
40208     },
40209
40210     canchors : {
40211         "west" : "tl-tr",
40212         "east" : "tr-tl",
40213         "north" : "tl-bl",
40214         "south" : "bl-tl"
40215     },
40216
40217     getAnchor : function(){
40218         return this.anchors[this.position];
40219     },
40220
40221     getCollapseAnchor : function(){
40222         return this.canchors[this.position];
40223     },
40224
40225     getSlideAnchor : function(){
40226         return this.sanchors[this.position];
40227     },
40228
40229     getAlignAdj : function(){
40230         var cm = this.cmargins;
40231         switch(this.position){
40232             case "west":
40233                 return [0, 0];
40234             break;
40235             case "east":
40236                 return [0, 0];
40237             break;
40238             case "north":
40239                 return [0, 0];
40240             break;
40241             case "south":
40242                 return [0, 0];
40243             break;
40244         }
40245     },
40246
40247     getExpandAdj : function(){
40248         var c = this.collapsedEl, cm = this.cmargins;
40249         switch(this.position){
40250             case "west":
40251                 return [-(cm.right+c.getWidth()+cm.left), 0];
40252             break;
40253             case "east":
40254                 return [cm.right+c.getWidth()+cm.left, 0];
40255             break;
40256             case "north":
40257                 return [0, -(cm.top+cm.bottom+c.getHeight())];
40258             break;
40259             case "south":
40260                 return [0, cm.top+cm.bottom+c.getHeight()];
40261             break;
40262         }
40263     }
40264 });/*
40265  * Based on:
40266  * Ext JS Library 1.1.1
40267  * Copyright(c) 2006-2007, Ext JS, LLC.
40268  *
40269  * Originally Released Under LGPL - original licence link has changed is not relivant.
40270  *
40271  * Fork - LGPL
40272  * <script type="text/javascript">
40273  */
40274 /*
40275  * These classes are private internal classes
40276  */
40277 Roo.bootstrap.layout.Center = function(config){
40278     config.region = "center";
40279     Roo.bootstrap.layout.Region.call(this, config);
40280     this.visible = true;
40281     this.minWidth = config.minWidth || 20;
40282     this.minHeight = config.minHeight || 20;
40283 };
40284
40285 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
40286     hide : function(){
40287         // center panel can't be hidden
40288     },
40289     
40290     show : function(){
40291         // center panel can't be hidden
40292     },
40293     
40294     getMinWidth: function(){
40295         return this.minWidth;
40296     },
40297     
40298     getMinHeight: function(){
40299         return this.minHeight;
40300     }
40301 });
40302
40303
40304
40305
40306  
40307
40308
40309
40310
40311
40312
40313 Roo.bootstrap.layout.North = function(config)
40314 {
40315     config.region = 'north';
40316     config.cursor = 'n-resize';
40317     
40318     Roo.bootstrap.layout.Split.call(this, config);
40319     
40320     
40321     if(this.split){
40322         this.split.placement = Roo.bootstrap.SplitBar.TOP;
40323         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40324         this.split.el.addClass("roo-layout-split-v");
40325     }
40326     //var size = config.initialSize || config.height;
40327     //if(this.el && typeof size != "undefined"){
40328     //    this.el.setHeight(size);
40329     //}
40330 };
40331 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
40332 {
40333     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40334      
40335      
40336     onRender : function(ctr, pos)
40337     {
40338         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40339         var size = this.config.initialSize || this.config.height;
40340         if(this.el && typeof size != "undefined"){
40341             this.el.setHeight(size);
40342         }
40343     
40344     },
40345     
40346     getBox : function(){
40347         if(this.collapsed){
40348             return this.collapsedEl.getBox();
40349         }
40350         var box = this.el.getBox();
40351         if(this.split){
40352             box.height += this.split.el.getHeight();
40353         }
40354         return box;
40355     },
40356     
40357     updateBox : function(box){
40358         if(this.split && !this.collapsed){
40359             box.height -= this.split.el.getHeight();
40360             this.split.el.setLeft(box.x);
40361             this.split.el.setTop(box.y+box.height);
40362             this.split.el.setWidth(box.width);
40363         }
40364         if(this.collapsed){
40365             this.updateBody(box.width, null);
40366         }
40367         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40368     }
40369 });
40370
40371
40372
40373
40374
40375 Roo.bootstrap.layout.South = function(config){
40376     config.region = 'south';
40377     config.cursor = 's-resize';
40378     Roo.bootstrap.layout.Split.call(this, config);
40379     if(this.split){
40380         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
40381         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
40382         this.split.el.addClass("roo-layout-split-v");
40383     }
40384     
40385 };
40386
40387 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
40388     orientation: Roo.bootstrap.SplitBar.VERTICAL,
40389     
40390     onRender : function(ctr, pos)
40391     {
40392         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40393         var size = this.config.initialSize || this.config.height;
40394         if(this.el && typeof size != "undefined"){
40395             this.el.setHeight(size);
40396         }
40397     
40398     },
40399     
40400     getBox : function(){
40401         if(this.collapsed){
40402             return this.collapsedEl.getBox();
40403         }
40404         var box = this.el.getBox();
40405         if(this.split){
40406             var sh = this.split.el.getHeight();
40407             box.height += sh;
40408             box.y -= sh;
40409         }
40410         return box;
40411     },
40412     
40413     updateBox : function(box){
40414         if(this.split && !this.collapsed){
40415             var sh = this.split.el.getHeight();
40416             box.height -= sh;
40417             box.y += sh;
40418             this.split.el.setLeft(box.x);
40419             this.split.el.setTop(box.y-sh);
40420             this.split.el.setWidth(box.width);
40421         }
40422         if(this.collapsed){
40423             this.updateBody(box.width, null);
40424         }
40425         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40426     }
40427 });
40428
40429 Roo.bootstrap.layout.East = function(config){
40430     config.region = "east";
40431     config.cursor = "e-resize";
40432     Roo.bootstrap.layout.Split.call(this, config);
40433     if(this.split){
40434         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
40435         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40436         this.split.el.addClass("roo-layout-split-h");
40437     }
40438     
40439 };
40440 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
40441     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40442     
40443     onRender : function(ctr, pos)
40444     {
40445         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
40446         var size = this.config.initialSize || this.config.width;
40447         if(this.el && typeof size != "undefined"){
40448             this.el.setWidth(size);
40449         }
40450     
40451     },
40452     
40453     getBox : function(){
40454         if(this.collapsed){
40455             return this.collapsedEl.getBox();
40456         }
40457         var box = this.el.getBox();
40458         if(this.split){
40459             var sw = this.split.el.getWidth();
40460             box.width += sw;
40461             box.x -= sw;
40462         }
40463         return box;
40464     },
40465
40466     updateBox : function(box){
40467         if(this.split && !this.collapsed){
40468             var sw = this.split.el.getWidth();
40469             box.width -= sw;
40470             this.split.el.setLeft(box.x);
40471             this.split.el.setTop(box.y);
40472             this.split.el.setHeight(box.height);
40473             box.x += sw;
40474         }
40475         if(this.collapsed){
40476             this.updateBody(null, box.height);
40477         }
40478         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40479     }
40480 });
40481
40482 Roo.bootstrap.layout.West = function(config){
40483     config.region = "west";
40484     config.cursor = "w-resize";
40485     
40486     Roo.bootstrap.layout.Split.call(this, config);
40487     if(this.split){
40488         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40489         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40490         this.split.el.addClass("roo-layout-split-h");
40491     }
40492     
40493 };
40494 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40495     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40496     
40497     onRender: function(ctr, pos)
40498     {
40499         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40500         var size = this.config.initialSize || this.config.width;
40501         if(typeof size != "undefined"){
40502             this.el.setWidth(size);
40503         }
40504     },
40505     
40506     getBox : function(){
40507         if(this.collapsed){
40508             return this.collapsedEl.getBox();
40509         }
40510         var box = this.el.getBox();
40511         if (box.width == 0) {
40512             box.width = this.config.width; // kludge?
40513         }
40514         if(this.split){
40515             box.width += this.split.el.getWidth();
40516         }
40517         return box;
40518     },
40519     
40520     updateBox : function(box){
40521         if(this.split && !this.collapsed){
40522             var sw = this.split.el.getWidth();
40523             box.width -= sw;
40524             this.split.el.setLeft(box.x+box.width);
40525             this.split.el.setTop(box.y);
40526             this.split.el.setHeight(box.height);
40527         }
40528         if(this.collapsed){
40529             this.updateBody(null, box.height);
40530         }
40531         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40532     }
40533 });Roo.namespace("Roo.bootstrap.panel");/*
40534  * Based on:
40535  * Ext JS Library 1.1.1
40536  * Copyright(c) 2006-2007, Ext JS, LLC.
40537  *
40538  * Originally Released Under LGPL - original licence link has changed is not relivant.
40539  *
40540  * Fork - LGPL
40541  * <script type="text/javascript">
40542  */
40543 /**
40544  * @class Roo.ContentPanel
40545  * @extends Roo.util.Observable
40546  * A basic ContentPanel element.
40547  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40548  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40549  * @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
40550  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40551  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40552  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40553  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40554  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40555  * @cfg {String} title          The title for this panel
40556  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40557  * @cfg {String} url            Calls {@link #setUrl} with this value
40558  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40559  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40560  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40561  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40562  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40563  * @cfg {Boolean} badges render the badges
40564  * @cfg {String} cls  extra classes to use  
40565  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40566
40567  * @constructor
40568  * Create a new ContentPanel.
40569  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40570  * @param {String/Object} config A string to set only the title or a config object
40571  * @param {String} content (optional) Set the HTML content for this panel
40572  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40573  */
40574 Roo.bootstrap.panel.Content = function( config){
40575     
40576     this.tpl = config.tpl || false;
40577     
40578     var el = config.el;
40579     var content = config.content;
40580
40581     if(config.autoCreate){ // xtype is available if this is called from factory
40582         el = Roo.id();
40583     }
40584     this.el = Roo.get(el);
40585     if(!this.el && config && config.autoCreate){
40586         if(typeof config.autoCreate == "object"){
40587             if(!config.autoCreate.id){
40588                 config.autoCreate.id = config.id||el;
40589             }
40590             this.el = Roo.DomHelper.append(document.body,
40591                         config.autoCreate, true);
40592         }else{
40593             var elcfg =  {
40594                 tag: "div",
40595                 cls: (config.cls || '') +
40596                     (config.background ? ' bg-' + config.background : '') +
40597                     " roo-layout-inactive-content",
40598                 id: config.id||el
40599             };
40600             if (config.iframe) {
40601                 elcfg.cn = [
40602                     {
40603                         tag : 'iframe',
40604                         style : 'border: 0px',
40605                         src : 'about:blank'
40606                     }
40607                 ];
40608             }
40609               
40610             if (config.html) {
40611                 elcfg.html = config.html;
40612                 
40613             }
40614                         
40615             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40616             if (config.iframe) {
40617                 this.iframeEl = this.el.select('iframe',true).first();
40618             }
40619             
40620         }
40621     } 
40622     this.closable = false;
40623     this.loaded = false;
40624     this.active = false;
40625    
40626       
40627     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40628         
40629         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40630         
40631         this.wrapEl = this.el; //this.el.wrap();
40632         var ti = [];
40633         if (config.toolbar.items) {
40634             ti = config.toolbar.items ;
40635             delete config.toolbar.items ;
40636         }
40637         
40638         var nitems = [];
40639         this.toolbar.render(this.wrapEl, 'before');
40640         for(var i =0;i < ti.length;i++) {
40641           //  Roo.log(['add child', items[i]]);
40642             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40643         }
40644         this.toolbar.items = nitems;
40645         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40646         delete config.toolbar;
40647         
40648     }
40649     /*
40650     // xtype created footer. - not sure if will work as we normally have to render first..
40651     if (this.footer && !this.footer.el && this.footer.xtype) {
40652         if (!this.wrapEl) {
40653             this.wrapEl = this.el.wrap();
40654         }
40655     
40656         this.footer.container = this.wrapEl.createChild();
40657          
40658         this.footer = Roo.factory(this.footer, Roo);
40659         
40660     }
40661     */
40662     
40663      if(typeof config == "string"){
40664         this.title = config;
40665     }else{
40666         Roo.apply(this, config);
40667     }
40668     
40669     if(this.resizeEl){
40670         this.resizeEl = Roo.get(this.resizeEl, true);
40671     }else{
40672         this.resizeEl = this.el;
40673     }
40674     // handle view.xtype
40675     
40676  
40677     
40678     
40679     this.addEvents({
40680         /**
40681          * @event activate
40682          * Fires when this panel is activated. 
40683          * @param {Roo.ContentPanel} this
40684          */
40685         "activate" : true,
40686         /**
40687          * @event deactivate
40688          * Fires when this panel is activated. 
40689          * @param {Roo.ContentPanel} this
40690          */
40691         "deactivate" : true,
40692
40693         /**
40694          * @event resize
40695          * Fires when this panel is resized if fitToFrame is true.
40696          * @param {Roo.ContentPanel} this
40697          * @param {Number} width The width after any component adjustments
40698          * @param {Number} height The height after any component adjustments
40699          */
40700         "resize" : true,
40701         
40702          /**
40703          * @event render
40704          * Fires when this tab is created
40705          * @param {Roo.ContentPanel} this
40706          */
40707         "render" : true,
40708         
40709           /**
40710          * @event scroll
40711          * Fires when this content is scrolled
40712          * @param {Roo.ContentPanel} this
40713          * @param {Event} scrollEvent
40714          */
40715         "scroll" : true
40716         
40717         
40718         
40719     });
40720     
40721
40722     
40723     
40724     if(this.autoScroll && !this.iframe){
40725         this.resizeEl.setStyle("overflow", "auto");
40726         this.resizeEl.on('scroll', this.onScroll, this);
40727     } else {
40728         // fix randome scrolling
40729         //this.el.on('scroll', function() {
40730         //    Roo.log('fix random scolling');
40731         //    this.scrollTo('top',0); 
40732         //});
40733     }
40734     content = content || this.content;
40735     if(content){
40736         this.setContent(content);
40737     }
40738     if(config && config.url){
40739         this.setUrl(this.url, this.params, this.loadOnce);
40740     }
40741     
40742     
40743     
40744     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40745     
40746     if (this.view && typeof(this.view.xtype) != 'undefined') {
40747         this.view.el = this.el.appendChild(document.createElement("div"));
40748         this.view = Roo.factory(this.view); 
40749         this.view.render  &&  this.view.render(false, '');  
40750     }
40751     
40752     
40753     this.fireEvent('render', this);
40754 };
40755
40756 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40757     
40758     cls : '',
40759     background : '',
40760     
40761     tabTip : '',
40762     
40763     iframe : false,
40764     iframeEl : false,
40765     
40766     /* Resize Element - use this to work out scroll etc. */
40767     resizeEl : false,
40768     
40769     setRegion : function(region){
40770         this.region = region;
40771         this.setActiveClass(region && !this.background);
40772     },
40773     
40774     
40775     setActiveClass: function(state)
40776     {
40777         if(state){
40778            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40779            this.el.setStyle('position','relative');
40780         }else{
40781            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40782            this.el.setStyle('position', 'absolute');
40783         } 
40784     },
40785     
40786     /**
40787      * Returns the toolbar for this Panel if one was configured. 
40788      * @return {Roo.Toolbar} 
40789      */
40790     getToolbar : function(){
40791         return this.toolbar;
40792     },
40793     
40794     setActiveState : function(active)
40795     {
40796         this.active = active;
40797         this.setActiveClass(active);
40798         if(!active){
40799             if(this.fireEvent("deactivate", this) === false){
40800                 return false;
40801             }
40802             return true;
40803         }
40804         this.fireEvent("activate", this);
40805         return true;
40806     },
40807     /**
40808      * Updates this panel's element (not for iframe)
40809      * @param {String} content The new content
40810      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40811     */
40812     setContent : function(content, loadScripts){
40813         if (this.iframe) {
40814             return;
40815         }
40816         
40817         this.el.update(content, loadScripts);
40818     },
40819
40820     ignoreResize : function(w, h){
40821         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40822             return true;
40823         }else{
40824             this.lastSize = {width: w, height: h};
40825             return false;
40826         }
40827     },
40828     /**
40829      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40830      * @return {Roo.UpdateManager} The UpdateManager
40831      */
40832     getUpdateManager : function(){
40833         if (this.iframe) {
40834             return false;
40835         }
40836         return this.el.getUpdateManager();
40837     },
40838      /**
40839      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40840      * Does not work with IFRAME contents
40841      * @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:
40842 <pre><code>
40843 panel.load({
40844     url: "your-url.php",
40845     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40846     callback: yourFunction,
40847     scope: yourObject, //(optional scope)
40848     discardUrl: false,
40849     nocache: false,
40850     text: "Loading...",
40851     timeout: 30,
40852     scripts: false
40853 });
40854 </code></pre>
40855      
40856      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40857      * 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.
40858      * @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}
40859      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40860      * @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.
40861      * @return {Roo.ContentPanel} this
40862      */
40863     load : function(){
40864         
40865         if (this.iframe) {
40866             return this;
40867         }
40868         
40869         var um = this.el.getUpdateManager();
40870         um.update.apply(um, arguments);
40871         return this;
40872     },
40873
40874
40875     /**
40876      * 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.
40877      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40878      * @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)
40879      * @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)
40880      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40881      */
40882     setUrl : function(url, params, loadOnce){
40883         if (this.iframe) {
40884             this.iframeEl.dom.src = url;
40885             return false;
40886         }
40887         
40888         if(this.refreshDelegate){
40889             this.removeListener("activate", this.refreshDelegate);
40890         }
40891         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40892         this.on("activate", this.refreshDelegate);
40893         return this.el.getUpdateManager();
40894     },
40895     
40896     _handleRefresh : function(url, params, loadOnce){
40897         if(!loadOnce || !this.loaded){
40898             var updater = this.el.getUpdateManager();
40899             updater.update(url, params, this._setLoaded.createDelegate(this));
40900         }
40901     },
40902     
40903     _setLoaded : function(){
40904         this.loaded = true;
40905     }, 
40906     
40907     /**
40908      * Returns this panel's id
40909      * @return {String} 
40910      */
40911     getId : function(){
40912         return this.el.id;
40913     },
40914     
40915     /** 
40916      * Returns this panel's element - used by regiosn to add.
40917      * @return {Roo.Element} 
40918      */
40919     getEl : function(){
40920         return this.wrapEl || this.el;
40921     },
40922     
40923    
40924     
40925     adjustForComponents : function(width, height)
40926     {
40927         //Roo.log('adjustForComponents ');
40928         if(this.resizeEl != this.el){
40929             width -= this.el.getFrameWidth('lr');
40930             height -= this.el.getFrameWidth('tb');
40931         }
40932         if(this.toolbar){
40933             var te = this.toolbar.getEl();
40934             te.setWidth(width);
40935             height -= te.getHeight();
40936         }
40937         if(this.footer){
40938             var te = this.footer.getEl();
40939             te.setWidth(width);
40940             height -= te.getHeight();
40941         }
40942         
40943         
40944         if(this.adjustments){
40945             width += this.adjustments[0];
40946             height += this.adjustments[1];
40947         }
40948         return {"width": width, "height": height};
40949     },
40950     
40951     setSize : function(width, height){
40952         if(this.fitToFrame && !this.ignoreResize(width, height)){
40953             if(this.fitContainer && this.resizeEl != this.el){
40954                 this.el.setSize(width, height);
40955             }
40956             var size = this.adjustForComponents(width, height);
40957             if (this.iframe) {
40958                 this.iframeEl.setSize(width,height);
40959             }
40960             
40961             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40962             this.fireEvent('resize', this, size.width, size.height);
40963             
40964             
40965         }
40966     },
40967     
40968     /**
40969      * Returns this panel's title
40970      * @return {String} 
40971      */
40972     getTitle : function(){
40973         
40974         if (typeof(this.title) != 'object') {
40975             return this.title;
40976         }
40977         
40978         var t = '';
40979         for (var k in this.title) {
40980             if (!this.title.hasOwnProperty(k)) {
40981                 continue;
40982             }
40983             
40984             if (k.indexOf('-') >= 0) {
40985                 var s = k.split('-');
40986                 for (var i = 0; i<s.length; i++) {
40987                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40988                 }
40989             } else {
40990                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40991             }
40992         }
40993         return t;
40994     },
40995     
40996     /**
40997      * Set this panel's title
40998      * @param {String} title
40999      */
41000     setTitle : function(title){
41001         this.title = title;
41002         if(this.region){
41003             this.region.updatePanelTitle(this, title);
41004         }
41005     },
41006     
41007     /**
41008      * Returns true is this panel was configured to be closable
41009      * @return {Boolean} 
41010      */
41011     isClosable : function(){
41012         return this.closable;
41013     },
41014     
41015     beforeSlide : function(){
41016         this.el.clip();
41017         this.resizeEl.clip();
41018     },
41019     
41020     afterSlide : function(){
41021         this.el.unclip();
41022         this.resizeEl.unclip();
41023     },
41024     
41025     /**
41026      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
41027      *   Will fail silently if the {@link #setUrl} method has not been called.
41028      *   This does not activate the panel, just updates its content.
41029      */
41030     refresh : function(){
41031         if(this.refreshDelegate){
41032            this.loaded = false;
41033            this.refreshDelegate();
41034         }
41035     },
41036     
41037     /**
41038      * Destroys this panel
41039      */
41040     destroy : function(){
41041         this.el.removeAllListeners();
41042         var tempEl = document.createElement("span");
41043         tempEl.appendChild(this.el.dom);
41044         tempEl.innerHTML = "";
41045         this.el.remove();
41046         this.el = null;
41047     },
41048     
41049     /**
41050      * form - if the content panel contains a form - this is a reference to it.
41051      * @type {Roo.form.Form}
41052      */
41053     form : false,
41054     /**
41055      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
41056      *    This contains a reference to it.
41057      * @type {Roo.View}
41058      */
41059     view : false,
41060     
41061       /**
41062      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
41063      * <pre><code>
41064
41065 layout.addxtype({
41066        xtype : 'Form',
41067        items: [ .... ]
41068    }
41069 );
41070
41071 </code></pre>
41072      * @param {Object} cfg Xtype definition of item to add.
41073      */
41074     
41075     
41076     getChildContainer: function () {
41077         return this.getEl();
41078     },
41079     
41080     
41081     onScroll : function(e)
41082     {
41083         this.fireEvent('scroll', this, e);
41084     }
41085     
41086     
41087     /*
41088         var  ret = new Roo.factory(cfg);
41089         return ret;
41090         
41091         
41092         // add form..
41093         if (cfg.xtype.match(/^Form$/)) {
41094             
41095             var el;
41096             //if (this.footer) {
41097             //    el = this.footer.container.insertSibling(false, 'before');
41098             //} else {
41099                 el = this.el.createChild();
41100             //}
41101
41102             this.form = new  Roo.form.Form(cfg);
41103             
41104             
41105             if ( this.form.allItems.length) {
41106                 this.form.render(el.dom);
41107             }
41108             return this.form;
41109         }
41110         // should only have one of theses..
41111         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
41112             // views.. should not be just added - used named prop 'view''
41113             
41114             cfg.el = this.el.appendChild(document.createElement("div"));
41115             // factory?
41116             
41117             var ret = new Roo.factory(cfg);
41118              
41119              ret.render && ret.render(false, ''); // render blank..
41120             this.view = ret;
41121             return ret;
41122         }
41123         return false;
41124     }
41125     \*/
41126 });
41127  
41128 /**
41129  * @class Roo.bootstrap.panel.Grid
41130  * @extends Roo.bootstrap.panel.Content
41131  * @constructor
41132  * Create a new GridPanel.
41133  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
41134  * @param {Object} config A the config object
41135   
41136  */
41137
41138
41139
41140 Roo.bootstrap.panel.Grid = function(config)
41141 {
41142     
41143       
41144     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
41145         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
41146
41147     config.el = this.wrapper;
41148     //this.el = this.wrapper;
41149     
41150       if (config.container) {
41151         // ctor'ed from a Border/panel.grid
41152         
41153         
41154         this.wrapper.setStyle("overflow", "hidden");
41155         this.wrapper.addClass('roo-grid-container');
41156
41157     }
41158     
41159     
41160     if(config.toolbar){
41161         var tool_el = this.wrapper.createChild();    
41162         this.toolbar = Roo.factory(config.toolbar);
41163         var ti = [];
41164         if (config.toolbar.items) {
41165             ti = config.toolbar.items ;
41166             delete config.toolbar.items ;
41167         }
41168         
41169         var nitems = [];
41170         this.toolbar.render(tool_el);
41171         for(var i =0;i < ti.length;i++) {
41172           //  Roo.log(['add child', items[i]]);
41173             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
41174         }
41175         this.toolbar.items = nitems;
41176         
41177         delete config.toolbar;
41178     }
41179     
41180     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
41181     config.grid.scrollBody = true;;
41182     config.grid.monitorWindowResize = false; // turn off autosizing
41183     config.grid.autoHeight = false;
41184     config.grid.autoWidth = false;
41185     
41186     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
41187     
41188     if (config.background) {
41189         // render grid on panel activation (if panel background)
41190         this.on('activate', function(gp) {
41191             if (!gp.grid.rendered) {
41192                 gp.grid.render(this.wrapper);
41193                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
41194             }
41195         });
41196             
41197     } else {
41198         this.grid.render(this.wrapper);
41199         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
41200
41201     }
41202     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
41203     // ??? needed ??? config.el = this.wrapper;
41204     
41205     
41206     
41207   
41208     // xtype created footer. - not sure if will work as we normally have to render first..
41209     if (this.footer && !this.footer.el && this.footer.xtype) {
41210         
41211         var ctr = this.grid.getView().getFooterPanel(true);
41212         this.footer.dataSource = this.grid.dataSource;
41213         this.footer = Roo.factory(this.footer, Roo);
41214         this.footer.render(ctr);
41215         
41216     }
41217     
41218     
41219     
41220     
41221      
41222 };
41223
41224 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
41225     getId : function(){
41226         return this.grid.id;
41227     },
41228     
41229     /**
41230      * Returns the grid for this panel
41231      * @return {Roo.bootstrap.Table} 
41232      */
41233     getGrid : function(){
41234         return this.grid;    
41235     },
41236     
41237     setSize : function(width, height){
41238         if(!this.ignoreResize(width, height)){
41239             var grid = this.grid;
41240             var size = this.adjustForComponents(width, height);
41241             // tfoot is not a footer?
41242           
41243             
41244             var gridel = grid.getGridEl();
41245             gridel.setSize(size.width, size.height);
41246             
41247             var tbd = grid.getGridEl().select('tbody', true).first();
41248             var thd = grid.getGridEl().select('thead',true).first();
41249             var tbf= grid.getGridEl().select('tfoot', true).first();
41250
41251             if (tbf) {
41252                 size.height -= tbf.getHeight();
41253             }
41254             if (thd) {
41255                 size.height -= thd.getHeight();
41256             }
41257             
41258             tbd.setSize(size.width, size.height );
41259             // this is for the account management tab -seems to work there.
41260             var thd = grid.getGridEl().select('thead',true).first();
41261             //if (tbd) {
41262             //    tbd.setSize(size.width, size.height - thd.getHeight());
41263             //}
41264              
41265             grid.autoSize();
41266         }
41267     },
41268      
41269     
41270     
41271     beforeSlide : function(){
41272         this.grid.getView().scroller.clip();
41273     },
41274     
41275     afterSlide : function(){
41276         this.grid.getView().scroller.unclip();
41277     },
41278     
41279     destroy : function(){
41280         this.grid.destroy();
41281         delete this.grid;
41282         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
41283     }
41284 });
41285
41286 /**
41287  * @class Roo.bootstrap.panel.Nest
41288  * @extends Roo.bootstrap.panel.Content
41289  * @constructor
41290  * Create a new Panel, that can contain a layout.Border.
41291  * 
41292  * 
41293  * @param {Roo.BorderLayout} layout The layout for this panel
41294  * @param {String/Object} config A string to set only the title or a config object
41295  */
41296 Roo.bootstrap.panel.Nest = function(config)
41297 {
41298     // construct with only one argument..
41299     /* FIXME - implement nicer consturctors
41300     if (layout.layout) {
41301         config = layout;
41302         layout = config.layout;
41303         delete config.layout;
41304     }
41305     if (layout.xtype && !layout.getEl) {
41306         // then layout needs constructing..
41307         layout = Roo.factory(layout, Roo);
41308     }
41309     */
41310     
41311     config.el =  config.layout.getEl();
41312     
41313     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
41314     
41315     config.layout.monitorWindowResize = false; // turn off autosizing
41316     this.layout = config.layout;
41317     this.layout.getEl().addClass("roo-layout-nested-layout");
41318     this.layout.parent = this;
41319     
41320     
41321     
41322     
41323 };
41324
41325 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
41326
41327     setSize : function(width, height){
41328         if(!this.ignoreResize(width, height)){
41329             var size = this.adjustForComponents(width, height);
41330             var el = this.layout.getEl();
41331             if (size.height < 1) {
41332                 el.setWidth(size.width);   
41333             } else {
41334                 el.setSize(size.width, size.height);
41335             }
41336             var touch = el.dom.offsetWidth;
41337             this.layout.layout();
41338             // ie requires a double layout on the first pass
41339             if(Roo.isIE && !this.initialized){
41340                 this.initialized = true;
41341                 this.layout.layout();
41342             }
41343         }
41344     },
41345     
41346     // activate all subpanels if not currently active..
41347     
41348     setActiveState : function(active){
41349         this.active = active;
41350         this.setActiveClass(active);
41351         
41352         if(!active){
41353             this.fireEvent("deactivate", this);
41354             return;
41355         }
41356         
41357         this.fireEvent("activate", this);
41358         // not sure if this should happen before or after..
41359         if (!this.layout) {
41360             return; // should not happen..
41361         }
41362         var reg = false;
41363         for (var r in this.layout.regions) {
41364             reg = this.layout.getRegion(r);
41365             if (reg.getActivePanel()) {
41366                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
41367                 reg.setActivePanel(reg.getActivePanel());
41368                 continue;
41369             }
41370             if (!reg.panels.length) {
41371                 continue;
41372             }
41373             reg.showPanel(reg.getPanel(0));
41374         }
41375         
41376         
41377         
41378         
41379     },
41380     
41381     /**
41382      * Returns the nested BorderLayout for this panel
41383      * @return {Roo.BorderLayout} 
41384      */
41385     getLayout : function(){
41386         return this.layout;
41387     },
41388     
41389      /**
41390      * Adds a xtype elements to the layout of the nested panel
41391      * <pre><code>
41392
41393 panel.addxtype({
41394        xtype : 'ContentPanel',
41395        region: 'west',
41396        items: [ .... ]
41397    }
41398 );
41399
41400 panel.addxtype({
41401         xtype : 'NestedLayoutPanel',
41402         region: 'west',
41403         layout: {
41404            center: { },
41405            west: { }   
41406         },
41407         items : [ ... list of content panels or nested layout panels.. ]
41408    }
41409 );
41410 </code></pre>
41411      * @param {Object} cfg Xtype definition of item to add.
41412      */
41413     addxtype : function(cfg) {
41414         return this.layout.addxtype(cfg);
41415     
41416     }
41417 });/*
41418  * Based on:
41419  * Ext JS Library 1.1.1
41420  * Copyright(c) 2006-2007, Ext JS, LLC.
41421  *
41422  * Originally Released Under LGPL - original licence link has changed is not relivant.
41423  *
41424  * Fork - LGPL
41425  * <script type="text/javascript">
41426  */
41427 /**
41428  * @class Roo.TabPanel
41429  * @extends Roo.util.Observable
41430  * A lightweight tab container.
41431  * <br><br>
41432  * Usage:
41433  * <pre><code>
41434 // basic tabs 1, built from existing content
41435 var tabs = new Roo.TabPanel("tabs1");
41436 tabs.addTab("script", "View Script");
41437 tabs.addTab("markup", "View Markup");
41438 tabs.activate("script");
41439
41440 // more advanced tabs, built from javascript
41441 var jtabs = new Roo.TabPanel("jtabs");
41442 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
41443
41444 // set up the UpdateManager
41445 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
41446 var updater = tab2.getUpdateManager();
41447 updater.setDefaultUrl("ajax1.htm");
41448 tab2.on('activate', updater.refresh, updater, true);
41449
41450 // Use setUrl for Ajax loading
41451 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
41452 tab3.setUrl("ajax2.htm", null, true);
41453
41454 // Disabled tab
41455 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
41456 tab4.disable();
41457
41458 jtabs.activate("jtabs-1");
41459  * </code></pre>
41460  * @constructor
41461  * Create a new TabPanel.
41462  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
41463  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
41464  */
41465 Roo.bootstrap.panel.Tabs = function(config){
41466     /**
41467     * The container element for this TabPanel.
41468     * @type Roo.Element
41469     */
41470     this.el = Roo.get(config.el);
41471     delete config.el;
41472     if(config){
41473         if(typeof config == "boolean"){
41474             this.tabPosition = config ? "bottom" : "top";
41475         }else{
41476             Roo.apply(this, config);
41477         }
41478     }
41479     
41480     if(this.tabPosition == "bottom"){
41481         // if tabs are at the bottom = create the body first.
41482         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41483         this.el.addClass("roo-tabs-bottom");
41484     }
41485     // next create the tabs holders
41486     
41487     if (this.tabPosition == "west"){
41488         
41489         var reg = this.region; // fake it..
41490         while (reg) {
41491             if (!reg.mgr.parent) {
41492                 break;
41493             }
41494             reg = reg.mgr.parent.region;
41495         }
41496         Roo.log("got nest?");
41497         Roo.log(reg);
41498         if (reg.mgr.getRegion('west')) {
41499             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41500             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41501             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41502             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41503             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41504         
41505             
41506         }
41507         
41508         
41509     } else {
41510      
41511         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41512         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41513         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41514         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41515     }
41516     
41517     
41518     if(Roo.isIE){
41519         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41520     }
41521     
41522     // finally - if tabs are at the top, then create the body last..
41523     if(this.tabPosition != "bottom"){
41524         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41525          * @type Roo.Element
41526          */
41527         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41528         this.el.addClass("roo-tabs-top");
41529     }
41530     this.items = [];
41531
41532     this.bodyEl.setStyle("position", "relative");
41533
41534     this.active = null;
41535     this.activateDelegate = this.activate.createDelegate(this);
41536
41537     this.addEvents({
41538         /**
41539          * @event tabchange
41540          * Fires when the active tab changes
41541          * @param {Roo.TabPanel} this
41542          * @param {Roo.TabPanelItem} activePanel The new active tab
41543          */
41544         "tabchange": true,
41545         /**
41546          * @event beforetabchange
41547          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41548          * @param {Roo.TabPanel} this
41549          * @param {Object} e Set cancel to true on this object to cancel the tab change
41550          * @param {Roo.TabPanelItem} tab The tab being changed to
41551          */
41552         "beforetabchange" : true
41553     });
41554
41555     Roo.EventManager.onWindowResize(this.onResize, this);
41556     this.cpad = this.el.getPadding("lr");
41557     this.hiddenCount = 0;
41558
41559
41560     // toolbar on the tabbar support...
41561     if (this.toolbar) {
41562         alert("no toolbar support yet");
41563         this.toolbar  = false;
41564         /*
41565         var tcfg = this.toolbar;
41566         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41567         this.toolbar = new Roo.Toolbar(tcfg);
41568         if (Roo.isSafari) {
41569             var tbl = tcfg.container.child('table', true);
41570             tbl.setAttribute('width', '100%');
41571         }
41572         */
41573         
41574     }
41575    
41576
41577
41578     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41579 };
41580
41581 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41582     /*
41583      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41584      */
41585     tabPosition : "top",
41586     /*
41587      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41588      */
41589     currentTabWidth : 0,
41590     /*
41591      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41592      */
41593     minTabWidth : 40,
41594     /*
41595      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41596      */
41597     maxTabWidth : 250,
41598     /*
41599      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41600      */
41601     preferredTabWidth : 175,
41602     /*
41603      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41604      */
41605     resizeTabs : false,
41606     /*
41607      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41608      */
41609     monitorResize : true,
41610     /*
41611      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41612      */
41613     toolbar : false,  // set by caller..
41614     
41615     region : false, /// set by caller
41616     
41617     disableTooltips : true, // not used yet...
41618
41619     /**
41620      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41621      * @param {String} id The id of the div to use <b>or create</b>
41622      * @param {String} text The text for the tab
41623      * @param {String} content (optional) Content to put in the TabPanelItem body
41624      * @param {Boolean} closable (optional) True to create a close icon on the tab
41625      * @return {Roo.TabPanelItem} The created TabPanelItem
41626      */
41627     addTab : function(id, text, content, closable, tpl)
41628     {
41629         var item = new Roo.bootstrap.panel.TabItem({
41630             panel: this,
41631             id : id,
41632             text : text,
41633             closable : closable,
41634             tpl : tpl
41635         });
41636         this.addTabItem(item);
41637         if(content){
41638             item.setContent(content);
41639         }
41640         return item;
41641     },
41642
41643     /**
41644      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41645      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41646      * @return {Roo.TabPanelItem}
41647      */
41648     getTab : function(id){
41649         return this.items[id];
41650     },
41651
41652     /**
41653      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41654      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41655      */
41656     hideTab : function(id){
41657         var t = this.items[id];
41658         if(!t.isHidden()){
41659            t.setHidden(true);
41660            this.hiddenCount++;
41661            this.autoSizeTabs();
41662         }
41663     },
41664
41665     /**
41666      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41667      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41668      */
41669     unhideTab : function(id){
41670         var t = this.items[id];
41671         if(t.isHidden()){
41672            t.setHidden(false);
41673            this.hiddenCount--;
41674            this.autoSizeTabs();
41675         }
41676     },
41677
41678     /**
41679      * Adds an existing {@link Roo.TabPanelItem}.
41680      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41681      */
41682     addTabItem : function(item)
41683     {
41684         this.items[item.id] = item;
41685         this.items.push(item);
41686         this.autoSizeTabs();
41687       //  if(this.resizeTabs){
41688     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41689   //         this.autoSizeTabs();
41690 //        }else{
41691 //            item.autoSize();
41692        // }
41693     },
41694
41695     /**
41696      * Removes a {@link Roo.TabPanelItem}.
41697      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41698      */
41699     removeTab : function(id){
41700         var items = this.items;
41701         var tab = items[id];
41702         if(!tab) { return; }
41703         var index = items.indexOf(tab);
41704         if(this.active == tab && items.length > 1){
41705             var newTab = this.getNextAvailable(index);
41706             if(newTab) {
41707                 newTab.activate();
41708             }
41709         }
41710         this.stripEl.dom.removeChild(tab.pnode.dom);
41711         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41712             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41713         }
41714         items.splice(index, 1);
41715         delete this.items[tab.id];
41716         tab.fireEvent("close", tab);
41717         tab.purgeListeners();
41718         this.autoSizeTabs();
41719     },
41720
41721     getNextAvailable : function(start){
41722         var items = this.items;
41723         var index = start;
41724         // look for a next tab that will slide over to
41725         // replace the one being removed
41726         while(index < items.length){
41727             var item = items[++index];
41728             if(item && !item.isHidden()){
41729                 return item;
41730             }
41731         }
41732         // if one isn't found select the previous tab (on the left)
41733         index = start;
41734         while(index >= 0){
41735             var item = items[--index];
41736             if(item && !item.isHidden()){
41737                 return item;
41738             }
41739         }
41740         return null;
41741     },
41742
41743     /**
41744      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41745      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41746      */
41747     disableTab : function(id){
41748         var tab = this.items[id];
41749         if(tab && this.active != tab){
41750             tab.disable();
41751         }
41752     },
41753
41754     /**
41755      * Enables a {@link Roo.TabPanelItem} that is disabled.
41756      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41757      */
41758     enableTab : function(id){
41759         var tab = this.items[id];
41760         tab.enable();
41761     },
41762
41763     /**
41764      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41765      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41766      * @return {Roo.TabPanelItem} The TabPanelItem.
41767      */
41768     activate : function(id)
41769     {
41770         //Roo.log('activite:'  + id);
41771         
41772         var tab = this.items[id];
41773         if(!tab){
41774             return null;
41775         }
41776         if(tab == this.active || tab.disabled){
41777             return tab;
41778         }
41779         var e = {};
41780         this.fireEvent("beforetabchange", this, e, tab);
41781         if(e.cancel !== true && !tab.disabled){
41782             if(this.active){
41783                 this.active.hide();
41784             }
41785             this.active = this.items[id];
41786             this.active.show();
41787             this.fireEvent("tabchange", this, this.active);
41788         }
41789         return tab;
41790     },
41791
41792     /**
41793      * Gets the active {@link Roo.TabPanelItem}.
41794      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41795      */
41796     getActiveTab : function(){
41797         return this.active;
41798     },
41799
41800     /**
41801      * Updates the tab body element to fit the height of the container element
41802      * for overflow scrolling
41803      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41804      */
41805     syncHeight : function(targetHeight){
41806         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41807         var bm = this.bodyEl.getMargins();
41808         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41809         this.bodyEl.setHeight(newHeight);
41810         return newHeight;
41811     },
41812
41813     onResize : function(){
41814         if(this.monitorResize){
41815             this.autoSizeTabs();
41816         }
41817     },
41818
41819     /**
41820      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41821      */
41822     beginUpdate : function(){
41823         this.updating = true;
41824     },
41825
41826     /**
41827      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41828      */
41829     endUpdate : function(){
41830         this.updating = false;
41831         this.autoSizeTabs();
41832     },
41833
41834     /**
41835      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41836      */
41837     autoSizeTabs : function()
41838     {
41839         var count = this.items.length;
41840         var vcount = count - this.hiddenCount;
41841         
41842         if (vcount < 2) {
41843             this.stripEl.hide();
41844         } else {
41845             this.stripEl.show();
41846         }
41847         
41848         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41849             return;
41850         }
41851         
41852         
41853         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41854         var availWidth = Math.floor(w / vcount);
41855         var b = this.stripBody;
41856         if(b.getWidth() > w){
41857             var tabs = this.items;
41858             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41859             if(availWidth < this.minTabWidth){
41860                 /*if(!this.sleft){    // incomplete scrolling code
41861                     this.createScrollButtons();
41862                 }
41863                 this.showScroll();
41864                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41865             }
41866         }else{
41867             if(this.currentTabWidth < this.preferredTabWidth){
41868                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41869             }
41870         }
41871     },
41872
41873     /**
41874      * Returns the number of tabs in this TabPanel.
41875      * @return {Number}
41876      */
41877      getCount : function(){
41878          return this.items.length;
41879      },
41880
41881     /**
41882      * Resizes all the tabs to the passed width
41883      * @param {Number} The new width
41884      */
41885     setTabWidth : function(width){
41886         this.currentTabWidth = width;
41887         for(var i = 0, len = this.items.length; i < len; i++) {
41888                 if(!this.items[i].isHidden()) {
41889                 this.items[i].setWidth(width);
41890             }
41891         }
41892     },
41893
41894     /**
41895      * Destroys this TabPanel
41896      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41897      */
41898     destroy : function(removeEl){
41899         Roo.EventManager.removeResizeListener(this.onResize, this);
41900         for(var i = 0, len = this.items.length; i < len; i++){
41901             this.items[i].purgeListeners();
41902         }
41903         if(removeEl === true){
41904             this.el.update("");
41905             this.el.remove();
41906         }
41907     },
41908     
41909     createStrip : function(container)
41910     {
41911         var strip = document.createElement("nav");
41912         strip.className = Roo.bootstrap.version == 4 ?
41913             "navbar-light bg-light" : 
41914             "navbar navbar-default"; //"x-tabs-wrap";
41915         container.appendChild(strip);
41916         return strip;
41917     },
41918     
41919     createStripList : function(strip)
41920     {
41921         // div wrapper for retard IE
41922         // returns the "tr" element.
41923         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41924         //'<div class="x-tabs-strip-wrap">'+
41925           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41926           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41927         return strip.firstChild; //.firstChild.firstChild.firstChild;
41928     },
41929     createBody : function(container)
41930     {
41931         var body = document.createElement("div");
41932         Roo.id(body, "tab-body");
41933         //Roo.fly(body).addClass("x-tabs-body");
41934         Roo.fly(body).addClass("tab-content");
41935         container.appendChild(body);
41936         return body;
41937     },
41938     createItemBody :function(bodyEl, id){
41939         var body = Roo.getDom(id);
41940         if(!body){
41941             body = document.createElement("div");
41942             body.id = id;
41943         }
41944         //Roo.fly(body).addClass("x-tabs-item-body");
41945         Roo.fly(body).addClass("tab-pane");
41946          bodyEl.insertBefore(body, bodyEl.firstChild);
41947         return body;
41948     },
41949     /** @private */
41950     createStripElements :  function(stripEl, text, closable, tpl)
41951     {
41952         var td = document.createElement("li"); // was td..
41953         td.className = 'nav-item';
41954         
41955         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41956         
41957         
41958         stripEl.appendChild(td);
41959         /*if(closable){
41960             td.className = "x-tabs-closable";
41961             if(!this.closeTpl){
41962                 this.closeTpl = new Roo.Template(
41963                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41964                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41965                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41966                 );
41967             }
41968             var el = this.closeTpl.overwrite(td, {"text": text});
41969             var close = el.getElementsByTagName("div")[0];
41970             var inner = el.getElementsByTagName("em")[0];
41971             return {"el": el, "close": close, "inner": inner};
41972         } else {
41973         */
41974         // not sure what this is..
41975 //            if(!this.tabTpl){
41976                 //this.tabTpl = new Roo.Template(
41977                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41978                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41979                 //);
41980 //                this.tabTpl = new Roo.Template(
41981 //                   '<a href="#">' +
41982 //                   '<span unselectable="on"' +
41983 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41984 //                            ' >{text}</span></a>'
41985 //                );
41986 //                
41987 //            }
41988
41989
41990             var template = tpl || this.tabTpl || false;
41991             
41992             if(!template){
41993                 template =  new Roo.Template(
41994                         Roo.bootstrap.version == 4 ? 
41995                             (
41996                                 '<a class="nav-link" href="#" unselectable="on"' +
41997                                      (this.disableTooltips ? '' : ' title="{text}"') +
41998                                      ' >{text}</a>'
41999                             ) : (
42000                                 '<a class="nav-link" href="#">' +
42001                                 '<span unselectable="on"' +
42002                                          (this.disableTooltips ? '' : ' title="{text}"') +
42003                                     ' >{text}</span></a>'
42004                             )
42005                 );
42006             }
42007             
42008             switch (typeof(template)) {
42009                 case 'object' :
42010                     break;
42011                 case 'string' :
42012                     template = new Roo.Template(template);
42013                     break;
42014                 default :
42015                     break;
42016             }
42017             
42018             var el = template.overwrite(td, {"text": text});
42019             
42020             var inner = el.getElementsByTagName("span")[0];
42021             
42022             return {"el": el, "inner": inner};
42023             
42024     }
42025         
42026     
42027 });
42028
42029 /**
42030  * @class Roo.TabPanelItem
42031  * @extends Roo.util.Observable
42032  * Represents an individual item (tab plus body) in a TabPanel.
42033  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
42034  * @param {String} id The id of this TabPanelItem
42035  * @param {String} text The text for the tab of this TabPanelItem
42036  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
42037  */
42038 Roo.bootstrap.panel.TabItem = function(config){
42039     /**
42040      * The {@link Roo.TabPanel} this TabPanelItem belongs to
42041      * @type Roo.TabPanel
42042      */
42043     this.tabPanel = config.panel;
42044     /**
42045      * The id for this TabPanelItem
42046      * @type String
42047      */
42048     this.id = config.id;
42049     /** @private */
42050     this.disabled = false;
42051     /** @private */
42052     this.text = config.text;
42053     /** @private */
42054     this.loaded = false;
42055     this.closable = config.closable;
42056
42057     /**
42058      * The body element for this TabPanelItem.
42059      * @type Roo.Element
42060      */
42061     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
42062     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
42063     this.bodyEl.setStyle("display", "block");
42064     this.bodyEl.setStyle("zoom", "1");
42065     //this.hideAction();
42066
42067     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
42068     /** @private */
42069     this.el = Roo.get(els.el);
42070     this.inner = Roo.get(els.inner, true);
42071      this.textEl = Roo.bootstrap.version == 4 ?
42072         this.el : Roo.get(this.el.dom.firstChild, true);
42073
42074     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
42075     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
42076
42077     
42078 //    this.el.on("mousedown", this.onTabMouseDown, this);
42079     this.el.on("click", this.onTabClick, this);
42080     /** @private */
42081     if(config.closable){
42082         var c = Roo.get(els.close, true);
42083         c.dom.title = this.closeText;
42084         c.addClassOnOver("close-over");
42085         c.on("click", this.closeClick, this);
42086      }
42087
42088     this.addEvents({
42089          /**
42090          * @event activate
42091          * Fires when this tab becomes the active tab.
42092          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42093          * @param {Roo.TabPanelItem} this
42094          */
42095         "activate": true,
42096         /**
42097          * @event beforeclose
42098          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
42099          * @param {Roo.TabPanelItem} this
42100          * @param {Object} e Set cancel to true on this object to cancel the close.
42101          */
42102         "beforeclose": true,
42103         /**
42104          * @event close
42105          * Fires when this tab is closed.
42106          * @param {Roo.TabPanelItem} this
42107          */
42108          "close": true,
42109         /**
42110          * @event deactivate
42111          * Fires when this tab is no longer the active tab.
42112          * @param {Roo.TabPanel} tabPanel The parent TabPanel
42113          * @param {Roo.TabPanelItem} this
42114          */
42115          "deactivate" : true
42116     });
42117     this.hidden = false;
42118
42119     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
42120 };
42121
42122 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
42123            {
42124     purgeListeners : function(){
42125        Roo.util.Observable.prototype.purgeListeners.call(this);
42126        this.el.removeAllListeners();
42127     },
42128     /**
42129      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
42130      */
42131     show : function(){
42132         this.status_node.addClass("active");
42133         this.showAction();
42134         if(Roo.isOpera){
42135             this.tabPanel.stripWrap.repaint();
42136         }
42137         this.fireEvent("activate", this.tabPanel, this);
42138     },
42139
42140     /**
42141      * Returns true if this tab is the active tab.
42142      * @return {Boolean}
42143      */
42144     isActive : function(){
42145         return this.tabPanel.getActiveTab() == this;
42146     },
42147
42148     /**
42149      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
42150      */
42151     hide : function(){
42152         this.status_node.removeClass("active");
42153         this.hideAction();
42154         this.fireEvent("deactivate", this.tabPanel, this);
42155     },
42156
42157     hideAction : function(){
42158         this.bodyEl.hide();
42159         this.bodyEl.setStyle("position", "absolute");
42160         this.bodyEl.setLeft("-20000px");
42161         this.bodyEl.setTop("-20000px");
42162     },
42163
42164     showAction : function(){
42165         this.bodyEl.setStyle("position", "relative");
42166         this.bodyEl.setTop("");
42167         this.bodyEl.setLeft("");
42168         this.bodyEl.show();
42169     },
42170
42171     /**
42172      * Set the tooltip for the tab.
42173      * @param {String} tooltip The tab's tooltip
42174      */
42175     setTooltip : function(text){
42176         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
42177             this.textEl.dom.qtip = text;
42178             this.textEl.dom.removeAttribute('title');
42179         }else{
42180             this.textEl.dom.title = text;
42181         }
42182     },
42183
42184     onTabClick : function(e){
42185         e.preventDefault();
42186         this.tabPanel.activate(this.id);
42187     },
42188
42189     onTabMouseDown : function(e){
42190         e.preventDefault();
42191         this.tabPanel.activate(this.id);
42192     },
42193 /*
42194     getWidth : function(){
42195         return this.inner.getWidth();
42196     },
42197
42198     setWidth : function(width){
42199         var iwidth = width - this.linode.getPadding("lr");
42200         this.inner.setWidth(iwidth);
42201         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
42202         this.linode.setWidth(width);
42203     },
42204 */
42205     /**
42206      * Show or hide the tab
42207      * @param {Boolean} hidden True to hide or false to show.
42208      */
42209     setHidden : function(hidden){
42210         this.hidden = hidden;
42211         this.linode.setStyle("display", hidden ? "none" : "");
42212     },
42213
42214     /**
42215      * Returns true if this tab is "hidden"
42216      * @return {Boolean}
42217      */
42218     isHidden : function(){
42219         return this.hidden;
42220     },
42221
42222     /**
42223      * Returns the text for this tab
42224      * @return {String}
42225      */
42226     getText : function(){
42227         return this.text;
42228     },
42229     /*
42230     autoSize : function(){
42231         //this.el.beginMeasure();
42232         this.textEl.setWidth(1);
42233         /*
42234          *  #2804 [new] Tabs in Roojs
42235          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
42236          */
42237         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
42238         //this.el.endMeasure();
42239     //},
42240
42241     /**
42242      * Sets the text for the tab (Note: this also sets the tooltip text)
42243      * @param {String} text The tab's text and tooltip
42244      */
42245     setText : function(text){
42246         this.text = text;
42247         this.textEl.update(text);
42248         this.setTooltip(text);
42249         //if(!this.tabPanel.resizeTabs){
42250         //    this.autoSize();
42251         //}
42252     },
42253     /**
42254      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
42255      */
42256     activate : function(){
42257         this.tabPanel.activate(this.id);
42258     },
42259
42260     /**
42261      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
42262      */
42263     disable : function(){
42264         if(this.tabPanel.active != this){
42265             this.disabled = true;
42266             this.status_node.addClass("disabled");
42267         }
42268     },
42269
42270     /**
42271      * Enables this TabPanelItem if it was previously disabled.
42272      */
42273     enable : function(){
42274         this.disabled = false;
42275         this.status_node.removeClass("disabled");
42276     },
42277
42278     /**
42279      * Sets the content for this TabPanelItem.
42280      * @param {String} content The content
42281      * @param {Boolean} loadScripts true to look for and load scripts
42282      */
42283     setContent : function(content, loadScripts){
42284         this.bodyEl.update(content, loadScripts);
42285     },
42286
42287     /**
42288      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
42289      * @return {Roo.UpdateManager} The UpdateManager
42290      */
42291     getUpdateManager : function(){
42292         return this.bodyEl.getUpdateManager();
42293     },
42294
42295     /**
42296      * Set a URL to be used to load the content for this TabPanelItem.
42297      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
42298      * @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)
42299      * @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)
42300      * @return {Roo.UpdateManager} The UpdateManager
42301      */
42302     setUrl : function(url, params, loadOnce){
42303         if(this.refreshDelegate){
42304             this.un('activate', this.refreshDelegate);
42305         }
42306         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
42307         this.on("activate", this.refreshDelegate);
42308         return this.bodyEl.getUpdateManager();
42309     },
42310
42311     /** @private */
42312     _handleRefresh : function(url, params, loadOnce){
42313         if(!loadOnce || !this.loaded){
42314             var updater = this.bodyEl.getUpdateManager();
42315             updater.update(url, params, this._setLoaded.createDelegate(this));
42316         }
42317     },
42318
42319     /**
42320      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
42321      *   Will fail silently if the setUrl method has not been called.
42322      *   This does not activate the panel, just updates its content.
42323      */
42324     refresh : function(){
42325         if(this.refreshDelegate){
42326            this.loaded = false;
42327            this.refreshDelegate();
42328         }
42329     },
42330
42331     /** @private */
42332     _setLoaded : function(){
42333         this.loaded = true;
42334     },
42335
42336     /** @private */
42337     closeClick : function(e){
42338         var o = {};
42339         e.stopEvent();
42340         this.fireEvent("beforeclose", this, o);
42341         if(o.cancel !== true){
42342             this.tabPanel.removeTab(this.id);
42343         }
42344     },
42345     /**
42346      * The text displayed in the tooltip for the close icon.
42347      * @type String
42348      */
42349     closeText : "Close this tab"
42350 });
42351 /**
42352 *    This script refer to:
42353 *    Title: International Telephone Input
42354 *    Author: Jack O'Connor
42355 *    Code version:  v12.1.12
42356 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42357 **/
42358
42359 Roo.bootstrap.PhoneInputData = function() {
42360     var d = [
42361       [
42362         "Afghanistan (‫افغانستان‬‎)",
42363         "af",
42364         "93"
42365       ],
42366       [
42367         "Albania (Shqipëri)",
42368         "al",
42369         "355"
42370       ],
42371       [
42372         "Algeria (‫الجزائر‬‎)",
42373         "dz",
42374         "213"
42375       ],
42376       [
42377         "American Samoa",
42378         "as",
42379         "1684"
42380       ],
42381       [
42382         "Andorra",
42383         "ad",
42384         "376"
42385       ],
42386       [
42387         "Angola",
42388         "ao",
42389         "244"
42390       ],
42391       [
42392         "Anguilla",
42393         "ai",
42394         "1264"
42395       ],
42396       [
42397         "Antigua and Barbuda",
42398         "ag",
42399         "1268"
42400       ],
42401       [
42402         "Argentina",
42403         "ar",
42404         "54"
42405       ],
42406       [
42407         "Armenia (Հայաստան)",
42408         "am",
42409         "374"
42410       ],
42411       [
42412         "Aruba",
42413         "aw",
42414         "297"
42415       ],
42416       [
42417         "Australia",
42418         "au",
42419         "61",
42420         0
42421       ],
42422       [
42423         "Austria (Österreich)",
42424         "at",
42425         "43"
42426       ],
42427       [
42428         "Azerbaijan (Azərbaycan)",
42429         "az",
42430         "994"
42431       ],
42432       [
42433         "Bahamas",
42434         "bs",
42435         "1242"
42436       ],
42437       [
42438         "Bahrain (‫البحرين‬‎)",
42439         "bh",
42440         "973"
42441       ],
42442       [
42443         "Bangladesh (বাংলাদেশ)",
42444         "bd",
42445         "880"
42446       ],
42447       [
42448         "Barbados",
42449         "bb",
42450         "1246"
42451       ],
42452       [
42453         "Belarus (Беларусь)",
42454         "by",
42455         "375"
42456       ],
42457       [
42458         "Belgium (België)",
42459         "be",
42460         "32"
42461       ],
42462       [
42463         "Belize",
42464         "bz",
42465         "501"
42466       ],
42467       [
42468         "Benin (Bénin)",
42469         "bj",
42470         "229"
42471       ],
42472       [
42473         "Bermuda",
42474         "bm",
42475         "1441"
42476       ],
42477       [
42478         "Bhutan (འབྲུག)",
42479         "bt",
42480         "975"
42481       ],
42482       [
42483         "Bolivia",
42484         "bo",
42485         "591"
42486       ],
42487       [
42488         "Bosnia and Herzegovina (Босна и Херцеговина)",
42489         "ba",
42490         "387"
42491       ],
42492       [
42493         "Botswana",
42494         "bw",
42495         "267"
42496       ],
42497       [
42498         "Brazil (Brasil)",
42499         "br",
42500         "55"
42501       ],
42502       [
42503         "British Indian Ocean Territory",
42504         "io",
42505         "246"
42506       ],
42507       [
42508         "British Virgin Islands",
42509         "vg",
42510         "1284"
42511       ],
42512       [
42513         "Brunei",
42514         "bn",
42515         "673"
42516       ],
42517       [
42518         "Bulgaria (България)",
42519         "bg",
42520         "359"
42521       ],
42522       [
42523         "Burkina Faso",
42524         "bf",
42525         "226"
42526       ],
42527       [
42528         "Burundi (Uburundi)",
42529         "bi",
42530         "257"
42531       ],
42532       [
42533         "Cambodia (កម្ពុជា)",
42534         "kh",
42535         "855"
42536       ],
42537       [
42538         "Cameroon (Cameroun)",
42539         "cm",
42540         "237"
42541       ],
42542       [
42543         "Canada",
42544         "ca",
42545         "1",
42546         1,
42547         ["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"]
42548       ],
42549       [
42550         "Cape Verde (Kabu Verdi)",
42551         "cv",
42552         "238"
42553       ],
42554       [
42555         "Caribbean Netherlands",
42556         "bq",
42557         "599",
42558         1
42559       ],
42560       [
42561         "Cayman Islands",
42562         "ky",
42563         "1345"
42564       ],
42565       [
42566         "Central African Republic (République centrafricaine)",
42567         "cf",
42568         "236"
42569       ],
42570       [
42571         "Chad (Tchad)",
42572         "td",
42573         "235"
42574       ],
42575       [
42576         "Chile",
42577         "cl",
42578         "56"
42579       ],
42580       [
42581         "China (中国)",
42582         "cn",
42583         "86"
42584       ],
42585       [
42586         "Christmas Island",
42587         "cx",
42588         "61",
42589         2
42590       ],
42591       [
42592         "Cocos (Keeling) Islands",
42593         "cc",
42594         "61",
42595         1
42596       ],
42597       [
42598         "Colombia",
42599         "co",
42600         "57"
42601       ],
42602       [
42603         "Comoros (‫جزر القمر‬‎)",
42604         "km",
42605         "269"
42606       ],
42607       [
42608         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42609         "cd",
42610         "243"
42611       ],
42612       [
42613         "Congo (Republic) (Congo-Brazzaville)",
42614         "cg",
42615         "242"
42616       ],
42617       [
42618         "Cook Islands",
42619         "ck",
42620         "682"
42621       ],
42622       [
42623         "Costa Rica",
42624         "cr",
42625         "506"
42626       ],
42627       [
42628         "Côte d’Ivoire",
42629         "ci",
42630         "225"
42631       ],
42632       [
42633         "Croatia (Hrvatska)",
42634         "hr",
42635         "385"
42636       ],
42637       [
42638         "Cuba",
42639         "cu",
42640         "53"
42641       ],
42642       [
42643         "Curaçao",
42644         "cw",
42645         "599",
42646         0
42647       ],
42648       [
42649         "Cyprus (Κύπρος)",
42650         "cy",
42651         "357"
42652       ],
42653       [
42654         "Czech Republic (Česká republika)",
42655         "cz",
42656         "420"
42657       ],
42658       [
42659         "Denmark (Danmark)",
42660         "dk",
42661         "45"
42662       ],
42663       [
42664         "Djibouti",
42665         "dj",
42666         "253"
42667       ],
42668       [
42669         "Dominica",
42670         "dm",
42671         "1767"
42672       ],
42673       [
42674         "Dominican Republic (República Dominicana)",
42675         "do",
42676         "1",
42677         2,
42678         ["809", "829", "849"]
42679       ],
42680       [
42681         "Ecuador",
42682         "ec",
42683         "593"
42684       ],
42685       [
42686         "Egypt (‫مصر‬‎)",
42687         "eg",
42688         "20"
42689       ],
42690       [
42691         "El Salvador",
42692         "sv",
42693         "503"
42694       ],
42695       [
42696         "Equatorial Guinea (Guinea Ecuatorial)",
42697         "gq",
42698         "240"
42699       ],
42700       [
42701         "Eritrea",
42702         "er",
42703         "291"
42704       ],
42705       [
42706         "Estonia (Eesti)",
42707         "ee",
42708         "372"
42709       ],
42710       [
42711         "Ethiopia",
42712         "et",
42713         "251"
42714       ],
42715       [
42716         "Falkland Islands (Islas Malvinas)",
42717         "fk",
42718         "500"
42719       ],
42720       [
42721         "Faroe Islands (Føroyar)",
42722         "fo",
42723         "298"
42724       ],
42725       [
42726         "Fiji",
42727         "fj",
42728         "679"
42729       ],
42730       [
42731         "Finland (Suomi)",
42732         "fi",
42733         "358",
42734         0
42735       ],
42736       [
42737         "France",
42738         "fr",
42739         "33"
42740       ],
42741       [
42742         "French Guiana (Guyane française)",
42743         "gf",
42744         "594"
42745       ],
42746       [
42747         "French Polynesia (Polynésie française)",
42748         "pf",
42749         "689"
42750       ],
42751       [
42752         "Gabon",
42753         "ga",
42754         "241"
42755       ],
42756       [
42757         "Gambia",
42758         "gm",
42759         "220"
42760       ],
42761       [
42762         "Georgia (საქართველო)",
42763         "ge",
42764         "995"
42765       ],
42766       [
42767         "Germany (Deutschland)",
42768         "de",
42769         "49"
42770       ],
42771       [
42772         "Ghana (Gaana)",
42773         "gh",
42774         "233"
42775       ],
42776       [
42777         "Gibraltar",
42778         "gi",
42779         "350"
42780       ],
42781       [
42782         "Greece (Ελλάδα)",
42783         "gr",
42784         "30"
42785       ],
42786       [
42787         "Greenland (Kalaallit Nunaat)",
42788         "gl",
42789         "299"
42790       ],
42791       [
42792         "Grenada",
42793         "gd",
42794         "1473"
42795       ],
42796       [
42797         "Guadeloupe",
42798         "gp",
42799         "590",
42800         0
42801       ],
42802       [
42803         "Guam",
42804         "gu",
42805         "1671"
42806       ],
42807       [
42808         "Guatemala",
42809         "gt",
42810         "502"
42811       ],
42812       [
42813         "Guernsey",
42814         "gg",
42815         "44",
42816         1
42817       ],
42818       [
42819         "Guinea (Guinée)",
42820         "gn",
42821         "224"
42822       ],
42823       [
42824         "Guinea-Bissau (Guiné Bissau)",
42825         "gw",
42826         "245"
42827       ],
42828       [
42829         "Guyana",
42830         "gy",
42831         "592"
42832       ],
42833       [
42834         "Haiti",
42835         "ht",
42836         "509"
42837       ],
42838       [
42839         "Honduras",
42840         "hn",
42841         "504"
42842       ],
42843       [
42844         "Hong Kong (香港)",
42845         "hk",
42846         "852"
42847       ],
42848       [
42849         "Hungary (Magyarország)",
42850         "hu",
42851         "36"
42852       ],
42853       [
42854         "Iceland (Ísland)",
42855         "is",
42856         "354"
42857       ],
42858       [
42859         "India (भारत)",
42860         "in",
42861         "91"
42862       ],
42863       [
42864         "Indonesia",
42865         "id",
42866         "62"
42867       ],
42868       [
42869         "Iran (‫ایران‬‎)",
42870         "ir",
42871         "98"
42872       ],
42873       [
42874         "Iraq (‫العراق‬‎)",
42875         "iq",
42876         "964"
42877       ],
42878       [
42879         "Ireland",
42880         "ie",
42881         "353"
42882       ],
42883       [
42884         "Isle of Man",
42885         "im",
42886         "44",
42887         2
42888       ],
42889       [
42890         "Israel (‫ישראל‬‎)",
42891         "il",
42892         "972"
42893       ],
42894       [
42895         "Italy (Italia)",
42896         "it",
42897         "39",
42898         0
42899       ],
42900       [
42901         "Jamaica",
42902         "jm",
42903         "1876"
42904       ],
42905       [
42906         "Japan (日本)",
42907         "jp",
42908         "81"
42909       ],
42910       [
42911         "Jersey",
42912         "je",
42913         "44",
42914         3
42915       ],
42916       [
42917         "Jordan (‫الأردن‬‎)",
42918         "jo",
42919         "962"
42920       ],
42921       [
42922         "Kazakhstan (Казахстан)",
42923         "kz",
42924         "7",
42925         1
42926       ],
42927       [
42928         "Kenya",
42929         "ke",
42930         "254"
42931       ],
42932       [
42933         "Kiribati",
42934         "ki",
42935         "686"
42936       ],
42937       [
42938         "Kosovo",
42939         "xk",
42940         "383"
42941       ],
42942       [
42943         "Kuwait (‫الكويت‬‎)",
42944         "kw",
42945         "965"
42946       ],
42947       [
42948         "Kyrgyzstan (Кыргызстан)",
42949         "kg",
42950         "996"
42951       ],
42952       [
42953         "Laos (ລາວ)",
42954         "la",
42955         "856"
42956       ],
42957       [
42958         "Latvia (Latvija)",
42959         "lv",
42960         "371"
42961       ],
42962       [
42963         "Lebanon (‫لبنان‬‎)",
42964         "lb",
42965         "961"
42966       ],
42967       [
42968         "Lesotho",
42969         "ls",
42970         "266"
42971       ],
42972       [
42973         "Liberia",
42974         "lr",
42975         "231"
42976       ],
42977       [
42978         "Libya (‫ليبيا‬‎)",
42979         "ly",
42980         "218"
42981       ],
42982       [
42983         "Liechtenstein",
42984         "li",
42985         "423"
42986       ],
42987       [
42988         "Lithuania (Lietuva)",
42989         "lt",
42990         "370"
42991       ],
42992       [
42993         "Luxembourg",
42994         "lu",
42995         "352"
42996       ],
42997       [
42998         "Macau (澳門)",
42999         "mo",
43000         "853"
43001       ],
43002       [
43003         "Macedonia (FYROM) (Македонија)",
43004         "mk",
43005         "389"
43006       ],
43007       [
43008         "Madagascar (Madagasikara)",
43009         "mg",
43010         "261"
43011       ],
43012       [
43013         "Malawi",
43014         "mw",
43015         "265"
43016       ],
43017       [
43018         "Malaysia",
43019         "my",
43020         "60"
43021       ],
43022       [
43023         "Maldives",
43024         "mv",
43025         "960"
43026       ],
43027       [
43028         "Mali",
43029         "ml",
43030         "223"
43031       ],
43032       [
43033         "Malta",
43034         "mt",
43035         "356"
43036       ],
43037       [
43038         "Marshall Islands",
43039         "mh",
43040         "692"
43041       ],
43042       [
43043         "Martinique",
43044         "mq",
43045         "596"
43046       ],
43047       [
43048         "Mauritania (‫موريتانيا‬‎)",
43049         "mr",
43050         "222"
43051       ],
43052       [
43053         "Mauritius (Moris)",
43054         "mu",
43055         "230"
43056       ],
43057       [
43058         "Mayotte",
43059         "yt",
43060         "262",
43061         1
43062       ],
43063       [
43064         "Mexico (México)",
43065         "mx",
43066         "52"
43067       ],
43068       [
43069         "Micronesia",
43070         "fm",
43071         "691"
43072       ],
43073       [
43074         "Moldova (Republica Moldova)",
43075         "md",
43076         "373"
43077       ],
43078       [
43079         "Monaco",
43080         "mc",
43081         "377"
43082       ],
43083       [
43084         "Mongolia (Монгол)",
43085         "mn",
43086         "976"
43087       ],
43088       [
43089         "Montenegro (Crna Gora)",
43090         "me",
43091         "382"
43092       ],
43093       [
43094         "Montserrat",
43095         "ms",
43096         "1664"
43097       ],
43098       [
43099         "Morocco (‫المغرب‬‎)",
43100         "ma",
43101         "212",
43102         0
43103       ],
43104       [
43105         "Mozambique (Moçambique)",
43106         "mz",
43107         "258"
43108       ],
43109       [
43110         "Myanmar (Burma) (မြန်မာ)",
43111         "mm",
43112         "95"
43113       ],
43114       [
43115         "Namibia (Namibië)",
43116         "na",
43117         "264"
43118       ],
43119       [
43120         "Nauru",
43121         "nr",
43122         "674"
43123       ],
43124       [
43125         "Nepal (नेपाल)",
43126         "np",
43127         "977"
43128       ],
43129       [
43130         "Netherlands (Nederland)",
43131         "nl",
43132         "31"
43133       ],
43134       [
43135         "New Caledonia (Nouvelle-Calédonie)",
43136         "nc",
43137         "687"
43138       ],
43139       [
43140         "New Zealand",
43141         "nz",
43142         "64"
43143       ],
43144       [
43145         "Nicaragua",
43146         "ni",
43147         "505"
43148       ],
43149       [
43150         "Niger (Nijar)",
43151         "ne",
43152         "227"
43153       ],
43154       [
43155         "Nigeria",
43156         "ng",
43157         "234"
43158       ],
43159       [
43160         "Niue",
43161         "nu",
43162         "683"
43163       ],
43164       [
43165         "Norfolk Island",
43166         "nf",
43167         "672"
43168       ],
43169       [
43170         "North Korea (조선 민주주의 인민 공화국)",
43171         "kp",
43172         "850"
43173       ],
43174       [
43175         "Northern Mariana Islands",
43176         "mp",
43177         "1670"
43178       ],
43179       [
43180         "Norway (Norge)",
43181         "no",
43182         "47",
43183         0
43184       ],
43185       [
43186         "Oman (‫عُمان‬‎)",
43187         "om",
43188         "968"
43189       ],
43190       [
43191         "Pakistan (‫پاکستان‬‎)",
43192         "pk",
43193         "92"
43194       ],
43195       [
43196         "Palau",
43197         "pw",
43198         "680"
43199       ],
43200       [
43201         "Palestine (‫فلسطين‬‎)",
43202         "ps",
43203         "970"
43204       ],
43205       [
43206         "Panama (Panamá)",
43207         "pa",
43208         "507"
43209       ],
43210       [
43211         "Papua New Guinea",
43212         "pg",
43213         "675"
43214       ],
43215       [
43216         "Paraguay",
43217         "py",
43218         "595"
43219       ],
43220       [
43221         "Peru (Perú)",
43222         "pe",
43223         "51"
43224       ],
43225       [
43226         "Philippines",
43227         "ph",
43228         "63"
43229       ],
43230       [
43231         "Poland (Polska)",
43232         "pl",
43233         "48"
43234       ],
43235       [
43236         "Portugal",
43237         "pt",
43238         "351"
43239       ],
43240       [
43241         "Puerto Rico",
43242         "pr",
43243         "1",
43244         3,
43245         ["787", "939"]
43246       ],
43247       [
43248         "Qatar (‫قطر‬‎)",
43249         "qa",
43250         "974"
43251       ],
43252       [
43253         "Réunion (La Réunion)",
43254         "re",
43255         "262",
43256         0
43257       ],
43258       [
43259         "Romania (România)",
43260         "ro",
43261         "40"
43262       ],
43263       [
43264         "Russia (Россия)",
43265         "ru",
43266         "7",
43267         0
43268       ],
43269       [
43270         "Rwanda",
43271         "rw",
43272         "250"
43273       ],
43274       [
43275         "Saint Barthélemy",
43276         "bl",
43277         "590",
43278         1
43279       ],
43280       [
43281         "Saint Helena",
43282         "sh",
43283         "290"
43284       ],
43285       [
43286         "Saint Kitts and Nevis",
43287         "kn",
43288         "1869"
43289       ],
43290       [
43291         "Saint Lucia",
43292         "lc",
43293         "1758"
43294       ],
43295       [
43296         "Saint Martin (Saint-Martin (partie française))",
43297         "mf",
43298         "590",
43299         2
43300       ],
43301       [
43302         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
43303         "pm",
43304         "508"
43305       ],
43306       [
43307         "Saint Vincent and the Grenadines",
43308         "vc",
43309         "1784"
43310       ],
43311       [
43312         "Samoa",
43313         "ws",
43314         "685"
43315       ],
43316       [
43317         "San Marino",
43318         "sm",
43319         "378"
43320       ],
43321       [
43322         "São Tomé and Príncipe (São Tomé e Príncipe)",
43323         "st",
43324         "239"
43325       ],
43326       [
43327         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
43328         "sa",
43329         "966"
43330       ],
43331       [
43332         "Senegal (Sénégal)",
43333         "sn",
43334         "221"
43335       ],
43336       [
43337         "Serbia (Србија)",
43338         "rs",
43339         "381"
43340       ],
43341       [
43342         "Seychelles",
43343         "sc",
43344         "248"
43345       ],
43346       [
43347         "Sierra Leone",
43348         "sl",
43349         "232"
43350       ],
43351       [
43352         "Singapore",
43353         "sg",
43354         "65"
43355       ],
43356       [
43357         "Sint Maarten",
43358         "sx",
43359         "1721"
43360       ],
43361       [
43362         "Slovakia (Slovensko)",
43363         "sk",
43364         "421"
43365       ],
43366       [
43367         "Slovenia (Slovenija)",
43368         "si",
43369         "386"
43370       ],
43371       [
43372         "Solomon Islands",
43373         "sb",
43374         "677"
43375       ],
43376       [
43377         "Somalia (Soomaaliya)",
43378         "so",
43379         "252"
43380       ],
43381       [
43382         "South Africa",
43383         "za",
43384         "27"
43385       ],
43386       [
43387         "South Korea (대한민국)",
43388         "kr",
43389         "82"
43390       ],
43391       [
43392         "South Sudan (‫جنوب السودان‬‎)",
43393         "ss",
43394         "211"
43395       ],
43396       [
43397         "Spain (España)",
43398         "es",
43399         "34"
43400       ],
43401       [
43402         "Sri Lanka (ශ්‍රී ලංකාව)",
43403         "lk",
43404         "94"
43405       ],
43406       [
43407         "Sudan (‫السودان‬‎)",
43408         "sd",
43409         "249"
43410       ],
43411       [
43412         "Suriname",
43413         "sr",
43414         "597"
43415       ],
43416       [
43417         "Svalbard and Jan Mayen",
43418         "sj",
43419         "47",
43420         1
43421       ],
43422       [
43423         "Swaziland",
43424         "sz",
43425         "268"
43426       ],
43427       [
43428         "Sweden (Sverige)",
43429         "se",
43430         "46"
43431       ],
43432       [
43433         "Switzerland (Schweiz)",
43434         "ch",
43435         "41"
43436       ],
43437       [
43438         "Syria (‫سوريا‬‎)",
43439         "sy",
43440         "963"
43441       ],
43442       [
43443         "Taiwan (台灣)",
43444         "tw",
43445         "886"
43446       ],
43447       [
43448         "Tajikistan",
43449         "tj",
43450         "992"
43451       ],
43452       [
43453         "Tanzania",
43454         "tz",
43455         "255"
43456       ],
43457       [
43458         "Thailand (ไทย)",
43459         "th",
43460         "66"
43461       ],
43462       [
43463         "Timor-Leste",
43464         "tl",
43465         "670"
43466       ],
43467       [
43468         "Togo",
43469         "tg",
43470         "228"
43471       ],
43472       [
43473         "Tokelau",
43474         "tk",
43475         "690"
43476       ],
43477       [
43478         "Tonga",
43479         "to",
43480         "676"
43481       ],
43482       [
43483         "Trinidad and Tobago",
43484         "tt",
43485         "1868"
43486       ],
43487       [
43488         "Tunisia (‫تونس‬‎)",
43489         "tn",
43490         "216"
43491       ],
43492       [
43493         "Turkey (Türkiye)",
43494         "tr",
43495         "90"
43496       ],
43497       [
43498         "Turkmenistan",
43499         "tm",
43500         "993"
43501       ],
43502       [
43503         "Turks and Caicos Islands",
43504         "tc",
43505         "1649"
43506       ],
43507       [
43508         "Tuvalu",
43509         "tv",
43510         "688"
43511       ],
43512       [
43513         "U.S. Virgin Islands",
43514         "vi",
43515         "1340"
43516       ],
43517       [
43518         "Uganda",
43519         "ug",
43520         "256"
43521       ],
43522       [
43523         "Ukraine (Україна)",
43524         "ua",
43525         "380"
43526       ],
43527       [
43528         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43529         "ae",
43530         "971"
43531       ],
43532       [
43533         "United Kingdom",
43534         "gb",
43535         "44",
43536         0
43537       ],
43538       [
43539         "United States",
43540         "us",
43541         "1",
43542         0
43543       ],
43544       [
43545         "Uruguay",
43546         "uy",
43547         "598"
43548       ],
43549       [
43550         "Uzbekistan (Oʻzbekiston)",
43551         "uz",
43552         "998"
43553       ],
43554       [
43555         "Vanuatu",
43556         "vu",
43557         "678"
43558       ],
43559       [
43560         "Vatican City (Città del Vaticano)",
43561         "va",
43562         "39",
43563         1
43564       ],
43565       [
43566         "Venezuela",
43567         "ve",
43568         "58"
43569       ],
43570       [
43571         "Vietnam (Việt Nam)",
43572         "vn",
43573         "84"
43574       ],
43575       [
43576         "Wallis and Futuna (Wallis-et-Futuna)",
43577         "wf",
43578         "681"
43579       ],
43580       [
43581         "Western Sahara (‫الصحراء الغربية‬‎)",
43582         "eh",
43583         "212",
43584         1
43585       ],
43586       [
43587         "Yemen (‫اليمن‬‎)",
43588         "ye",
43589         "967"
43590       ],
43591       [
43592         "Zambia",
43593         "zm",
43594         "260"
43595       ],
43596       [
43597         "Zimbabwe",
43598         "zw",
43599         "263"
43600       ],
43601       [
43602         "Åland Islands",
43603         "ax",
43604         "358",
43605         1
43606       ]
43607   ];
43608   
43609   return d;
43610 }/**
43611 *    This script refer to:
43612 *    Title: International Telephone Input
43613 *    Author: Jack O'Connor
43614 *    Code version:  v12.1.12
43615 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43616 **/
43617
43618 /**
43619  * @class Roo.bootstrap.PhoneInput
43620  * @extends Roo.bootstrap.TriggerField
43621  * An input with International dial-code selection
43622  
43623  * @cfg {String} defaultDialCode default '+852'
43624  * @cfg {Array} preferedCountries default []
43625   
43626  * @constructor
43627  * Create a new PhoneInput.
43628  * @param {Object} config Configuration options
43629  */
43630
43631 Roo.bootstrap.PhoneInput = function(config) {
43632     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43633 };
43634
43635 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43636         
43637         listWidth: undefined,
43638         
43639         selectedClass: 'active',
43640         
43641         invalidClass : "has-warning",
43642         
43643         validClass: 'has-success',
43644         
43645         allowed: '0123456789',
43646         
43647         max_length: 15,
43648         
43649         /**
43650          * @cfg {String} defaultDialCode The default dial code when initializing the input
43651          */
43652         defaultDialCode: '+852',
43653         
43654         /**
43655          * @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
43656          */
43657         preferedCountries: false,
43658         
43659         getAutoCreate : function()
43660         {
43661             var data = Roo.bootstrap.PhoneInputData();
43662             var align = this.labelAlign || this.parentLabelAlign();
43663             var id = Roo.id();
43664             
43665             this.allCountries = [];
43666             this.dialCodeMapping = [];
43667             
43668             for (var i = 0; i < data.length; i++) {
43669               var c = data[i];
43670               this.allCountries[i] = {
43671                 name: c[0],
43672                 iso2: c[1],
43673                 dialCode: c[2],
43674                 priority: c[3] || 0,
43675                 areaCodes: c[4] || null
43676               };
43677               this.dialCodeMapping[c[2]] = {
43678                   name: c[0],
43679                   iso2: c[1],
43680                   priority: c[3] || 0,
43681                   areaCodes: c[4] || null
43682               };
43683             }
43684             
43685             var cfg = {
43686                 cls: 'form-group',
43687                 cn: []
43688             };
43689             
43690             var input =  {
43691                 tag: 'input',
43692                 id : id,
43693                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43694                 maxlength: this.max_length,
43695                 cls : 'form-control tel-input',
43696                 autocomplete: 'new-password'
43697             };
43698             
43699             var hiddenInput = {
43700                 tag: 'input',
43701                 type: 'hidden',
43702                 cls: 'hidden-tel-input'
43703             };
43704             
43705             if (this.name) {
43706                 hiddenInput.name = this.name;
43707             }
43708             
43709             if (this.disabled) {
43710                 input.disabled = true;
43711             }
43712             
43713             var flag_container = {
43714                 tag: 'div',
43715                 cls: 'flag-box',
43716                 cn: [
43717                     {
43718                         tag: 'div',
43719                         cls: 'flag'
43720                     },
43721                     {
43722                         tag: 'div',
43723                         cls: 'caret'
43724                     }
43725                 ]
43726             };
43727             
43728             var box = {
43729                 tag: 'div',
43730                 cls: this.hasFeedback ? 'has-feedback' : '',
43731                 cn: [
43732                     hiddenInput,
43733                     input,
43734                     {
43735                         tag: 'input',
43736                         cls: 'dial-code-holder',
43737                         disabled: true
43738                     }
43739                 ]
43740             };
43741             
43742             var container = {
43743                 cls: 'roo-select2-container input-group',
43744                 cn: [
43745                     flag_container,
43746                     box
43747                 ]
43748             };
43749             
43750             if (this.fieldLabel.length) {
43751                 var indicator = {
43752                     tag: 'i',
43753                     tooltip: 'This field is required'
43754                 };
43755                 
43756                 var label = {
43757                     tag: 'label',
43758                     'for':  id,
43759                     cls: 'control-label',
43760                     cn: []
43761                 };
43762                 
43763                 var label_text = {
43764                     tag: 'span',
43765                     html: this.fieldLabel
43766                 };
43767                 
43768                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43769                 label.cn = [
43770                     indicator,
43771                     label_text
43772                 ];
43773                 
43774                 if(this.indicatorpos == 'right') {
43775                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43776                     label.cn = [
43777                         label_text,
43778                         indicator
43779                     ];
43780                 }
43781                 
43782                 if(align == 'left') {
43783                     container = {
43784                         tag: 'div',
43785                         cn: [
43786                             container
43787                         ]
43788                     };
43789                     
43790                     if(this.labelWidth > 12){
43791                         label.style = "width: " + this.labelWidth + 'px';
43792                     }
43793                     if(this.labelWidth < 13 && this.labelmd == 0){
43794                         this.labelmd = this.labelWidth;
43795                     }
43796                     if(this.labellg > 0){
43797                         label.cls += ' col-lg-' + this.labellg;
43798                         input.cls += ' col-lg-' + (12 - this.labellg);
43799                     }
43800                     if(this.labelmd > 0){
43801                         label.cls += ' col-md-' + this.labelmd;
43802                         container.cls += ' col-md-' + (12 - this.labelmd);
43803                     }
43804                     if(this.labelsm > 0){
43805                         label.cls += ' col-sm-' + this.labelsm;
43806                         container.cls += ' col-sm-' + (12 - this.labelsm);
43807                     }
43808                     if(this.labelxs > 0){
43809                         label.cls += ' col-xs-' + this.labelxs;
43810                         container.cls += ' col-xs-' + (12 - this.labelxs);
43811                     }
43812                 }
43813             }
43814             
43815             cfg.cn = [
43816                 label,
43817                 container
43818             ];
43819             
43820             var settings = this;
43821             
43822             ['xs','sm','md','lg'].map(function(size){
43823                 if (settings[size]) {
43824                     cfg.cls += ' col-' + size + '-' + settings[size];
43825                 }
43826             });
43827             
43828             this.store = new Roo.data.Store({
43829                 proxy : new Roo.data.MemoryProxy({}),
43830                 reader : new Roo.data.JsonReader({
43831                     fields : [
43832                         {
43833                             'name' : 'name',
43834                             'type' : 'string'
43835                         },
43836                         {
43837                             'name' : 'iso2',
43838                             'type' : 'string'
43839                         },
43840                         {
43841                             'name' : 'dialCode',
43842                             'type' : 'string'
43843                         },
43844                         {
43845                             'name' : 'priority',
43846                             'type' : 'string'
43847                         },
43848                         {
43849                             'name' : 'areaCodes',
43850                             'type' : 'string'
43851                         }
43852                     ]
43853                 })
43854             });
43855             
43856             if(!this.preferedCountries) {
43857                 this.preferedCountries = [
43858                     'hk',
43859                     'gb',
43860                     'us'
43861                 ];
43862             }
43863             
43864             var p = this.preferedCountries.reverse();
43865             
43866             if(p) {
43867                 for (var i = 0; i < p.length; i++) {
43868                     for (var j = 0; j < this.allCountries.length; j++) {
43869                         if(this.allCountries[j].iso2 == p[i]) {
43870                             var t = this.allCountries[j];
43871                             this.allCountries.splice(j,1);
43872                             this.allCountries.unshift(t);
43873                         }
43874                     } 
43875                 }
43876             }
43877             
43878             this.store.proxy.data = {
43879                 success: true,
43880                 data: this.allCountries
43881             };
43882             
43883             return cfg;
43884         },
43885         
43886         initEvents : function()
43887         {
43888             this.createList();
43889             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43890             
43891             this.indicator = this.indicatorEl();
43892             this.flag = this.flagEl();
43893             this.dialCodeHolder = this.dialCodeHolderEl();
43894             
43895             this.trigger = this.el.select('div.flag-box',true).first();
43896             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43897             
43898             var _this = this;
43899             
43900             (function(){
43901                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43902                 _this.list.setWidth(lw);
43903             }).defer(100);
43904             
43905             this.list.on('mouseover', this.onViewOver, this);
43906             this.list.on('mousemove', this.onViewMove, this);
43907             this.inputEl().on("keyup", this.onKeyUp, this);
43908             this.inputEl().on("keypress", this.onKeyPress, this);
43909             
43910             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43911
43912             this.view = new Roo.View(this.list, this.tpl, {
43913                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43914             });
43915             
43916             this.view.on('click', this.onViewClick, this);
43917             this.setValue(this.defaultDialCode);
43918         },
43919         
43920         onTriggerClick : function(e)
43921         {
43922             Roo.log('trigger click');
43923             if(this.disabled){
43924                 return;
43925             }
43926             
43927             if(this.isExpanded()){
43928                 this.collapse();
43929                 this.hasFocus = false;
43930             }else {
43931                 this.store.load({});
43932                 this.hasFocus = true;
43933                 this.expand();
43934             }
43935         },
43936         
43937         isExpanded : function()
43938         {
43939             return this.list.isVisible();
43940         },
43941         
43942         collapse : function()
43943         {
43944             if(!this.isExpanded()){
43945                 return;
43946             }
43947             this.list.hide();
43948             Roo.get(document).un('mousedown', this.collapseIf, this);
43949             Roo.get(document).un('mousewheel', this.collapseIf, this);
43950             this.fireEvent('collapse', this);
43951             this.validate();
43952         },
43953         
43954         expand : function()
43955         {
43956             Roo.log('expand');
43957
43958             if(this.isExpanded() || !this.hasFocus){
43959                 return;
43960             }
43961             
43962             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43963             this.list.setWidth(lw);
43964             
43965             this.list.show();
43966             this.restrictHeight();
43967             
43968             Roo.get(document).on('mousedown', this.collapseIf, this);
43969             Roo.get(document).on('mousewheel', this.collapseIf, this);
43970             
43971             this.fireEvent('expand', this);
43972         },
43973         
43974         restrictHeight : function()
43975         {
43976             this.list.alignTo(this.inputEl(), this.listAlign);
43977             this.list.alignTo(this.inputEl(), this.listAlign);
43978         },
43979         
43980         onViewOver : function(e, t)
43981         {
43982             if(this.inKeyMode){
43983                 return;
43984             }
43985             var item = this.view.findItemFromChild(t);
43986             
43987             if(item){
43988                 var index = this.view.indexOf(item);
43989                 this.select(index, false);
43990             }
43991         },
43992
43993         // private
43994         onViewClick : function(view, doFocus, el, e)
43995         {
43996             var index = this.view.getSelectedIndexes()[0];
43997             
43998             var r = this.store.getAt(index);
43999             
44000             if(r){
44001                 this.onSelect(r, index);
44002             }
44003             if(doFocus !== false && !this.blockFocus){
44004                 this.inputEl().focus();
44005             }
44006         },
44007         
44008         onViewMove : function(e, t)
44009         {
44010             this.inKeyMode = false;
44011         },
44012         
44013         select : function(index, scrollIntoView)
44014         {
44015             this.selectedIndex = index;
44016             this.view.select(index);
44017             if(scrollIntoView !== false){
44018                 var el = this.view.getNode(index);
44019                 if(el){
44020                     this.list.scrollChildIntoView(el, false);
44021                 }
44022             }
44023         },
44024         
44025         createList : function()
44026         {
44027             this.list = Roo.get(document.body).createChild({
44028                 tag: 'ul',
44029                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
44030                 style: 'display:none'
44031             });
44032             
44033             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
44034         },
44035         
44036         collapseIf : function(e)
44037         {
44038             var in_combo  = e.within(this.el);
44039             var in_list =  e.within(this.list);
44040             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
44041             
44042             if (in_combo || in_list || is_list) {
44043                 return;
44044             }
44045             this.collapse();
44046         },
44047         
44048         onSelect : function(record, index)
44049         {
44050             if(this.fireEvent('beforeselect', this, record, index) !== false){
44051                 
44052                 this.setFlagClass(record.data.iso2);
44053                 this.setDialCode(record.data.dialCode);
44054                 this.hasFocus = false;
44055                 this.collapse();
44056                 this.fireEvent('select', this, record, index);
44057             }
44058         },
44059         
44060         flagEl : function()
44061         {
44062             var flag = this.el.select('div.flag',true).first();
44063             if(!flag){
44064                 return false;
44065             }
44066             return flag;
44067         },
44068         
44069         dialCodeHolderEl : function()
44070         {
44071             var d = this.el.select('input.dial-code-holder',true).first();
44072             if(!d){
44073                 return false;
44074             }
44075             return d;
44076         },
44077         
44078         setDialCode : function(v)
44079         {
44080             this.dialCodeHolder.dom.value = '+'+v;
44081         },
44082         
44083         setFlagClass : function(n)
44084         {
44085             this.flag.dom.className = 'flag '+n;
44086         },
44087         
44088         getValue : function()
44089         {
44090             var v = this.inputEl().getValue();
44091             if(this.dialCodeHolder) {
44092                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
44093             }
44094             return v;
44095         },
44096         
44097         setValue : function(v)
44098         {
44099             var d = this.getDialCode(v);
44100             
44101             //invalid dial code
44102             if(v.length == 0 || !d || d.length == 0) {
44103                 if(this.rendered){
44104                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
44105                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44106                 }
44107                 return;
44108             }
44109             
44110             //valid dial code
44111             this.setFlagClass(this.dialCodeMapping[d].iso2);
44112             this.setDialCode(d);
44113             this.inputEl().dom.value = v.replace('+'+d,'');
44114             this.hiddenEl().dom.value = this.getValue();
44115             
44116             this.validate();
44117         },
44118         
44119         getDialCode : function(v)
44120         {
44121             v = v ||  '';
44122             
44123             if (v.length == 0) {
44124                 return this.dialCodeHolder.dom.value;
44125             }
44126             
44127             var dialCode = "";
44128             if (v.charAt(0) != "+") {
44129                 return false;
44130             }
44131             var numericChars = "";
44132             for (var i = 1; i < v.length; i++) {
44133               var c = v.charAt(i);
44134               if (!isNaN(c)) {
44135                 numericChars += c;
44136                 if (this.dialCodeMapping[numericChars]) {
44137                   dialCode = v.substr(1, i);
44138                 }
44139                 if (numericChars.length == 4) {
44140                   break;
44141                 }
44142               }
44143             }
44144             return dialCode;
44145         },
44146         
44147         reset : function()
44148         {
44149             this.setValue(this.defaultDialCode);
44150             this.validate();
44151         },
44152         
44153         hiddenEl : function()
44154         {
44155             return this.el.select('input.hidden-tel-input',true).first();
44156         },
44157         
44158         // after setting val
44159         onKeyUp : function(e){
44160             this.setValue(this.getValue());
44161         },
44162         
44163         onKeyPress : function(e){
44164             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
44165                 e.stopEvent();
44166             }
44167         }
44168         
44169 });
44170 /**
44171  * @class Roo.bootstrap.MoneyField
44172  * @extends Roo.bootstrap.ComboBox
44173  * Bootstrap MoneyField class
44174  * 
44175  * @constructor
44176  * Create a new MoneyField.
44177  * @param {Object} config Configuration options
44178  */
44179
44180 Roo.bootstrap.MoneyField = function(config) {
44181     
44182     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
44183     
44184 };
44185
44186 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
44187     
44188     /**
44189      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
44190      */
44191     allowDecimals : true,
44192     /**
44193      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
44194      */
44195     decimalSeparator : ".",
44196     /**
44197      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
44198      */
44199     decimalPrecision : 0,
44200     /**
44201      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
44202      */
44203     allowNegative : true,
44204     /**
44205      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
44206      */
44207     allowZero: true,
44208     /**
44209      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
44210      */
44211     minValue : Number.NEGATIVE_INFINITY,
44212     /**
44213      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
44214      */
44215     maxValue : Number.MAX_VALUE,
44216     /**
44217      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
44218      */
44219     minText : "The minimum value for this field is {0}",
44220     /**
44221      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
44222      */
44223     maxText : "The maximum value for this field is {0}",
44224     /**
44225      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
44226      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
44227      */
44228     nanText : "{0} is not a valid number",
44229     /**
44230      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
44231      */
44232     castInt : true,
44233     /**
44234      * @cfg {String} defaults currency of the MoneyField
44235      * value should be in lkey
44236      */
44237     defaultCurrency : false,
44238     /**
44239      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
44240      */
44241     thousandsDelimiter : false,
44242     /**
44243      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
44244      */
44245     max_length: false,
44246     
44247     inputlg : 9,
44248     inputmd : 9,
44249     inputsm : 9,
44250     inputxs : 6,
44251     
44252     store : false,
44253     
44254     getAutoCreate : function()
44255     {
44256         var align = this.labelAlign || this.parentLabelAlign();
44257         
44258         var id = Roo.id();
44259
44260         var cfg = {
44261             cls: 'form-group',
44262             cn: []
44263         };
44264
44265         var input =  {
44266             tag: 'input',
44267             id : id,
44268             cls : 'form-control roo-money-amount-input',
44269             autocomplete: 'new-password'
44270         };
44271         
44272         var hiddenInput = {
44273             tag: 'input',
44274             type: 'hidden',
44275             id: Roo.id(),
44276             cls: 'hidden-number-input'
44277         };
44278         
44279         if(this.max_length) {
44280             input.maxlength = this.max_length; 
44281         }
44282         
44283         if (this.name) {
44284             hiddenInput.name = this.name;
44285         }
44286
44287         if (this.disabled) {
44288             input.disabled = true;
44289         }
44290
44291         var clg = 12 - this.inputlg;
44292         var cmd = 12 - this.inputmd;
44293         var csm = 12 - this.inputsm;
44294         var cxs = 12 - this.inputxs;
44295         
44296         var container = {
44297             tag : 'div',
44298             cls : 'row roo-money-field',
44299             cn : [
44300                 {
44301                     tag : 'div',
44302                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
44303                     cn : [
44304                         {
44305                             tag : 'div',
44306                             cls: 'roo-select2-container input-group',
44307                             cn: [
44308                                 {
44309                                     tag : 'input',
44310                                     cls : 'form-control roo-money-currency-input',
44311                                     autocomplete: 'new-password',
44312                                     readOnly : 1,
44313                                     name : this.currencyName
44314                                 },
44315                                 {
44316                                     tag :'span',
44317                                     cls : 'input-group-addon',
44318                                     cn : [
44319                                         {
44320                                             tag: 'span',
44321                                             cls: 'caret'
44322                                         }
44323                                     ]
44324                                 }
44325                             ]
44326                         }
44327                     ]
44328                 },
44329                 {
44330                     tag : 'div',
44331                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
44332                     cn : [
44333                         {
44334                             tag: 'div',
44335                             cls: this.hasFeedback ? 'has-feedback' : '',
44336                             cn: [
44337                                 input
44338                             ]
44339                         }
44340                     ]
44341                 }
44342             ]
44343             
44344         };
44345         
44346         if (this.fieldLabel.length) {
44347             var indicator = {
44348                 tag: 'i',
44349                 tooltip: 'This field is required'
44350             };
44351
44352             var label = {
44353                 tag: 'label',
44354                 'for':  id,
44355                 cls: 'control-label',
44356                 cn: []
44357             };
44358
44359             var label_text = {
44360                 tag: 'span',
44361                 html: this.fieldLabel
44362             };
44363
44364             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
44365             label.cn = [
44366                 indicator,
44367                 label_text
44368             ];
44369
44370             if(this.indicatorpos == 'right') {
44371                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
44372                 label.cn = [
44373                     label_text,
44374                     indicator
44375                 ];
44376             }
44377
44378             if(align == 'left') {
44379                 container = {
44380                     tag: 'div',
44381                     cn: [
44382                         container
44383                     ]
44384                 };
44385
44386                 if(this.labelWidth > 12){
44387                     label.style = "width: " + this.labelWidth + 'px';
44388                 }
44389                 if(this.labelWidth < 13 && this.labelmd == 0){
44390                     this.labelmd = this.labelWidth;
44391                 }
44392                 if(this.labellg > 0){
44393                     label.cls += ' col-lg-' + this.labellg;
44394                     input.cls += ' col-lg-' + (12 - this.labellg);
44395                 }
44396                 if(this.labelmd > 0){
44397                     label.cls += ' col-md-' + this.labelmd;
44398                     container.cls += ' col-md-' + (12 - this.labelmd);
44399                 }
44400                 if(this.labelsm > 0){
44401                     label.cls += ' col-sm-' + this.labelsm;
44402                     container.cls += ' col-sm-' + (12 - this.labelsm);
44403                 }
44404                 if(this.labelxs > 0){
44405                     label.cls += ' col-xs-' + this.labelxs;
44406                     container.cls += ' col-xs-' + (12 - this.labelxs);
44407                 }
44408             }
44409         }
44410
44411         cfg.cn = [
44412             label,
44413             container,
44414             hiddenInput
44415         ];
44416         
44417         var settings = this;
44418
44419         ['xs','sm','md','lg'].map(function(size){
44420             if (settings[size]) {
44421                 cfg.cls += ' col-' + size + '-' + settings[size];
44422             }
44423         });
44424         
44425         return cfg;
44426     },
44427     
44428     initEvents : function()
44429     {
44430         this.indicator = this.indicatorEl();
44431         
44432         this.initCurrencyEvent();
44433         
44434         this.initNumberEvent();
44435     },
44436     
44437     initCurrencyEvent : function()
44438     {
44439         if (!this.store) {
44440             throw "can not find store for combo";
44441         }
44442         
44443         this.store = Roo.factory(this.store, Roo.data);
44444         this.store.parent = this;
44445         
44446         this.createList();
44447         
44448         this.triggerEl = this.el.select('.input-group-addon', true).first();
44449         
44450         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
44451         
44452         var _this = this;
44453         
44454         (function(){
44455             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
44456             _this.list.setWidth(lw);
44457         }).defer(100);
44458         
44459         this.list.on('mouseover', this.onViewOver, this);
44460         this.list.on('mousemove', this.onViewMove, this);
44461         this.list.on('scroll', this.onViewScroll, this);
44462         
44463         if(!this.tpl){
44464             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
44465         }
44466         
44467         this.view = new Roo.View(this.list, this.tpl, {
44468             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44469         });
44470         
44471         this.view.on('click', this.onViewClick, this);
44472         
44473         this.store.on('beforeload', this.onBeforeLoad, this);
44474         this.store.on('load', this.onLoad, this);
44475         this.store.on('loadexception', this.onLoadException, this);
44476         
44477         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44478             "up" : function(e){
44479                 this.inKeyMode = true;
44480                 this.selectPrev();
44481             },
44482
44483             "down" : function(e){
44484                 if(!this.isExpanded()){
44485                     this.onTriggerClick();
44486                 }else{
44487                     this.inKeyMode = true;
44488                     this.selectNext();
44489                 }
44490             },
44491
44492             "enter" : function(e){
44493                 this.collapse();
44494                 
44495                 if(this.fireEvent("specialkey", this, e)){
44496                     this.onViewClick(false);
44497                 }
44498                 
44499                 return true;
44500             },
44501
44502             "esc" : function(e){
44503                 this.collapse();
44504             },
44505
44506             "tab" : function(e){
44507                 this.collapse();
44508                 
44509                 if(this.fireEvent("specialkey", this, e)){
44510                     this.onViewClick(false);
44511                 }
44512                 
44513                 return true;
44514             },
44515
44516             scope : this,
44517
44518             doRelay : function(foo, bar, hname){
44519                 if(hname == 'down' || this.scope.isExpanded()){
44520                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44521                 }
44522                 return true;
44523             },
44524
44525             forceKeyDown: true
44526         });
44527         
44528         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44529         
44530     },
44531     
44532     initNumberEvent : function(e)
44533     {
44534         this.inputEl().on("keydown" , this.fireKey,  this);
44535         this.inputEl().on("focus", this.onFocus,  this);
44536         this.inputEl().on("blur", this.onBlur,  this);
44537         
44538         this.inputEl().relayEvent('keyup', this);
44539         
44540         if(this.indicator){
44541             this.indicator.addClass('invisible');
44542         }
44543  
44544         this.originalValue = this.getValue();
44545         
44546         if(this.validationEvent == 'keyup'){
44547             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44548             this.inputEl().on('keyup', this.filterValidation, this);
44549         }
44550         else if(this.validationEvent !== false){
44551             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44552         }
44553         
44554         if(this.selectOnFocus){
44555             this.on("focus", this.preFocus, this);
44556             
44557         }
44558         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44559             this.inputEl().on("keypress", this.filterKeys, this);
44560         } else {
44561             this.inputEl().relayEvent('keypress', this);
44562         }
44563         
44564         var allowed = "0123456789";
44565         
44566         if(this.allowDecimals){
44567             allowed += this.decimalSeparator;
44568         }
44569         
44570         if(this.allowNegative){
44571             allowed += "-";
44572         }
44573         
44574         if(this.thousandsDelimiter) {
44575             allowed += ",";
44576         }
44577         
44578         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44579         
44580         var keyPress = function(e){
44581             
44582             var k = e.getKey();
44583             
44584             var c = e.getCharCode();
44585             
44586             if(
44587                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44588                     allowed.indexOf(String.fromCharCode(c)) === -1
44589             ){
44590                 e.stopEvent();
44591                 return;
44592             }
44593             
44594             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44595                 return;
44596             }
44597             
44598             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44599                 e.stopEvent();
44600             }
44601         };
44602         
44603         this.inputEl().on("keypress", keyPress, this);
44604         
44605     },
44606     
44607     onTriggerClick : function(e)
44608     {   
44609         if(this.disabled){
44610             return;
44611         }
44612         
44613         this.page = 0;
44614         this.loadNext = false;
44615         
44616         if(this.isExpanded()){
44617             this.collapse();
44618             return;
44619         }
44620         
44621         this.hasFocus = true;
44622         
44623         if(this.triggerAction == 'all') {
44624             this.doQuery(this.allQuery, true);
44625             return;
44626         }
44627         
44628         this.doQuery(this.getRawValue());
44629     },
44630     
44631     getCurrency : function()
44632     {   
44633         var v = this.currencyEl().getValue();
44634         
44635         return v;
44636     },
44637     
44638     restrictHeight : function()
44639     {
44640         this.list.alignTo(this.currencyEl(), this.listAlign);
44641         this.list.alignTo(this.currencyEl(), this.listAlign);
44642     },
44643     
44644     onViewClick : function(view, doFocus, el, e)
44645     {
44646         var index = this.view.getSelectedIndexes()[0];
44647         
44648         var r = this.store.getAt(index);
44649         
44650         if(r){
44651             this.onSelect(r, index);
44652         }
44653     },
44654     
44655     onSelect : function(record, index){
44656         
44657         if(this.fireEvent('beforeselect', this, record, index) !== false){
44658         
44659             this.setFromCurrencyData(index > -1 ? record.data : false);
44660             
44661             this.collapse();
44662             
44663             this.fireEvent('select', this, record, index);
44664         }
44665     },
44666     
44667     setFromCurrencyData : function(o)
44668     {
44669         var currency = '';
44670         
44671         this.lastCurrency = o;
44672         
44673         if (this.currencyField) {
44674             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44675         } else {
44676             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44677         }
44678         
44679         this.lastSelectionText = currency;
44680         
44681         //setting default currency
44682         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44683             this.setCurrency(this.defaultCurrency);
44684             return;
44685         }
44686         
44687         this.setCurrency(currency);
44688     },
44689     
44690     setFromData : function(o)
44691     {
44692         var c = {};
44693         
44694         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44695         
44696         this.setFromCurrencyData(c);
44697         
44698         var value = '';
44699         
44700         if (this.name) {
44701             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44702         } else {
44703             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44704         }
44705         
44706         this.setValue(value);
44707         
44708     },
44709     
44710     setCurrency : function(v)
44711     {   
44712         this.currencyValue = v;
44713         
44714         if(this.rendered){
44715             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44716             this.validate();
44717         }
44718     },
44719     
44720     setValue : function(v)
44721     {
44722         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44723         
44724         this.value = v;
44725         
44726         if(this.rendered){
44727             
44728             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44729             
44730             this.inputEl().dom.value = (v == '') ? '' :
44731                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44732             
44733             if(!this.allowZero && v === '0') {
44734                 this.hiddenEl().dom.value = '';
44735                 this.inputEl().dom.value = '';
44736             }
44737             
44738             this.validate();
44739         }
44740     },
44741     
44742     getRawValue : function()
44743     {
44744         var v = this.inputEl().getValue();
44745         
44746         return v;
44747     },
44748     
44749     getValue : function()
44750     {
44751         return this.fixPrecision(this.parseValue(this.getRawValue()));
44752     },
44753     
44754     parseValue : function(value)
44755     {
44756         if(this.thousandsDelimiter) {
44757             value += "";
44758             r = new RegExp(",", "g");
44759             value = value.replace(r, "");
44760         }
44761         
44762         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44763         return isNaN(value) ? '' : value;
44764         
44765     },
44766     
44767     fixPrecision : function(value)
44768     {
44769         if(this.thousandsDelimiter) {
44770             value += "";
44771             r = new RegExp(",", "g");
44772             value = value.replace(r, "");
44773         }
44774         
44775         var nan = isNaN(value);
44776         
44777         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44778             return nan ? '' : value;
44779         }
44780         return parseFloat(value).toFixed(this.decimalPrecision);
44781     },
44782     
44783     decimalPrecisionFcn : function(v)
44784     {
44785         return Math.floor(v);
44786     },
44787     
44788     validateValue : function(value)
44789     {
44790         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44791             return false;
44792         }
44793         
44794         var num = this.parseValue(value);
44795         
44796         if(isNaN(num)){
44797             this.markInvalid(String.format(this.nanText, value));
44798             return false;
44799         }
44800         
44801         if(num < this.minValue){
44802             this.markInvalid(String.format(this.minText, this.minValue));
44803             return false;
44804         }
44805         
44806         if(num > this.maxValue){
44807             this.markInvalid(String.format(this.maxText, this.maxValue));
44808             return false;
44809         }
44810         
44811         return true;
44812     },
44813     
44814     validate : function()
44815     {
44816         if(this.disabled || this.allowBlank){
44817             this.markValid();
44818             return true;
44819         }
44820         
44821         var currency = this.getCurrency();
44822         
44823         if(this.validateValue(this.getRawValue()) && currency.length){
44824             this.markValid();
44825             return true;
44826         }
44827         
44828         this.markInvalid();
44829         return false;
44830     },
44831     
44832     getName: function()
44833     {
44834         return this.name;
44835     },
44836     
44837     beforeBlur : function()
44838     {
44839         if(!this.castInt){
44840             return;
44841         }
44842         
44843         var v = this.parseValue(this.getRawValue());
44844         
44845         if(v || v == 0){
44846             this.setValue(v);
44847         }
44848     },
44849     
44850     onBlur : function()
44851     {
44852         this.beforeBlur();
44853         
44854         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44855             //this.el.removeClass(this.focusClass);
44856         }
44857         
44858         this.hasFocus = false;
44859         
44860         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44861             this.validate();
44862         }
44863         
44864         var v = this.getValue();
44865         
44866         if(String(v) !== String(this.startValue)){
44867             this.fireEvent('change', this, v, this.startValue);
44868         }
44869         
44870         this.fireEvent("blur", this);
44871     },
44872     
44873     inputEl : function()
44874     {
44875         return this.el.select('.roo-money-amount-input', true).first();
44876     },
44877     
44878     currencyEl : function()
44879     {
44880         return this.el.select('.roo-money-currency-input', true).first();
44881     },
44882     
44883     hiddenEl : function()
44884     {
44885         return this.el.select('input.hidden-number-input',true).first();
44886     }
44887     
44888 });/**
44889  * @class Roo.bootstrap.BezierSignature
44890  * @extends Roo.bootstrap.Component
44891  * Bootstrap BezierSignature class
44892  * This script refer to:
44893  *    Title: Signature Pad
44894  *    Author: szimek
44895  *    Availability: https://github.com/szimek/signature_pad
44896  *
44897  * @constructor
44898  * Create a new BezierSignature
44899  * @param {Object} config The config object
44900  */
44901
44902 Roo.bootstrap.BezierSignature = function(config){
44903     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44904     this.addEvents({
44905         "resize" : true
44906     });
44907 };
44908
44909 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44910 {
44911      
44912     curve_data: [],
44913     
44914     is_empty: true,
44915     
44916     mouse_btn_down: true,
44917     
44918     /**
44919      * @cfg {int} canvas height
44920      */
44921     canvas_height: '200px',
44922     
44923     /**
44924      * @cfg {float|function} Radius of a single dot.
44925      */ 
44926     dot_size: false,
44927     
44928     /**
44929      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44930      */
44931     min_width: 0.5,
44932     
44933     /**
44934      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44935      */
44936     max_width: 2.5,
44937     
44938     /**
44939      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44940      */
44941     throttle: 16,
44942     
44943     /**
44944      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44945      */
44946     min_distance: 5,
44947     
44948     /**
44949      * @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.
44950      */
44951     bg_color: 'rgba(0, 0, 0, 0)',
44952     
44953     /**
44954      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44955      */
44956     dot_color: 'black',
44957     
44958     /**
44959      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44960      */ 
44961     velocity_filter_weight: 0.7,
44962     
44963     /**
44964      * @cfg {function} Callback when stroke begin. 
44965      */
44966     onBegin: false,
44967     
44968     /**
44969      * @cfg {function} Callback when stroke end.
44970      */
44971     onEnd: false,
44972     
44973     getAutoCreate : function()
44974     {
44975         var cls = 'roo-signature column';
44976         
44977         if(this.cls){
44978             cls += ' ' + this.cls;
44979         }
44980         
44981         var col_sizes = [
44982             'lg',
44983             'md',
44984             'sm',
44985             'xs'
44986         ];
44987         
44988         for(var i = 0; i < col_sizes.length; i++) {
44989             if(this[col_sizes[i]]) {
44990                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44991             }
44992         }
44993         
44994         var cfg = {
44995             tag: 'div',
44996             cls: cls,
44997             cn: [
44998                 {
44999                     tag: 'div',
45000                     cls: 'roo-signature-body',
45001                     cn: [
45002                         {
45003                             tag: 'canvas',
45004                             cls: 'roo-signature-body-canvas',
45005                             height: this.canvas_height,
45006                             width: this.canvas_width
45007                         }
45008                     ]
45009                 },
45010                 {
45011                     tag: 'input',
45012                     type: 'file',
45013                     style: 'display: none'
45014                 }
45015             ]
45016         };
45017         
45018         return cfg;
45019     },
45020     
45021     initEvents: function() 
45022     {
45023         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
45024         
45025         var canvas = this.canvasEl();
45026         
45027         // mouse && touch event swapping...
45028         canvas.dom.style.touchAction = 'none';
45029         canvas.dom.style.msTouchAction = 'none';
45030         
45031         this.mouse_btn_down = false;
45032         canvas.on('mousedown', this._handleMouseDown, this);
45033         canvas.on('mousemove', this._handleMouseMove, this);
45034         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
45035         
45036         if (window.PointerEvent) {
45037             canvas.on('pointerdown', this._handleMouseDown, this);
45038             canvas.on('pointermove', this._handleMouseMove, this);
45039             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
45040         }
45041         
45042         if ('ontouchstart' in window) {
45043             canvas.on('touchstart', this._handleTouchStart, this);
45044             canvas.on('touchmove', this._handleTouchMove, this);
45045             canvas.on('touchend', this._handleTouchEnd, this);
45046         }
45047         
45048         Roo.EventManager.onWindowResize(this.resize, this, true);
45049         
45050         // file input event
45051         this.fileEl().on('change', this.uploadImage, this);
45052         
45053         this.clear();
45054         
45055         this.resize();
45056     },
45057     
45058     resize: function(){
45059         
45060         var canvas = this.canvasEl().dom;
45061         var ctx = this.canvasElCtx();
45062         var img_data = false;
45063         
45064         if(canvas.width > 0) {
45065             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
45066         }
45067         // setting canvas width will clean img data
45068         canvas.width = 0;
45069         
45070         var style = window.getComputedStyle ? 
45071             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
45072             
45073         var padding_left = parseInt(style.paddingLeft) || 0;
45074         var padding_right = parseInt(style.paddingRight) || 0;
45075         
45076         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
45077         
45078         if(img_data) {
45079             ctx.putImageData(img_data, 0, 0);
45080         }
45081     },
45082     
45083     _handleMouseDown: function(e)
45084     {
45085         if (e.browserEvent.which === 1) {
45086             this.mouse_btn_down = true;
45087             this.strokeBegin(e);
45088         }
45089     },
45090     
45091     _handleMouseMove: function (e)
45092     {
45093         if (this.mouse_btn_down) {
45094             this.strokeMoveUpdate(e);
45095         }
45096     },
45097     
45098     _handleMouseUp: function (e)
45099     {
45100         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
45101             this.mouse_btn_down = false;
45102             this.strokeEnd(e);
45103         }
45104     },
45105     
45106     _handleTouchStart: function (e) {
45107         
45108         e.preventDefault();
45109         if (e.browserEvent.targetTouches.length === 1) {
45110             // var touch = e.browserEvent.changedTouches[0];
45111             // this.strokeBegin(touch);
45112             
45113              this.strokeBegin(e); // assume e catching the correct xy...
45114         }
45115     },
45116     
45117     _handleTouchMove: function (e) {
45118         e.preventDefault();
45119         // var touch = event.targetTouches[0];
45120         // _this._strokeMoveUpdate(touch);
45121         this.strokeMoveUpdate(e);
45122     },
45123     
45124     _handleTouchEnd: function (e) {
45125         var wasCanvasTouched = e.target === this.canvasEl().dom;
45126         if (wasCanvasTouched) {
45127             e.preventDefault();
45128             // var touch = event.changedTouches[0];
45129             // _this._strokeEnd(touch);
45130             this.strokeEnd(e);
45131         }
45132     },
45133     
45134     reset: function () {
45135         this._lastPoints = [];
45136         this._lastVelocity = 0;
45137         this._lastWidth = (this.min_width + this.max_width) / 2;
45138         this.canvasElCtx().fillStyle = this.dot_color;
45139     },
45140     
45141     strokeMoveUpdate: function(e)
45142     {
45143         this.strokeUpdate(e);
45144         
45145         if (this.throttle) {
45146             this.throttleStroke(this.strokeUpdate, this.throttle);
45147         }
45148         else {
45149             this.strokeUpdate(e);
45150         }
45151     },
45152     
45153     strokeBegin: function(e)
45154     {
45155         var newPointGroup = {
45156             color: this.dot_color,
45157             points: []
45158         };
45159         
45160         if (typeof this.onBegin === 'function') {
45161             this.onBegin(e);
45162         }
45163         
45164         this.curve_data.push(newPointGroup);
45165         this.reset();
45166         this.strokeUpdate(e);
45167     },
45168     
45169     strokeUpdate: function(e)
45170     {
45171         var rect = this.canvasEl().dom.getBoundingClientRect();
45172         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
45173         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
45174         var lastPoints = lastPointGroup.points;
45175         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
45176         var isLastPointTooClose = lastPoint
45177             ? point.distanceTo(lastPoint) <= this.min_distance
45178             : false;
45179         var color = lastPointGroup.color;
45180         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
45181             var curve = this.addPoint(point);
45182             if (!lastPoint) {
45183                 this.drawDot({color: color, point: point});
45184             }
45185             else if (curve) {
45186                 this.drawCurve({color: color, curve: curve});
45187             }
45188             lastPoints.push({
45189                 time: point.time,
45190                 x: point.x,
45191                 y: point.y
45192             });
45193         }
45194     },
45195     
45196     strokeEnd: function(e)
45197     {
45198         this.strokeUpdate(e);
45199         if (typeof this.onEnd === 'function') {
45200             this.onEnd(e);
45201         }
45202     },
45203     
45204     addPoint:  function (point) {
45205         var _lastPoints = this._lastPoints;
45206         _lastPoints.push(point);
45207         if (_lastPoints.length > 2) {
45208             if (_lastPoints.length === 3) {
45209                 _lastPoints.unshift(_lastPoints[0]);
45210             }
45211             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
45212             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
45213             _lastPoints.shift();
45214             return curve;
45215         }
45216         return null;
45217     },
45218     
45219     calculateCurveWidths: function (startPoint, endPoint) {
45220         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
45221             (1 - this.velocity_filter_weight) * this._lastVelocity;
45222
45223         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
45224         var widths = {
45225             end: newWidth,
45226             start: this._lastWidth
45227         };
45228         
45229         this._lastVelocity = velocity;
45230         this._lastWidth = newWidth;
45231         return widths;
45232     },
45233     
45234     drawDot: function (_a) {
45235         var color = _a.color, point = _a.point;
45236         var ctx = this.canvasElCtx();
45237         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
45238         ctx.beginPath();
45239         this.drawCurveSegment(point.x, point.y, width);
45240         ctx.closePath();
45241         ctx.fillStyle = color;
45242         ctx.fill();
45243     },
45244     
45245     drawCurve: function (_a) {
45246         var color = _a.color, curve = _a.curve;
45247         var ctx = this.canvasElCtx();
45248         var widthDelta = curve.endWidth - curve.startWidth;
45249         var drawSteps = Math.floor(curve.length()) * 2;
45250         ctx.beginPath();
45251         ctx.fillStyle = color;
45252         for (var i = 0; i < drawSteps; i += 1) {
45253         var t = i / drawSteps;
45254         var tt = t * t;
45255         var ttt = tt * t;
45256         var u = 1 - t;
45257         var uu = u * u;
45258         var uuu = uu * u;
45259         var x = uuu * curve.startPoint.x;
45260         x += 3 * uu * t * curve.control1.x;
45261         x += 3 * u * tt * curve.control2.x;
45262         x += ttt * curve.endPoint.x;
45263         var y = uuu * curve.startPoint.y;
45264         y += 3 * uu * t * curve.control1.y;
45265         y += 3 * u * tt * curve.control2.y;
45266         y += ttt * curve.endPoint.y;
45267         var width = curve.startWidth + ttt * widthDelta;
45268         this.drawCurveSegment(x, y, width);
45269         }
45270         ctx.closePath();
45271         ctx.fill();
45272     },
45273     
45274     drawCurveSegment: function (x, y, width) {
45275         var ctx = this.canvasElCtx();
45276         ctx.moveTo(x, y);
45277         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
45278         this.is_empty = false;
45279     },
45280     
45281     clear: function()
45282     {
45283         var ctx = this.canvasElCtx();
45284         var canvas = this.canvasEl().dom;
45285         ctx.fillStyle = this.bg_color;
45286         ctx.clearRect(0, 0, canvas.width, canvas.height);
45287         ctx.fillRect(0, 0, canvas.width, canvas.height);
45288         this.curve_data = [];
45289         this.reset();
45290         this.is_empty = true;
45291     },
45292     
45293     fileEl: function()
45294     {
45295         return  this.el.select('input',true).first();
45296     },
45297     
45298     canvasEl: function()
45299     {
45300         return this.el.select('canvas',true).first();
45301     },
45302     
45303     canvasElCtx: function()
45304     {
45305         return this.el.select('canvas',true).first().dom.getContext('2d');
45306     },
45307     
45308     getImage: function(type)
45309     {
45310         if(this.is_empty) {
45311             return false;
45312         }
45313         
45314         // encryption ?
45315         return this.canvasEl().dom.toDataURL('image/'+type, 1);
45316     },
45317     
45318     drawFromImage: function(img_src)
45319     {
45320         var img = new Image();
45321         
45322         img.onload = function(){
45323             this.canvasElCtx().drawImage(img, 0, 0);
45324         }.bind(this);
45325         
45326         img.src = img_src;
45327         
45328         this.is_empty = false;
45329     },
45330     
45331     selectImage: function()
45332     {
45333         this.fileEl().dom.click();
45334     },
45335     
45336     uploadImage: function(e)
45337     {
45338         var reader = new FileReader();
45339         
45340         reader.onload = function(e){
45341             var img = new Image();
45342             img.onload = function(){
45343                 this.reset();
45344                 this.canvasElCtx().drawImage(img, 0, 0);
45345             }.bind(this);
45346             img.src = e.target.result;
45347         }.bind(this);
45348         
45349         reader.readAsDataURL(e.target.files[0]);
45350     },
45351     
45352     // Bezier Point Constructor
45353     Point: (function () {
45354         function Point(x, y, time) {
45355             this.x = x;
45356             this.y = y;
45357             this.time = time || Date.now();
45358         }
45359         Point.prototype.distanceTo = function (start) {
45360             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
45361         };
45362         Point.prototype.equals = function (other) {
45363             return this.x === other.x && this.y === other.y && this.time === other.time;
45364         };
45365         Point.prototype.velocityFrom = function (start) {
45366             return this.time !== start.time
45367             ? this.distanceTo(start) / (this.time - start.time)
45368             : 0;
45369         };
45370         return Point;
45371     }()),
45372     
45373     
45374     // Bezier Constructor
45375     Bezier: (function () {
45376         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
45377             this.startPoint = startPoint;
45378             this.control2 = control2;
45379             this.control1 = control1;
45380             this.endPoint = endPoint;
45381             this.startWidth = startWidth;
45382             this.endWidth = endWidth;
45383         }
45384         Bezier.fromPoints = function (points, widths, scope) {
45385             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
45386             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
45387             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
45388         };
45389         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
45390             var dx1 = s1.x - s2.x;
45391             var dy1 = s1.y - s2.y;
45392             var dx2 = s2.x - s3.x;
45393             var dy2 = s2.y - s3.y;
45394             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
45395             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
45396             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
45397             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
45398             var dxm = m1.x - m2.x;
45399             var dym = m1.y - m2.y;
45400             var k = l2 / (l1 + l2);
45401             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
45402             var tx = s2.x - cm.x;
45403             var ty = s2.y - cm.y;
45404             return {
45405                 c1: new scope.Point(m1.x + tx, m1.y + ty),
45406                 c2: new scope.Point(m2.x + tx, m2.y + ty)
45407             };
45408         };
45409         Bezier.prototype.length = function () {
45410             var steps = 10;
45411             var length = 0;
45412             var px;
45413             var py;
45414             for (var i = 0; i <= steps; i += 1) {
45415                 var t = i / steps;
45416                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
45417                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
45418                 if (i > 0) {
45419                     var xdiff = cx - px;
45420                     var ydiff = cy - py;
45421                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
45422                 }
45423                 px = cx;
45424                 py = cy;
45425             }
45426             return length;
45427         };
45428         Bezier.prototype.point = function (t, start, c1, c2, end) {
45429             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
45430             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
45431             + (3.0 * c2 * (1.0 - t) * t * t)
45432             + (end * t * t * t);
45433         };
45434         return Bezier;
45435     }()),
45436     
45437     throttleStroke: function(fn, wait) {
45438       if (wait === void 0) { wait = 250; }
45439       var previous = 0;
45440       var timeout = null;
45441       var result;
45442       var storedContext;
45443       var storedArgs;
45444       var later = function () {
45445           previous = Date.now();
45446           timeout = null;
45447           result = fn.apply(storedContext, storedArgs);
45448           if (!timeout) {
45449               storedContext = null;
45450               storedArgs = [];
45451           }
45452       };
45453       return function wrapper() {
45454           var args = [];
45455           for (var _i = 0; _i < arguments.length; _i++) {
45456               args[_i] = arguments[_i];
45457           }
45458           var now = Date.now();
45459           var remaining = wait - (now - previous);
45460           storedContext = this;
45461           storedArgs = args;
45462           if (remaining <= 0 || remaining > wait) {
45463               if (timeout) {
45464                   clearTimeout(timeout);
45465                   timeout = null;
45466               }
45467               previous = now;
45468               result = fn.apply(storedContext, storedArgs);
45469               if (!timeout) {
45470                   storedContext = null;
45471                   storedArgs = [];
45472               }
45473           }
45474           else if (!timeout) {
45475               timeout = window.setTimeout(later, remaining);
45476           }
45477           return result;
45478       };
45479   }
45480   
45481 });
45482
45483  
45484
45485