roojs-ui.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<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>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined' ? this.cls : cfg.cls) + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = (typeof(cfg.style) == 'undefined' ? this.style : cfg.style) + '; ' + this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     getDocumentBody : function() // used by menus - as they are attached to the body so zIndexes work
366     {
367         return Roo.get(document.body);
368     },
369     
370     /**
371      * Fetch the element to display the tooltip on.
372      * @return {Roo.Element} defaults to this.el
373      */
374     tooltipEl : function()
375     {
376         return this.el;
377     },
378         
379     addxtype  : function(tree,cntr)
380     {
381         var cn = this;
382         
383         cn = Roo.factory(tree);
384         //Roo.log(['addxtype', cn]);
385            
386         cn.parentType = this.xtype; //??
387         cn.parentId = this.id;
388         
389         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
390         if (typeof(cn.container_method) == 'string') {
391             cntr = cn.container_method;
392         }
393         
394         
395         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
396         
397         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
398         
399         var build_from_html =  Roo.XComponent.build_from_html;
400           
401         var is_body  = (tree.xtype == 'Body') ;
402           
403         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
404           
405         var self_cntr_el = Roo.get(this[cntr](false));
406         
407         // do not try and build conditional elements 
408         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
409             return false;
410         }
411         
412         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
413             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
414                 return this.addxtypeChild(tree,cntr, is_body);
415             }
416             
417             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
418                 
419             if(echild){
420                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
421             }
422             
423             Roo.log('skipping render');
424             return cn;
425             
426         }
427         
428         var ret = false;
429         if (!build_from_html) {
430             return false;
431         }
432         
433         // this i think handles overlaying multiple children of the same type
434         // with the sam eelement.. - which might be buggy..
435         while (true) {
436             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
437             
438             if (!echild) {
439                 break;
440             }
441             
442             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
443                 break;
444             }
445             
446             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
447         }
448        
449         return ret;
450     },
451     
452     
453     addxtypeChild : function (tree, cntr, is_body)
454     {
455         Roo.debug && Roo.log('addxtypeChild:' + cntr);
456         var cn = this;
457         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
458         
459         
460         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
461                     (typeof(tree['flexy:foreach']) != 'undefined');
462           
463     
464         
465         skip_children = false;
466         // render the element if it's not BODY.
467         if (!is_body) {
468             
469             // if parent was disabled, then do not try and create the children..
470             if(!this[cntr](true)){
471                 tree.items = [];
472                 return tree;
473             }
474            
475             cn = Roo.factory(tree);
476            
477             cn.parentType = this.xtype; //??
478             cn.parentId = this.id;
479             
480             var build_from_html =  Roo.XComponent.build_from_html;
481             
482             
483             // does the container contain child eleemnts with 'xtype' attributes.
484             // that match this xtype..
485             // note - when we render we create these as well..
486             // so we should check to see if body has xtype set.
487             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
488                
489                 var self_cntr_el = Roo.get(this[cntr](false));
490                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
491                 if (echild) { 
492                     //Roo.log(Roo.XComponent.build_from_html);
493                     //Roo.log("got echild:");
494                     //Roo.log(echild);
495                 }
496                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
497                 // and are not displayed -this causes this to use up the wrong element when matching.
498                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
499                 
500                 
501                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
502                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
503                   
504                   
505                   
506                     cn.el = echild;
507                   //  Roo.log("GOT");
508                     //echild.dom.removeAttribute('xtype');
509                 } else {
510                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
511                     Roo.debug && Roo.log(self_cntr_el);
512                     Roo.debug && Roo.log(echild);
513                     Roo.debug && Roo.log(cn);
514                 }
515             }
516            
517             
518            
519             // if object has flexy:if - then it may or may not be rendered.
520             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
521                 // skip a flexy if element.
522                 Roo.debug && Roo.log('skipping render');
523                 Roo.debug && Roo.log(tree);
524                 if (!cn.el) {
525                     Roo.debug && Roo.log('skipping all children');
526                     skip_children = true;
527                 }
528                 
529              } else {
530                  
531                 // actually if flexy:foreach is found, we really want to create 
532                 // multiple copies here...
533                 //Roo.log('render');
534                 //Roo.log(this[cntr]());
535                 // some elements do not have render methods.. like the layouts...
536                 /*
537                 if(this[cntr](true) === false){
538                     cn.items = [];
539                     return cn;
540                 }
541                 */
542                 cn.render && cn.render(this[cntr](true));
543                 
544              }
545             // then add the element..
546         }
547          
548         // handle the kids..
549         
550         var nitems = [];
551         /*
552         if (typeof (tree.menu) != 'undefined') {
553             tree.menu.parentType = cn.xtype;
554             tree.menu.triggerEl = cn.el;
555             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
556             
557         }
558         */
559         if (!tree.items || !tree.items.length) {
560             cn.items = nitems;
561             //Roo.log(["no children", this]);
562             
563             return cn;
564         }
565          
566         var items = tree.items;
567         delete tree.items;
568         
569         //Roo.log(items.length);
570             // add the items..
571         if (!skip_children) {    
572             for(var i =0;i < items.length;i++) {
573               //  Roo.log(['add child', items[i]]);
574                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
575             }
576         }
577         
578         cn.items = nitems;
579         
580         //Roo.log("fire childrenrendered");
581         
582         cn.fireEvent('childrenrendered', this);
583         
584         return cn;
585     },
586     
587     /**
588      * Set the element that will be used to show or hide
589      */
590     setVisibilityEl : function(el)
591     {
592         this.visibilityEl = el;
593     },
594     
595      /**
596      * Get the element that will be used to show or hide
597      */
598     getVisibilityEl : function()
599     {
600         if (typeof(this.visibilityEl) == 'object') {
601             return this.visibilityEl;
602         }
603         
604         if (typeof(this.visibilityEl) == 'string') {
605             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
606         }
607         
608         return this.getEl();
609     },
610     
611     /**
612      * Show a component - removes 'hidden' class
613      */
614     show : function()
615     {
616         if(!this.getVisibilityEl()){
617             return;
618         }
619          
620         this.getVisibilityEl().removeClass(['hidden','d-none']);
621         
622         this.fireEvent('show', this);
623         
624         
625     },
626     /**
627      * Hide a component - adds 'hidden' class
628      */
629     hide: function()
630     {
631         if(!this.getVisibilityEl()){
632             return;
633         }
634         
635         this.getVisibilityEl().addClass(['hidden','d-none']);
636         
637         this.fireEvent('hide', this);
638         
639     }
640 });
641
642  /*
643  * - LGPL
644  *
645  * element
646  * 
647  */
648
649 /**
650  * @class Roo.bootstrap.Element
651  * @extends Roo.bootstrap.Component
652  * Bootstrap Element class
653  * @cfg {String} html contents of the element
654  * @cfg {String} tag tag of the element
655  * @cfg {String} cls class of the element
656  * @cfg {Boolean} preventDefault (true|false) default false
657  * @cfg {Boolean} clickable (true|false) default false
658  * @cfg {String} role default blank - set to button to force cursor pointer
659  
660  * 
661  * @constructor
662  * Create a new Element
663  * @param {Object} config The config object
664  */
665
666 Roo.bootstrap.Element = function(config){
667     Roo.bootstrap.Element.superclass.constructor.call(this, config);
668     
669     this.addEvents({
670         // raw events
671         /**
672          * @event click
673          * When a element is chick
674          * @param {Roo.bootstrap.Element} this
675          * @param {Roo.EventObject} e
676          */
677         "click" : true 
678         
679       
680     });
681 };
682
683 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
684     
685     tag: 'div',
686     cls: '',
687     html: '',
688     preventDefault: false, 
689     clickable: false,
690     tapedTwice : false,
691     role : false,
692     
693     getAutoCreate : function(){
694         
695         var cfg = {
696             tag: this.tag,
697             // cls: this.cls, double assign in parent class Component.js :: onRender
698             html: this.html
699         };
700         if (this.role !== false) {
701             cfg.role = this.role;
702         }
703         
704         return cfg;
705     },
706     
707     initEvents: function() 
708     {
709         Roo.bootstrap.Element.superclass.initEvents.call(this);
710         
711         if(this.clickable){
712             this.el.on('click', this.onClick, this);
713         }
714         
715         
716     },
717     
718     onClick : function(e)
719     {
720         if(this.preventDefault){
721             e.preventDefault();
722         }
723         
724         this.fireEvent('click', this, e); // why was this double click before?
725     },
726     
727     
728     
729
730     
731     
732     getValue : function()
733     {
734         return this.el.dom.innerHTML;
735     },
736     
737     setValue : function(value)
738     {
739         this.el.dom.innerHTML = value;
740     }
741    
742 });
743
744  
745
746  /*
747  * - LGPL
748  *
749  * dropable area
750  * 
751  */
752
753 /**
754  * @class Roo.bootstrap.DropTarget
755  * @extends Roo.bootstrap.Element
756  * Bootstrap DropTarget class
757  
758  * @cfg {string} name dropable name
759  * 
760  * @constructor
761  * Create a new Dropable Area
762  * @param {Object} config The config object
763  */
764
765 Roo.bootstrap.DropTarget = function(config){
766     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
767     
768     this.addEvents({
769         // raw events
770         /**
771          * @event click
772          * When a element is chick
773          * @param {Roo.bootstrap.Element} this
774          * @param {Roo.EventObject} e
775          */
776         "drop" : true
777     });
778 };
779
780 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
781     
782     
783     getAutoCreate : function(){
784         
785          
786     },
787     
788     initEvents: function() 
789     {
790         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
791         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
792             ddGroup: this.name,
793             listeners : {
794                 drop : this.dragDrop.createDelegate(this),
795                 enter : this.dragEnter.createDelegate(this),
796                 out : this.dragOut.createDelegate(this),
797                 over : this.dragOver.createDelegate(this)
798             }
799             
800         });
801         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
802     },
803     
804     dragDrop : function(source,e,data)
805     {
806         // user has to decide how to impliment this.
807         Roo.log('drop');
808         Roo.log(this);
809         //this.fireEvent('drop', this, source, e ,data);
810         return false;
811     },
812     
813     dragEnter : function(n, dd, e, data)
814     {
815         // probably want to resize the element to match the dropped element..
816         Roo.log("enter");
817         this.originalSize = this.el.getSize();
818         this.el.setSize( n.el.getSize());
819         this.dropZone.DDM.refreshCache(this.name);
820         Roo.log([n, dd, e, data]);
821     },
822     
823     dragOut : function(value)
824     {
825         // resize back to normal
826         Roo.log("out");
827         this.el.setSize(this.originalSize);
828         this.dropZone.resetConstraints();
829     },
830     
831     dragOver : function()
832     {
833         // ??? do nothing?
834     }
835    
836 });
837
838  
839
840  /*
841  * - LGPL
842  *
843  * Body
844  *
845  */
846
847 /**
848  * @class Roo.bootstrap.Body
849  * @extends Roo.bootstrap.Component
850  * Bootstrap Body class
851  *
852  * @constructor
853  * Create a new body
854  * @param {Object} config The config object
855  */
856
857 Roo.bootstrap.Body = function(config){
858
859     config = config || {};
860
861     Roo.bootstrap.Body.superclass.constructor.call(this, config);
862     this.el = Roo.get(config.el ? config.el : document.body );
863     if (this.cls && this.cls.length) {
864         Roo.get(document.body).addClass(this.cls);
865     }
866 };
867
868 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
869
870     is_body : true,// just to make sure it's constructed?
871
872         autoCreate : {
873         cls: 'container'
874     },
875     onRender : function(ct, position)
876     {
877        /* Roo.log("Roo.bootstrap.Body - onRender");
878         if (this.cls && this.cls.length) {
879             Roo.get(document.body).addClass(this.cls);
880         }
881         // style??? xttr???
882         */
883     }
884
885
886
887
888 });
889 /*
890  * - LGPL
891  *
892  * button group
893  * 
894  */
895
896
897 /**
898  * @class Roo.bootstrap.ButtonGroup
899  * @extends Roo.bootstrap.Component
900  * Bootstrap ButtonGroup class
901  * @cfg {String} size lg | sm | xs (default empty normal)
902  * @cfg {String} align vertical | justified  (default none)
903  * @cfg {String} direction up | down (default down)
904  * @cfg {Boolean} toolbar false | true
905  * @cfg {Boolean} btn true | false
906  * 
907  * 
908  * @constructor
909  * Create a new Input
910  * @param {Object} config The config object
911  */
912
913 Roo.bootstrap.ButtonGroup = function(config){
914     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
915 };
916
917 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
918     
919     size: '',
920     align: '',
921     direction: '',
922     toolbar: false,
923     btn: true,
924
925     getAutoCreate : function(){
926         var cfg = {
927             cls: 'btn-group',
928             html : null
929         };
930         
931         cfg.html = this.html || cfg.html;
932         
933         if (this.toolbar) {
934             cfg = {
935                 cls: 'btn-toolbar',
936                 html: null
937             };
938             
939             return cfg;
940         }
941         
942         if (['vertical','justified'].indexOf(this.align)!==-1) {
943             cfg.cls = 'btn-group-' + this.align;
944             
945             if (this.align == 'justified') {
946                 console.log(this.items);
947             }
948         }
949         
950         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
951             cfg.cls += ' btn-group-' + this.size;
952         }
953         
954         if (this.direction == 'up') {
955             cfg.cls += ' dropup' ;
956         }
957         
958         return cfg;
959     },
960     /**
961      * Add a button to the group (similar to NavItem API.)
962      */
963     addItem : function(cfg)
964     {
965         var cn = new Roo.bootstrap.Button(cfg);
966         //this.register(cn);
967         cn.parentId = this.id;
968         cn.onRender(this.el, null);
969         return cn;
970     }
971    
972 });
973
974  /*
975  * - LGPL
976  *
977  * button
978  * 
979  */
980
981 /**
982  * @class Roo.bootstrap.Button
983  * @extends Roo.bootstrap.Component
984  * Bootstrap Button class
985  * @cfg {String} html The button content
986  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
987  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
988  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
989  * @cfg {String} size (lg|sm|xs)
990  * @cfg {String} tag (a|input|submit)
991  * @cfg {String} href empty or href
992  * @cfg {Boolean} disabled default false;
993  * @cfg {Boolean} isClose default false;
994  * @cfg {String} glyphicon depricated - use fa
995  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
996  * @cfg {String} badge text for badge
997  * @cfg {String} theme (default|glow)  
998  * @cfg {Boolean} inverse dark themed version
999  * @cfg {Boolean} toggle is it a slidy toggle button
1000  * @cfg {Boolean} pressed   default null - if the button ahs active state
1001  * @cfg {String} ontext text for on slidy toggle state
1002  * @cfg {String} offtext text for off slidy toggle state
1003  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
1004  * @cfg {Boolean} removeClass remove the standard class..
1005  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
1006  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
1007  * 
1008  * @constructor
1009  * Create a new button
1010  * @param {Object} config The config object
1011  */
1012
1013
1014 Roo.bootstrap.Button = function(config){
1015     Roo.bootstrap.Button.superclass.constructor.call(this, config);
1016     
1017     this.addEvents({
1018         // raw events
1019         /**
1020          * @event click
1021          * When a button is pressed
1022          * @param {Roo.bootstrap.Button} btn
1023          * @param {Roo.EventObject} e
1024          */
1025         "click" : true,
1026         /**
1027          * @event dblclick
1028          * When a button is double clicked
1029          * @param {Roo.bootstrap.Button} btn
1030          * @param {Roo.EventObject} e
1031          */
1032         "dblclick" : true,
1033          /**
1034          * @event toggle
1035          * After the button has been toggles
1036          * @param {Roo.bootstrap.Button} btn
1037          * @param {Roo.EventObject} e
1038          * @param {boolean} pressed (also available as button.pressed)
1039          */
1040         "toggle" : true
1041     });
1042 };
1043
1044 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1045     html: false,
1046     active: false,
1047     weight: '',
1048     badge_weight: '',
1049     outline : false,
1050     size: '',
1051     tag: 'button',
1052     href: '',
1053     disabled: false,
1054     isClose: false,
1055     glyphicon: '',
1056     fa: '',
1057     badge: '',
1058     theme: 'default',
1059     inverse: false,
1060     
1061     toggle: false,
1062     ontext: 'ON',
1063     offtext: 'OFF',
1064     defaulton: true,
1065     preventDefault: true,
1066     removeClass: false,
1067     name: false,
1068     target: false,
1069     group : false,
1070      
1071     pressed : null,
1072      
1073     
1074     getAutoCreate : function(){
1075         
1076         var cfg = {
1077             tag : 'button',
1078             cls : 'roo-button',
1079             html: ''
1080         };
1081         
1082         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1083             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1084             this.tag = 'button';
1085         } else {
1086             cfg.tag = this.tag;
1087         }
1088         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1089         
1090         if (this.toggle == true) {
1091             cfg={
1092                 tag: 'div',
1093                 cls: 'slider-frame roo-button',
1094                 cn: [
1095                     {
1096                         tag: 'span',
1097                         'data-on-text':'ON',
1098                         'data-off-text':'OFF',
1099                         cls: 'slider-button',
1100                         html: this.offtext
1101                     }
1102                 ]
1103             };
1104             // why are we validating the weights?
1105             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1106                 cfg.cls +=  ' ' + this.weight;
1107             }
1108             
1109             return cfg;
1110         }
1111         
1112         if (this.isClose) {
1113             cfg.cls += ' close';
1114             
1115             cfg["aria-hidden"] = true;
1116             
1117             cfg.html = "&times;";
1118             
1119             return cfg;
1120         }
1121              
1122         
1123         if (this.theme==='default') {
1124             cfg.cls = 'btn roo-button';
1125             
1126             //if (this.parentType != 'Navbar') {
1127             this.weight = this.weight.length ?  this.weight : 'default';
1128             //}
1129             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1130                 
1131                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1132                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1133                 cfg.cls += ' btn-' + outline + weight;
1134                 if (this.weight == 'default') {
1135                     // BC
1136                     cfg.cls += ' btn-' + this.weight;
1137                 }
1138             }
1139         } else if (this.theme==='glow') {
1140             
1141             cfg.tag = 'a';
1142             cfg.cls = 'btn-glow roo-button';
1143             
1144             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1145                 
1146                 cfg.cls += ' ' + this.weight;
1147             }
1148         }
1149    
1150         
1151         if (this.inverse) {
1152             this.cls += ' inverse';
1153         }
1154         
1155         
1156         if (this.active || this.pressed === true) {
1157             cfg.cls += ' active';
1158         }
1159         
1160         if (this.disabled) {
1161             cfg.disabled = 'disabled';
1162         }
1163         
1164         if (this.items) {
1165             Roo.log('changing to ul' );
1166             cfg.tag = 'ul';
1167             this.glyphicon = 'caret';
1168             if (Roo.bootstrap.version == 4) {
1169                 this.fa = 'caret-down';
1170             }
1171             
1172         }
1173         
1174         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1175          
1176         //gsRoo.log(this.parentType);
1177         if (this.parentType === 'Navbar' && !this.parent().bar) {
1178             Roo.log('changing to li?');
1179             
1180             cfg.tag = 'li';
1181             
1182             cfg.cls = '';
1183             cfg.cn =  [{
1184                 tag : 'a',
1185                 cls : 'roo-button',
1186                 html : this.html,
1187                 href : this.href || '#'
1188             }];
1189             if (this.menu) {
1190                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1191                 cfg.cls += ' dropdown';
1192             }   
1193             
1194             delete cfg.html;
1195             
1196         }
1197         
1198        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1199         
1200         if (this.glyphicon) {
1201             cfg.html = ' ' + cfg.html;
1202             
1203             cfg.cn = [
1204                 {
1205                     tag: 'span',
1206                     cls: 'glyphicon glyphicon-' + this.glyphicon
1207                 }
1208             ];
1209         }
1210         if (this.fa) {
1211             cfg.html = ' ' + cfg.html;
1212             
1213             cfg.cn = [
1214                 {
1215                     tag: 'i',
1216                     cls: 'fa fas fa-' + this.fa
1217                 }
1218             ];
1219         }
1220         
1221         if (this.badge) {
1222             cfg.html += ' ';
1223             
1224             cfg.tag = 'a';
1225             
1226 //            cfg.cls='btn roo-button';
1227             
1228             cfg.href=this.href;
1229             
1230             var value = cfg.html;
1231             
1232             if(this.glyphicon){
1233                 value = {
1234                     tag: 'span',
1235                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1236                     html: this.html
1237                 };
1238             }
1239             if(this.fa){
1240                 value = {
1241                     tag: 'i',
1242                     cls: 'fa fas fa-' + this.fa,
1243                     html: this.html
1244                 };
1245             }
1246             
1247             var bw = this.badge_weight.length ? this.badge_weight :
1248                 (this.weight.length ? this.weight : 'secondary');
1249             bw = bw == 'default' ? 'secondary' : bw;
1250             
1251             cfg.cn = [
1252                 value,
1253                 {
1254                     tag: 'span',
1255                     cls: 'badge badge-' + bw,
1256                     html: this.badge
1257                 }
1258             ];
1259             
1260             cfg.html='';
1261         }
1262         
1263         if (this.menu) {
1264             cfg.cls += ' dropdown';
1265             cfg.html = typeof(cfg.html) != 'undefined' ?
1266                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1267         }
1268         
1269         if (cfg.tag !== 'a' && this.href !== '') {
1270             throw "Tag must be a to set href.";
1271         } else if (this.href.length > 0) {
1272             cfg.href = this.href;
1273         }
1274         
1275         if(this.removeClass){
1276             cfg.cls = '';
1277         }
1278         
1279         if(this.target){
1280             cfg.target = this.target;
1281         }
1282         
1283         return cfg;
1284     },
1285     initEvents: function() {
1286        // Roo.log('init events?');
1287 //        Roo.log(this.el.dom);
1288         // add the menu...
1289         
1290         if (typeof (this.menu) != 'undefined') {
1291             this.menu.parentType = this.xtype;
1292             this.menu.triggerEl = this.el;
1293             this.addxtype(Roo.apply({}, this.menu));
1294         }
1295
1296
1297         if (this.el.hasClass('roo-button')) {
1298              this.el.on('click', this.onClick, this);
1299              this.el.on('dblclick', this.onDblClick, this);
1300         } else {
1301              this.el.select('.roo-button').on('click', this.onClick, this);
1302              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1303              
1304         }
1305         // why?
1306         if(this.removeClass){
1307             this.el.on('click', this.onClick, this);
1308         }
1309         
1310         if (this.group === true) {
1311              if (this.pressed === false || this.pressed === true) {
1312                 // nothing
1313             } else {
1314                 this.pressed = false;
1315                 this.setActive(this.pressed);
1316             }
1317             
1318         }
1319         
1320         this.el.enableDisplayMode();
1321         
1322     },
1323     onClick : function(e)
1324     {
1325         if (this.disabled) {
1326             return;
1327         }
1328         
1329         Roo.log('button on click ');
1330         if(this.preventDefault){
1331             e.preventDefault();
1332         }
1333         
1334         if (this.group) {
1335             if (this.pressed) {
1336                 // do nothing -
1337                 return;
1338             }
1339             this.setActive(true);
1340             var pi = this.parent().items;
1341             for (var i = 0;i < pi.length;i++) {
1342                 if (this == pi[i]) {
1343                     continue;
1344                 }
1345                 if (pi[i].el.hasClass('roo-button')) {
1346                     pi[i].setActive(false);
1347                 }
1348             }
1349             this.fireEvent('click', this, e);            
1350             return;
1351         }
1352         
1353         if (this.pressed === true || this.pressed === false) {
1354             this.toggleActive(e);
1355         }
1356         
1357         
1358         this.fireEvent('click', this, e);
1359     },
1360     onDblClick: function(e)
1361     {
1362         if (this.disabled) {
1363             return;
1364         }
1365         if(this.preventDefault){
1366             e.preventDefault();
1367         }
1368         this.fireEvent('dblclick', this, e);
1369     },
1370     /**
1371      * Enables this button
1372      */
1373     enable : function()
1374     {
1375         this.disabled = false;
1376         this.el.removeClass('disabled');
1377         this.el.dom.removeAttribute("disabled");
1378     },
1379     
1380     /**
1381      * Disable this button
1382      */
1383     disable : function()
1384     {
1385         this.disabled = true;
1386         this.el.addClass('disabled');
1387         this.el.attr("disabled", "disabled")
1388     },
1389      /**
1390      * sets the active state on/off, 
1391      * @param {Boolean} state (optional) Force a particular state
1392      */
1393     setActive : function(v) {
1394         
1395         this.el[v ? 'addClass' : 'removeClass']('active');
1396         this.pressed = v;
1397     },
1398      /**
1399      * toggles the current active state 
1400      */
1401     toggleActive : function(e)
1402     {
1403         this.setActive(!this.pressed); // this modifies pressed...
1404         this.fireEvent('toggle', this, e, this.pressed);
1405     },
1406      /**
1407      * get the current active state
1408      * @return {boolean} true if it's active
1409      */
1410     isActive : function()
1411     {
1412         return this.el.hasClass('active');
1413     },
1414     /**
1415      * set the text of the first selected button
1416      */
1417     setText : function(str)
1418     {
1419         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1420     },
1421     /**
1422      * get the text of the first selected button
1423      */
1424     getText : function()
1425     {
1426         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1427     },
1428     
1429     setWeight : function(str)
1430     {
1431         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1432         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1433         this.weight = str;
1434         var outline = this.outline ? 'outline-' : '';
1435         if (str == 'default') {
1436             this.el.addClass('btn-default btn-outline-secondary');        
1437             return;
1438         }
1439         this.el.addClass('btn-' + outline + str);        
1440     }
1441     
1442     
1443 });
1444 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1445
1446 Roo.bootstrap.Button.weights = [
1447     'default',
1448     'secondary' ,
1449     'primary',
1450     'success',
1451     'info',
1452     'warning',
1453     'danger',
1454     'link',
1455     'light',
1456     'dark'              
1457    
1458 ];/*
1459  * - LGPL
1460  *
1461  * column
1462  * 
1463  */
1464
1465 /**
1466  * @class Roo.bootstrap.Column
1467  * @extends Roo.bootstrap.Component
1468  * Bootstrap Column class
1469  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1470  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1471  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1472  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1473  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1474  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1475  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1476  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1477  *
1478  * 
1479  * @cfg {Boolean} hidden (true|false) hide the element
1480  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1481  * @cfg {String} fa (ban|check|...) font awesome icon
1482  * @cfg {Number} fasize (1|2|....) font awsome size
1483
1484  * @cfg {String} icon (info-sign|check|...) glyphicon name
1485
1486  * @cfg {String} html content of column.
1487  * 
1488  * @constructor
1489  * Create a new Column
1490  * @param {Object} config The config object
1491  */
1492
1493 Roo.bootstrap.Column = function(config){
1494     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1495 };
1496
1497 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1498     
1499     xs: false,
1500     sm: false,
1501     md: false,
1502     lg: false,
1503     xsoff: false,
1504     smoff: false,
1505     mdoff: false,
1506     lgoff: false,
1507     html: '',
1508     offset: 0,
1509     alert: false,
1510     fa: false,
1511     icon : false,
1512     hidden : false,
1513     fasize : 1,
1514     
1515     getAutoCreate : function(){
1516         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1517         
1518         cfg = {
1519             tag: 'div',
1520             cls: 'column'
1521         };
1522         
1523         var settings=this;
1524         var sizes =   ['xs','sm','md','lg'];
1525         sizes.map(function(size ,ix){
1526             //Roo.log( size + ':' + settings[size]);
1527             
1528             if (settings[size+'off'] !== false) {
1529                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1530             }
1531             
1532             if (settings[size] === false) {
1533                 return;
1534             }
1535             
1536             if (!settings[size]) { // 0 = hidden
1537                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1538                 // bootsrap4
1539                 for (var i = ix; i > -1; i--) {
1540                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1541                 }
1542                 
1543                 
1544                 return;
1545             }
1546             cfg.cls += ' col-' + size + '-' + settings[size] + (
1547                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1548             );
1549             
1550         });
1551         
1552         if (this.hidden) {
1553             cfg.cls += ' hidden';
1554         }
1555         
1556         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1557             cfg.cls +=' alert alert-' + this.alert;
1558         }
1559         
1560         
1561         if (this.html.length) {
1562             cfg.html = this.html;
1563         }
1564         if (this.fa) {
1565             var fasize = '';
1566             if (this.fasize > 1) {
1567                 fasize = ' fa-' + this.fasize + 'x';
1568             }
1569             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1570             
1571             
1572         }
1573         if (this.icon) {
1574             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1575         }
1576         
1577         return cfg;
1578     }
1579    
1580 });
1581
1582  
1583
1584  /*
1585  * - LGPL
1586  *
1587  * page container.
1588  * 
1589  */
1590
1591
1592 /**
1593  * @class Roo.bootstrap.Container
1594  * @extends Roo.bootstrap.Component
1595  * Bootstrap Container class
1596  * @cfg {Boolean} jumbotron is it a jumbotron element
1597  * @cfg {String} html content of element
1598  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1599  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1600  * @cfg {String} header content of header (for panel)
1601  * @cfg {String} footer content of footer (for panel)
1602  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1603  * @cfg {String} tag (header|aside|section) type of HTML tag.
1604  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1605  * @cfg {String} fa font awesome icon
1606  * @cfg {String} icon (info-sign|check|...) glyphicon name
1607  * @cfg {Boolean} hidden (true|false) hide the element
1608  * @cfg {Boolean} expandable (true|false) default false
1609  * @cfg {Boolean} expanded (true|false) default true
1610  * @cfg {String} rheader contet on the right of header
1611  * @cfg {Boolean} clickable (true|false) default false
1612
1613  *     
1614  * @constructor
1615  * Create a new Container
1616  * @param {Object} config The config object
1617  */
1618
1619 Roo.bootstrap.Container = function(config){
1620     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1621     
1622     this.addEvents({
1623         // raw events
1624          /**
1625          * @event expand
1626          * After the panel has been expand
1627          * 
1628          * @param {Roo.bootstrap.Container} this
1629          */
1630         "expand" : true,
1631         /**
1632          * @event collapse
1633          * After the panel has been collapsed
1634          * 
1635          * @param {Roo.bootstrap.Container} this
1636          */
1637         "collapse" : true,
1638         /**
1639          * @event click
1640          * When a element is chick
1641          * @param {Roo.bootstrap.Container} this
1642          * @param {Roo.EventObject} e
1643          */
1644         "click" : true
1645     });
1646 };
1647
1648 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1649     
1650     jumbotron : false,
1651     well: '',
1652     panel : '',
1653     header: '',
1654     footer : '',
1655     sticky: '',
1656     tag : false,
1657     alert : false,
1658     fa: false,
1659     icon : false,
1660     expandable : false,
1661     rheader : '',
1662     expanded : true,
1663     clickable: false,
1664   
1665      
1666     getChildContainer : function() {
1667         
1668         if(!this.el){
1669             return false;
1670         }
1671         
1672         if (this.panel.length) {
1673             return this.el.select('.panel-body',true).first();
1674         }
1675         
1676         return this.el;
1677     },
1678     
1679     
1680     getAutoCreate : function(){
1681         
1682         var cfg = {
1683             tag : this.tag || 'div',
1684             html : '',
1685             cls : ''
1686         };
1687         if (this.jumbotron) {
1688             cfg.cls = 'jumbotron';
1689         }
1690         
1691         
1692         
1693         // - this is applied by the parent..
1694         //if (this.cls) {
1695         //    cfg.cls = this.cls + '';
1696         //}
1697         
1698         if (this.sticky.length) {
1699             
1700             var bd = Roo.get(document.body);
1701             if (!bd.hasClass('bootstrap-sticky')) {
1702                 bd.addClass('bootstrap-sticky');
1703                 Roo.select('html',true).setStyle('height', '100%');
1704             }
1705              
1706             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1707         }
1708         
1709         
1710         if (this.well.length) {
1711             switch (this.well) {
1712                 case 'lg':
1713                 case 'sm':
1714                     cfg.cls +=' well well-' +this.well;
1715                     break;
1716                 default:
1717                     cfg.cls +=' well';
1718                     break;
1719             }
1720         }
1721         
1722         if (this.hidden) {
1723             cfg.cls += ' hidden';
1724         }
1725         
1726         
1727         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1728             cfg.cls +=' alert alert-' + this.alert;
1729         }
1730         
1731         var body = cfg;
1732         
1733         if (this.panel.length) {
1734             cfg.cls += ' panel panel-' + this.panel;
1735             cfg.cn = [];
1736             if (this.header.length) {
1737                 
1738                 var h = [];
1739                 
1740                 if(this.expandable){
1741                     
1742                     cfg.cls = cfg.cls + ' expandable';
1743                     
1744                     h.push({
1745                         tag: 'i',
1746                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1747                     });
1748                     
1749                 }
1750                 
1751                 h.push(
1752                     {
1753                         tag: 'span',
1754                         cls : 'panel-title',
1755                         html : (this.expandable ? '&nbsp;' : '') + this.header
1756                     },
1757                     {
1758                         tag: 'span',
1759                         cls: 'panel-header-right',
1760                         html: this.rheader
1761                     }
1762                 );
1763                 
1764                 cfg.cn.push({
1765                     cls : 'panel-heading',
1766                     style : this.expandable ? 'cursor: pointer' : '',
1767                     cn : h
1768                 });
1769                 
1770             }
1771             
1772             body = false;
1773             cfg.cn.push({
1774                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1775                 html : this.html
1776             });
1777             
1778             
1779             if (this.footer.length) {
1780                 cfg.cn.push({
1781                     cls : 'panel-footer',
1782                     html : this.footer
1783                     
1784                 });
1785             }
1786             
1787         }
1788         
1789         if (body) {
1790             body.html = this.html || cfg.html;
1791             // prefix with the icons..
1792             if (this.fa) {
1793                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1794             }
1795             if (this.icon) {
1796                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1797             }
1798             
1799             
1800         }
1801         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1802             cfg.cls =  'container';
1803         }
1804         
1805         return cfg;
1806     },
1807     
1808     initEvents: function() 
1809     {
1810         if(this.expandable){
1811             var headerEl = this.headerEl();
1812         
1813             if(headerEl){
1814                 headerEl.on('click', this.onToggleClick, this);
1815             }
1816         }
1817         
1818         if(this.clickable){
1819             this.el.on('click', this.onClick, this);
1820         }
1821         
1822     },
1823     
1824     onToggleClick : function()
1825     {
1826         var headerEl = this.headerEl();
1827         
1828         if(!headerEl){
1829             return;
1830         }
1831         
1832         if(this.expanded){
1833             this.collapse();
1834             return;
1835         }
1836         
1837         this.expand();
1838     },
1839     
1840     expand : function()
1841     {
1842         if(this.fireEvent('expand', this)) {
1843             
1844             this.expanded = true;
1845             
1846             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1847             
1848             this.el.select('.panel-body',true).first().removeClass('hide');
1849             
1850             var toggleEl = this.toggleEl();
1851
1852             if(!toggleEl){
1853                 return;
1854             }
1855
1856             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1857         }
1858         
1859     },
1860     
1861     collapse : function()
1862     {
1863         if(this.fireEvent('collapse', this)) {
1864             
1865             this.expanded = false;
1866             
1867             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1868             this.el.select('.panel-body',true).first().addClass('hide');
1869         
1870             var toggleEl = this.toggleEl();
1871
1872             if(!toggleEl){
1873                 return;
1874             }
1875
1876             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1877         }
1878     },
1879     
1880     toggleEl : function()
1881     {
1882         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1883             return;
1884         }
1885         
1886         return this.el.select('.panel-heading .fa',true).first();
1887     },
1888     
1889     headerEl : function()
1890     {
1891         if(!this.el || !this.panel.length || !this.header.length){
1892             return;
1893         }
1894         
1895         return this.el.select('.panel-heading',true).first()
1896     },
1897     
1898     bodyEl : function()
1899     {
1900         if(!this.el || !this.panel.length){
1901             return;
1902         }
1903         
1904         return this.el.select('.panel-body',true).first()
1905     },
1906     
1907     titleEl : function()
1908     {
1909         if(!this.el || !this.panel.length || !this.header.length){
1910             return;
1911         }
1912         
1913         return this.el.select('.panel-title',true).first();
1914     },
1915     
1916     setTitle : function(v)
1917     {
1918         var titleEl = this.titleEl();
1919         
1920         if(!titleEl){
1921             return;
1922         }
1923         
1924         titleEl.dom.innerHTML = v;
1925     },
1926     
1927     getTitle : function()
1928     {
1929         
1930         var titleEl = this.titleEl();
1931         
1932         if(!titleEl){
1933             return '';
1934         }
1935         
1936         return titleEl.dom.innerHTML;
1937     },
1938     
1939     setRightTitle : function(v)
1940     {
1941         var t = this.el.select('.panel-header-right',true).first();
1942         
1943         if(!t){
1944             return;
1945         }
1946         
1947         t.dom.innerHTML = v;
1948     },
1949     
1950     onClick : function(e)
1951     {
1952         e.preventDefault();
1953         
1954         this.fireEvent('click', this, e);
1955     }
1956 });
1957
1958  /*
1959  *  - LGPL
1960  *
1961  *  This is BS4's Card element.. - similar to our containers probably..
1962  * 
1963  */
1964 /**
1965  * @class Roo.bootstrap.Card
1966  * @extends Roo.bootstrap.Component
1967  * Bootstrap Card class
1968  *
1969  *
1970  * possible... may not be implemented..
1971  * @cfg {String} header_image  src url of image.
1972  * @cfg {String|Object} header
1973  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1974  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1975  * 
1976  * @cfg {String} title
1977  * @cfg {String} subtitle
1978  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1979  * @cfg {String} footer
1980  
1981  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1982  * 
1983  * @cfg {String} margin (0|1|2|3|4|5|auto)
1984  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1985  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1986  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1987  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1988  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1989  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1990  *
1991  * @cfg {String} padding (0|1|2|3|4|5)
1992  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1993  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1994  * @cfg {String} padding_left (0|1|2|3|4|5)
1995  * @cfg {String} padding_right (0|1|2|3|4|5)
1996  * @cfg {String} padding_x (0|1|2|3|4|5)
1997  * @cfg {String} padding_y (0|1|2|3|4|5)
1998  *
1999  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2000  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2001  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2002  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2003  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
2004  
2005  * @config {Boolean} dragable  if this card can be dragged.
2006  * @config {String} drag_group  group for drag
2007  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
2008  * @config {String} drop_group  group for drag
2009  * 
2010  * @config {Boolean} collapsable can the body be collapsed.
2011  * @config {Boolean} collapsed is the body collapsed when rendered...
2012  * @config {Boolean} rotateable can the body be rotated by clicking on it..
2013  * @config {Boolean} rotated is the body rotated when rendered...
2014  * 
2015  * @constructor
2016  * Create a new Container
2017  * @param {Object} config The config object
2018  */
2019
2020 Roo.bootstrap.Card = function(config){
2021     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2022     
2023     this.addEvents({
2024          // raw events
2025         /**
2026          * @event drop
2027          * When a element a card is dropped
2028          * @param {Roo.bootstrap.Card} this
2029          *
2030          * 
2031          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2032          * @param {String} position 'above' or 'below'
2033          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2034         
2035          */
2036         'drop' : true,
2037          /**
2038          * @event rotate
2039          * When a element a card is rotate
2040          * @param {Roo.bootstrap.Card} this
2041          * @param {Roo.Element} n the node being dropped?
2042          * @param {Boolean} rotate status
2043          */
2044         'rotate' : true,
2045         /**
2046          * @event cardover
2047          * When a card element is dragged over ready to drop (return false to block dropable)
2048          * @param {Roo.bootstrap.Card} this
2049          * @param {Object} data from dragdrop 
2050          */
2051          'cardover' : true
2052          
2053     });
2054 };
2055
2056
2057 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2058     
2059     
2060     weight : '',
2061     
2062     margin: '', /// may be better in component?
2063     margin_top: '', 
2064     margin_bottom: '', 
2065     margin_left: '',
2066     margin_right: '',
2067     margin_x: '',
2068     margin_y: '',
2069     
2070     padding : '',
2071     padding_top: '', 
2072     padding_bottom: '', 
2073     padding_left: '',
2074     padding_right: '',
2075     padding_x: '',
2076     padding_y: '',
2077     
2078     display: '', 
2079     display_xs: '', 
2080     display_sm: '', 
2081     display_lg: '',
2082     display_xl: '',
2083  
2084     header_image  : '',
2085     header : '',
2086     header_size : 0,
2087     title : '',
2088     subtitle : '',
2089     html : '',
2090     footer: '',
2091
2092     collapsable : false,
2093     collapsed : false,
2094     rotateable : false,
2095     rotated : false,
2096     
2097     dragable : false,
2098     drag_group : false,
2099     dropable : false,
2100     drop_group : false,
2101     childContainer : false,
2102     dropEl : false, /// the dom placeholde element that indicates drop location.
2103     containerEl: false, // body container
2104     bodyEl: false, // card-body
2105     headerContainerEl : false, //
2106     headerEl : false,
2107     header_imageEl : false,
2108     
2109     
2110     layoutCls : function()
2111     {
2112         var cls = '';
2113         var t = this;
2114         Roo.log(this.margin_bottom.length);
2115         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2116             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2117             
2118             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2119                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2120             }
2121             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2122                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2123             }
2124         });
2125         
2126         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2127             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2128                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2129             }
2130         });
2131         
2132         // more generic support?
2133         if (this.hidden) {
2134             cls += ' d-none';
2135         }
2136         
2137         return cls;
2138     },
2139  
2140        // Roo.log("Call onRender: " + this.xtype);
2141         /*  We are looking at something like this.
2142 <div class="card">
2143     <img src="..." class="card-img-top" alt="...">
2144     <div class="card-body">
2145         <h5 class="card-title">Card title</h5>
2146          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2147
2148         >> this bit is really the body...
2149         <div> << we will ad dthis in hopefully it will not break shit.
2150         
2151         ** card text does not actually have any styling...
2152         
2153             <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>
2154         
2155         </div> <<
2156           <a href="#" class="card-link">Card link</a>
2157           
2158     </div>
2159     <div class="card-footer">
2160         <small class="text-muted">Last updated 3 mins ago</small>
2161     </div>
2162 </div>
2163          */
2164     getAutoCreate : function(){
2165         
2166         var cfg = {
2167             tag : 'div',
2168             cls : 'card',
2169             cn : [ ]
2170         };
2171         
2172         if (this.weight.length && this.weight != 'light') {
2173             cfg.cls += ' text-white';
2174         } else {
2175             cfg.cls += ' text-dark'; // need as it's nested..
2176         }
2177         if (this.weight.length) {
2178             cfg.cls += ' bg-' + this.weight;
2179         }
2180         
2181         cfg.cls += ' ' + this.layoutCls(); 
2182         
2183         var hdr = false;
2184         var hdr_ctr = false;
2185         if (this.header.length) {
2186             hdr = {
2187                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2188                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2189                 cn : []
2190             };
2191             cfg.cn.push(hdr);
2192             hdr_ctr = hdr;
2193         } else {
2194             hdr = {
2195                 tag : 'div',
2196                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2197                 cn : []
2198             };
2199             cfg.cn.push(hdr);
2200             hdr_ctr = hdr;
2201         }
2202         if (this.collapsable) {
2203             hdr_ctr = {
2204             tag : 'a',
2205             cls : 'd-block user-select-none',
2206             cn: [
2207                     {
2208                         tag: 'i',
2209                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2210                     }
2211                    
2212                 ]
2213             };
2214             hdr.cn.push(hdr_ctr);
2215         }
2216         
2217         hdr_ctr.cn.push(        {
2218             tag: 'span',
2219             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2220             html : this.header
2221         });
2222         
2223         
2224         if (this.header_image.length) {
2225             cfg.cn.push({
2226                 tag : 'img',
2227                 cls : 'card-img-top',
2228                 src: this.header_image // escape?
2229             });
2230         } else {
2231             cfg.cn.push({
2232                     tag : 'div',
2233                     cls : 'card-img-top d-none' 
2234                 });
2235         }
2236             
2237         var body = {
2238             tag : 'div',
2239             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2240             cn : []
2241         };
2242         var obody = body;
2243         if (this.collapsable || this.rotateable) {
2244             obody = {
2245                 tag: 'div',
2246                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2247                 cn : [  body ]
2248             };
2249         }
2250         
2251         cfg.cn.push(obody);
2252         
2253         if (this.title.length) {
2254             body.cn.push({
2255                 tag : 'div',
2256                 cls : 'card-title',
2257                 src: this.title // escape?
2258             });
2259         }  
2260         
2261         if (this.subtitle.length) {
2262             body.cn.push({
2263                 tag : 'div',
2264                 cls : 'card-title',
2265                 src: this.subtitle // escape?
2266             });
2267         }
2268         
2269         body.cn.push({
2270             tag : 'div',
2271             cls : 'roo-card-body-ctr'
2272         });
2273         
2274         if (this.html.length) {
2275             body.cn.push({
2276                 tag: 'div',
2277                 html : this.html
2278             });
2279         }
2280         // fixme ? handle objects?
2281         
2282         if (this.footer.length) {
2283            
2284             cfg.cn.push({
2285                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2286                 html : this.footer
2287             });
2288             
2289         } else {
2290             cfg.cn.push({cls : 'card-footer d-none'});
2291         }
2292         
2293         // footer...
2294         
2295         return cfg;
2296     },
2297     
2298     
2299     getCardHeader : function()
2300     {
2301         var  ret = this.el.select('.card-header',true).first();
2302         if (ret.hasClass('d-none')) {
2303             ret.removeClass('d-none');
2304         }
2305         
2306         return ret;
2307     },
2308     getCardFooter : function()
2309     {
2310         var  ret = this.el.select('.card-footer',true).first();
2311         if (ret.hasClass('d-none')) {
2312             ret.removeClass('d-none');
2313         }
2314         
2315         return ret;
2316     },
2317     getCardImageTop : function()
2318     {
2319         var  ret = this.header_imageEl;
2320         if (ret.hasClass('d-none')) {
2321             ret.removeClass('d-none');
2322         }
2323             
2324         return ret;
2325     },
2326     
2327     getChildContainer : function()
2328     {
2329         
2330         if(!this.el){
2331             return false;
2332         }
2333         return this.el.select('.roo-card-body-ctr',true).first();    
2334     },
2335     
2336     initEvents: function() 
2337     {
2338         this.bodyEl = this.el.select('.card-body',true).first(); 
2339         this.containerEl = this.getChildContainer();
2340         if(this.dragable){
2341             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2342                     containerScroll: true,
2343                     ddGroup: this.drag_group || 'default_card_drag_group'
2344             });
2345             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2346         }
2347         if (this.dropable) {
2348             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2349                 containerScroll: true,
2350                 ddGroup: this.drop_group || 'default_card_drag_group'
2351             });
2352             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2353             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2354             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2355             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2356             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2357         }
2358         
2359         if (this.collapsable) {
2360             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2361         }
2362         if (this.rotateable) {
2363             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2364         }
2365         this.collapsableEl = this.el.select('.roo-collapsable',true).first();
2366          
2367         this.footerEl = this.el.select('.card-footer',true).first();
2368         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle',true).first();
2369         this.headerContainerEl = this.el.select('.roo-card-header-ctr',true).first();
2370         this.headerEl = this.el.select('.card-header',true).first();
2371         
2372         if (this.rotated) {
2373             this.el.addClass('roo-card-rotated');
2374             this.fireEvent('rotate', this, true);
2375         }
2376         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2377         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2378         
2379     },
2380     getDragData : function(e)
2381     {
2382         var target = this.getEl();
2383         if (target) {
2384             //this.handleSelection(e);
2385             
2386             var dragData = {
2387                 source: this,
2388                 copy: false,
2389                 nodes: this.getEl(),
2390                 records: []
2391             };
2392             
2393             
2394             dragData.ddel = target.dom ;    // the div element
2395             Roo.log(target.getWidth( ));
2396             dragData.ddel.style.width = target.getWidth() + 'px';
2397             
2398             return dragData;
2399         }
2400         return false;
2401     },
2402     /**
2403     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2404     *    whole Element becomes the target, and this causes the drop gesture to append.
2405     *
2406     *    Returns an object:
2407     *     {
2408            
2409            position : 'below' or 'above'
2410            card  : relateive to card OBJECT (or true for no cards listed)
2411            items_n : relative to nth item in list
2412            card_n : relative to  nth card in list
2413     }
2414     *
2415     *    
2416     */
2417     getTargetFromEvent : function(e, dragged_card_el)
2418     {
2419         var target = e.getTarget();
2420         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2421             target = target.parentNode;
2422         }
2423         
2424         var ret = {
2425             position: '',
2426             cards : [],
2427             card_n : -1,
2428             items_n : -1,
2429             card : false 
2430         };
2431         
2432         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2433         // see if target is one of the 'cards'...
2434         
2435         
2436         //Roo.log(this.items.length);
2437         var pos = false;
2438         
2439         var last_card_n = 0;
2440         var cards_len  = 0;
2441         for (var i = 0;i< this.items.length;i++) {
2442             
2443             if (!this.items[i].el.hasClass('card')) {
2444                  continue;
2445             }
2446             pos = this.getDropPoint(e, this.items[i].el.dom);
2447             
2448             cards_len = ret.cards.length;
2449             //Roo.log(this.items[i].el.dom.id);
2450             ret.cards.push(this.items[i]);
2451             last_card_n  = i;
2452             if (ret.card_n < 0 && pos == 'above') {
2453                 ret.position = cards_len > 0 ? 'below' : pos;
2454                 ret.items_n = i > 0 ? i - 1 : 0;
2455                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2456                 ret.card = ret.cards[ret.card_n];
2457             }
2458         }
2459         if (!ret.cards.length) {
2460             ret.card = true;
2461             ret.position = 'below';
2462             ret.items_n;
2463             return ret;
2464         }
2465         // could not find a card.. stick it at the end..
2466         if (ret.card_n < 0) {
2467             ret.card_n = last_card_n;
2468             ret.card = ret.cards[last_card_n];
2469             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2470             ret.position = 'below';
2471         }
2472         
2473         if (this.items[ret.items_n].el == dragged_card_el) {
2474             return false;
2475         }
2476         
2477         if (ret.position == 'below') {
2478             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2479             
2480             if (card_after  && card_after.el == dragged_card_el) {
2481                 return false;
2482             }
2483             return ret;
2484         }
2485         
2486         // its's after ..
2487         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2488         
2489         if (card_before  && card_before.el == dragged_card_el) {
2490             return false;
2491         }
2492         
2493         return ret;
2494     },
2495     
2496     onNodeEnter : function(n, dd, e, data){
2497         return false;
2498     },
2499     onNodeOver : function(n, dd, e, data)
2500     {
2501        
2502         var target_info = this.getTargetFromEvent(e,data.source.el);
2503         if (target_info === false) {
2504             this.dropPlaceHolder('hide');
2505             return false;
2506         }
2507         Roo.log(['getTargetFromEvent', target_info ]);
2508         
2509         
2510         if (this.fireEvent('cardover', this, [ data ]) === false) {
2511             return false;
2512         }
2513         
2514         this.dropPlaceHolder('show', target_info,data);
2515         
2516         return false; 
2517     },
2518     onNodeOut : function(n, dd, e, data){
2519         this.dropPlaceHolder('hide');
2520      
2521     },
2522     onNodeDrop : function(n, dd, e, data)
2523     {
2524         
2525         // call drop - return false if
2526         
2527         // this could actually fail - if the Network drops..
2528         // we will ignore this at present..- client should probably reload
2529         // the whole set of cards if stuff like that fails.
2530         
2531         
2532         var info = this.getTargetFromEvent(e,data.source.el);
2533         if (info === false) {
2534             return false;
2535         }
2536         this.dropPlaceHolder('hide');
2537   
2538           
2539     
2540         this.acceptCard(data.source, info.position, info.card, info.items_n);
2541         return true;
2542          
2543     },
2544     firstChildCard : function()
2545     {
2546         for (var i = 0;i< this.items.length;i++) {
2547             
2548             if (!this.items[i].el.hasClass('card')) {
2549                  continue;
2550             }
2551             return this.items[i];
2552         }
2553         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2554     },
2555     /**
2556      * accept card
2557      *
2558      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2559      */
2560     acceptCard : function(move_card,  position, next_to_card )
2561     {
2562         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2563             return false;
2564         }
2565         
2566         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2567         
2568         move_card.parent().removeCard(move_card);
2569         
2570         
2571         var dom = move_card.el.dom;
2572         dom.style.width = ''; // clear with - which is set by drag.
2573         
2574         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2575             var cardel = next_to_card.el.dom;
2576             
2577             if (position == 'above' ) {
2578                 cardel.parentNode.insertBefore(dom, cardel);
2579             } else if (cardel.nextSibling) {
2580                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2581             } else {
2582                 cardel.parentNode.append(dom);
2583             }
2584         } else {
2585             // card container???
2586             this.containerEl.dom.append(dom);
2587         }
2588         
2589         //FIXME HANDLE card = true 
2590         
2591         // add this to the correct place in items.
2592         
2593         // remove Card from items.
2594         
2595        
2596         if (this.items.length) {
2597             var nitems = [];
2598             //Roo.log([info.items_n, info.position, this.items.length]);
2599             for (var i =0; i < this.items.length; i++) {
2600                 if (i == to_items_n && position == 'above') {
2601                     nitems.push(move_card);
2602                 }
2603                 nitems.push(this.items[i]);
2604                 if (i == to_items_n && position == 'below') {
2605                     nitems.push(move_card);
2606                 }
2607             }
2608             this.items = nitems;
2609             Roo.log(this.items);
2610         } else {
2611             this.items.push(move_card);
2612         }
2613         
2614         move_card.parentId = this.id;
2615         
2616         return true;
2617         
2618         
2619     },
2620     removeCard : function(c)
2621     {
2622         this.items = this.items.filter(function(e) { return e != c });
2623  
2624         var dom = c.el.dom;
2625         dom.parentNode.removeChild(dom);
2626         dom.style.width = ''; // clear with - which is set by drag.
2627         c.parentId = false;
2628         
2629     },
2630     
2631     /**    Decide whether to drop above or below a View node. */
2632     getDropPoint : function(e, n, dd)
2633     {
2634         if (dd) {
2635              return false;
2636         }
2637         if (n == this.containerEl.dom) {
2638             return "above";
2639         }
2640         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2641         var c = t + (b - t) / 2;
2642         var y = Roo.lib.Event.getPageY(e);
2643         if(y <= c) {
2644             return "above";
2645         }else{
2646             return "below";
2647         }
2648     },
2649     onToggleCollapse : function(e)
2650         {
2651         if (this.collapsed) {
2652             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2653             this.collapsableEl.addClass('show');
2654             this.collapsed = false;
2655             return;
2656         }
2657         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2658         this.collapsableEl.removeClass('show');
2659         this.collapsed = true;
2660         
2661     
2662     },
2663     
2664     onToggleRotate : function(e)
2665     {
2666         this.collapsableEl.removeClass('show');
2667         this.footerEl.removeClass('d-none');
2668         this.el.removeClass('roo-card-rotated');
2669         this.el.removeClass('d-none');
2670         if (this.rotated) {
2671             
2672             this.collapsableEl.addClass('show');
2673             this.rotated = false;
2674             this.fireEvent('rotate', this, this.rotated);
2675             return;
2676         }
2677         this.el.addClass('roo-card-rotated');
2678         this.footerEl.addClass('d-none');
2679         this.el.select('.roo-collapsable').removeClass('show');
2680         
2681         this.rotated = true;
2682         this.fireEvent('rotate', this, this.rotated);
2683     
2684     },
2685     
2686     dropPlaceHolder: function (action, info, data)
2687     {
2688         if (this.dropEl === false) {
2689             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2690             cls : 'd-none'
2691             },true);
2692         }
2693         this.dropEl.removeClass(['d-none', 'd-block']);        
2694         if (action == 'hide') {
2695             
2696             this.dropEl.addClass('d-none');
2697             return;
2698         }
2699         // FIXME - info.card == true!!!
2700         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2701         
2702         if (info.card !== true) {
2703             var cardel = info.card.el.dom;
2704             
2705             if (info.position == 'above') {
2706                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2707             } else if (cardel.nextSibling) {
2708                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2709             } else {
2710                 cardel.parentNode.append(this.dropEl.dom);
2711             }
2712         } else {
2713             // card container???
2714             this.containerEl.dom.append(this.dropEl.dom);
2715         }
2716         
2717         this.dropEl.addClass('d-block roo-card-dropzone');
2718         
2719         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2720         
2721         
2722     
2723     
2724     
2725     },
2726     setHeaderText: function(html)
2727     {
2728         this.header = html;
2729         if (this.headerContainerEl) {
2730             this.headerContainerEl.dom.innerHTML = html;
2731         }
2732     },
2733     onHeaderImageLoad : function(ev, he)
2734     {
2735         if (!this.header_image_fit_square) {
2736             return;
2737         }
2738         
2739         var hw = he.naturalHeight / he.naturalWidth;
2740         // wide image = < 0
2741         // tall image = > 1
2742         //var w = he.dom.naturalWidth;
2743         var ww = he.width;
2744         he.style.left =  0;
2745         he.style.position =  'relative';
2746         if (hw > 1) {
2747             var nw = (ww * (1/hw));
2748             Roo.get(he).setSize( ww * (1/hw),  ww);
2749             he.style.left =  ((ww - nw)/ 2) + 'px';
2750             he.style.position =  'relative';
2751         }
2752
2753     }
2754
2755     
2756 });
2757
2758 /*
2759  * - LGPL
2760  *
2761  * Card header - holder for the card header elements.
2762  * 
2763  */
2764
2765 /**
2766  * @class Roo.bootstrap.CardHeader
2767  * @extends Roo.bootstrap.Element
2768  * Bootstrap CardHeader class
2769  * @constructor
2770  * Create a new Card Header - that you can embed children into
2771  * @param {Object} config The config object
2772  */
2773
2774 Roo.bootstrap.CardHeader = function(config){
2775     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2776 };
2777
2778 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2779     
2780     
2781     container_method : 'getCardHeader' 
2782     
2783      
2784     
2785     
2786    
2787 });
2788
2789  
2790
2791  /*
2792  * - LGPL
2793  *
2794  * Card footer - holder for the card footer elements.
2795  * 
2796  */
2797
2798 /**
2799  * @class Roo.bootstrap.CardFooter
2800  * @extends Roo.bootstrap.Element
2801  * Bootstrap CardFooter class
2802  * @constructor
2803  * Create a new Card Footer - that you can embed children into
2804  * @param {Object} config The config object
2805  */
2806
2807 Roo.bootstrap.CardFooter = function(config){
2808     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2809 };
2810
2811 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2812     
2813     
2814     container_method : 'getCardFooter' 
2815     
2816      
2817     
2818     
2819    
2820 });
2821
2822  
2823
2824  /*
2825  * - LGPL
2826  *
2827  * Card header - holder for the card header elements.
2828  * 
2829  */
2830
2831 /**
2832  * @class Roo.bootstrap.CardImageTop
2833  * @extends Roo.bootstrap.Element
2834  * Bootstrap CardImageTop class
2835  * @constructor
2836  * Create a new Card Image Top container
2837  * @param {Object} config The config object
2838  */
2839
2840 Roo.bootstrap.CardImageTop = function(config){
2841     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2842 };
2843
2844 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2845     
2846    
2847     container_method : 'getCardImageTop' 
2848     
2849      
2850     
2851    
2852 });
2853
2854  
2855
2856  
2857 /*
2858 * Licence: LGPL
2859 */
2860
2861 /**
2862  * @class Roo.bootstrap.ButtonUploader
2863  * @extends Roo.bootstrap.Button
2864  * Bootstrap Button Uploader class - it's a button which when you add files to it
2865  *
2866  * 
2867  * @cfg {Number} errorTimeout default 3000
2868  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
2869  * @cfg {Array}  html The button text.
2870  * @cfg {Boolean}  multiple (default true) Should the upload allow multiple files to be uploaded.
2871  *
2872  * @constructor
2873  * Create a new CardUploader
2874  * @param {Object} config The config object
2875  */
2876
2877 Roo.bootstrap.ButtonUploader = function(config){
2878     
2879  
2880     
2881     Roo.bootstrap.ButtonUploader.superclass.constructor.call(this, config);
2882     
2883      
2884      this.addEvents({
2885          // raw events
2886         /**
2887          * @event beforeselect
2888          * When button is pressed, before show upload files dialog is shown
2889          * @param {Roo.bootstrap.UploaderButton} this
2890          *
2891          */
2892         'beforeselect' : true,
2893          /**
2894          * @event fired when files have been selected, 
2895          * When a the download link is clicked
2896          * @param {Roo.bootstrap.UploaderButton} this
2897          * @param {Array} Array of files that have been uploaded
2898          */
2899         'uploaded' : true
2900         
2901     });
2902 };
2903  
2904 Roo.extend(Roo.bootstrap.ButtonUploader, Roo.bootstrap.Button,  {
2905     
2906      
2907     errorTimeout : 3000,
2908      
2909     images : false,
2910    
2911     fileCollection : false,
2912     allowBlank : true,
2913     
2914     multiple : true,
2915     
2916     getAutoCreate : function()
2917     {
2918         var im = {
2919             tag: 'input',
2920             type : 'file',
2921             cls : 'd-none  roo-card-upload-selector' 
2922           
2923         };
2924         if (this.multiple) {
2925             im.multiple = 'multiple';
2926         }
2927         
2928         return  {
2929             cls :'div' ,
2930             cn : [
2931                 Roo.bootstrap.Button.prototype.getAutoCreate.call(this),
2932                 im
2933
2934             ]
2935         };
2936            
2937          
2938     },
2939      
2940    
2941     initEvents : function()
2942     {
2943         
2944         Roo.bootstrap.Button.prototype.initEvents.call(this);
2945         
2946         
2947         
2948         
2949         
2950         this.urlAPI = (window.createObjectURL && window) || 
2951                                 (window.URL && URL.revokeObjectURL && URL) || 
2952                                 (window.webkitURL && webkitURL);
2953                         
2954          
2955          
2956          
2957         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
2958         
2959         this.selectorEl.on('change', this.onFileSelected, this);
2960          
2961          
2962        
2963     },
2964     
2965    
2966     onClick : function(e)
2967     {
2968         e.preventDefault();
2969         
2970         if ( this.fireEvent('beforeselect', this) === false) {
2971             return;
2972         }
2973          
2974         this.selectorEl.dom.click();
2975          
2976     },
2977     
2978     onFileSelected : function(e)
2979     {
2980         e.preventDefault();
2981         
2982         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
2983             return;
2984         }
2985         var files = Array.prototype.slice.call(this.selectorEl.dom.files);
2986         this.selectorEl.dom.value  = '';// hopefully reset..
2987         
2988         this.fireEvent('uploaded', this,  files );
2989         
2990     },
2991     
2992        
2993    
2994     
2995     /**
2996      * addCard - add an Attachment to the uploader
2997      * @param data - the data about the image to upload
2998      *
2999      * {
3000           id : 123
3001           title : "Title of file",
3002           is_uploaded : false,
3003           src : "http://.....",
3004           srcfile : { the File upload object },
3005           mimetype : file.type,
3006           preview : false,
3007           is_deleted : 0
3008           .. any other data...
3009         }
3010      *
3011      * 
3012     */
3013      
3014     reset: function()
3015     {
3016          
3017          this.selectorEl
3018     } 
3019     
3020     
3021     
3022     
3023 });
3024  /*
3025  * - LGPL
3026  *
3027  * image
3028  * 
3029  */
3030
3031
3032 /**
3033  * @class Roo.bootstrap.Img
3034  * @extends Roo.bootstrap.Component
3035  * Bootstrap Img class
3036  * @cfg {Boolean} imgResponsive false | true
3037  * @cfg {String} border rounded | circle | thumbnail
3038  * @cfg {String} src image source
3039  * @cfg {String} alt image alternative text
3040  * @cfg {String} href a tag href
3041  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
3042  * @cfg {String} xsUrl xs image source
3043  * @cfg {String} smUrl sm image source
3044  * @cfg {String} mdUrl md image source
3045  * @cfg {String} lgUrl lg image source
3046  * @cfg {Boolean} backgroundContain (use style background and contain image in content)
3047  * 
3048  * @constructor
3049  * Create a new Input
3050  * @param {Object} config The config object
3051  */
3052
3053 Roo.bootstrap.Img = function(config){
3054     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3055     
3056     this.addEvents({
3057         // img events
3058         /**
3059          * @event click
3060          * The img click event for the img.
3061          * @param {Roo.EventObject} e
3062          */
3063         "click" : true,
3064         /**
3065          * @event load
3066          * The when any image loads
3067          * @param {Roo.EventObject} e
3068          */
3069         "load" : true
3070     });
3071 };
3072
3073 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3074     
3075     imgResponsive: true,
3076     border: '',
3077     src: 'about:blank',
3078     href: false,
3079     target: false,
3080     xsUrl: '',
3081     smUrl: '',
3082     mdUrl: '',
3083     lgUrl: '',
3084     backgroundContain : false,
3085
3086     getAutoCreate : function()
3087     {   
3088         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3089             return this.createSingleImg();
3090         }
3091         
3092         var cfg = {
3093             tag: 'div',
3094             cls: 'roo-image-responsive-group',
3095             cn: []
3096         };
3097         var _this = this;
3098         
3099         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3100             
3101             if(!_this[size + 'Url']){
3102                 return;
3103             }
3104             
3105             var img = {
3106                 tag: 'img',
3107                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3108                 html: _this.html || cfg.html,
3109                 src: _this[size + 'Url']
3110             };
3111             
3112             img.cls += ' roo-image-responsive-' + size;
3113             
3114             var s = ['xs', 'sm', 'md', 'lg'];
3115             
3116             s.splice(s.indexOf(size), 1);
3117             
3118             Roo.each(s, function(ss){
3119                 img.cls += ' hidden-' + ss;
3120             });
3121             
3122             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3123                 cfg.cls += ' img-' + _this.border;
3124             }
3125             
3126             if(_this.alt){
3127                 cfg.alt = _this.alt;
3128             }
3129             
3130             if(_this.href){
3131                 var a = {
3132                     tag: 'a',
3133                     href: _this.href,
3134                     cn: [
3135                         img
3136                     ]
3137                 };
3138
3139                 if(this.target){
3140                     a.target = _this.target;
3141                 }
3142             }
3143             
3144             cfg.cn.push((_this.href) ? a : img);
3145             
3146         });
3147         
3148         return cfg;
3149     },
3150     
3151     createSingleImg : function()
3152     {
3153         var cfg = {
3154             tag: 'img',
3155             cls: (this.imgResponsive) ? 'img-responsive' : '',
3156             html : null,
3157             src : Roo.BLANK_IMAGE_URL  // just incase src get's set to undefined?!?
3158         };
3159         
3160         if (this.backgroundContain) {
3161             cfg.cls += ' background-contain';
3162         }
3163         
3164         cfg.html = this.html || cfg.html;
3165         
3166         if (this.backgroundContain) {
3167             cfg.style="background-image: url(" + this.src + ')';
3168         } else {
3169             cfg.src = this.src || cfg.src;
3170         }
3171         
3172         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3173             cfg.cls += ' img-' + this.border;
3174         }
3175         
3176         if(this.alt){
3177             cfg.alt = this.alt;
3178         }
3179         
3180         if(this.href){
3181             var a = {
3182                 tag: 'a',
3183                 href: this.href,
3184                 cn: [
3185                     cfg
3186                 ]
3187             };
3188             
3189             if(this.target){
3190                 a.target = this.target;
3191             }
3192             
3193         }
3194         
3195         return (this.href) ? a : cfg;
3196     },
3197     
3198     initEvents: function() 
3199     {
3200         if(!this.href){
3201             this.el.on('click', this.onClick, this);
3202         }
3203         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3204             this.el.on('load', this.onImageLoad, this);
3205         } else {
3206             // not sure if this works.. not tested
3207             this.el.select('img', true).on('load', this.onImageLoad, this);
3208         }
3209         
3210     },
3211     
3212     onClick : function(e)
3213     {
3214         Roo.log('img onclick');
3215         this.fireEvent('click', this, e);
3216     },
3217     onImageLoad: function(e)
3218     {
3219         Roo.log('img load');
3220         this.fireEvent('load', this, e);
3221     },
3222     
3223     /**
3224      * Sets the url of the image - used to update it
3225      * @param {String} url the url of the image
3226      */
3227     
3228     setSrc : function(url)
3229     {
3230         this.src =  url;
3231         
3232         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3233             if (this.backgroundContain) {
3234                 this.el.dom.style.backgroundImage =  'url(' + url + ')';
3235             } else {
3236                 this.el.dom.src =  url;
3237             }
3238             return;
3239         }
3240         
3241         this.el.select('img', true).first().dom.src =  url;
3242     }
3243     
3244     
3245    
3246 });
3247
3248  /*
3249  * - LGPL
3250  *
3251  * image
3252  * 
3253  */
3254
3255
3256 /**
3257  * @class Roo.bootstrap.Link
3258  * @extends Roo.bootstrap.Component
3259  * Bootstrap Link Class
3260  * @cfg {String} alt image alternative text
3261  * @cfg {String} href a tag href
3262  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3263  * @cfg {String} html the content of the link.
3264  * @cfg {String} anchor name for the anchor link
3265  * @cfg {String} fa - favicon
3266
3267  * @cfg {Boolean} preventDefault (true | false) default false
3268
3269  * 
3270  * @constructor
3271  * Create a new Input
3272  * @param {Object} config The config object
3273  */
3274
3275 Roo.bootstrap.Link = function(config){
3276     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3277     
3278     this.addEvents({
3279         // img events
3280         /**
3281          * @event click
3282          * The img click event for the img.
3283          * @param {Roo.EventObject} e
3284          */
3285         "click" : true
3286     });
3287 };
3288
3289 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3290     
3291     href: false,
3292     target: false,
3293     preventDefault: false,
3294     anchor : false,
3295     alt : false,
3296     fa: false,
3297
3298
3299     getAutoCreate : function()
3300     {
3301         var html = this.html || '';
3302         
3303         if (this.fa !== false) {
3304             html = '<i class="fa fa-' + this.fa + '"></i>';
3305         }
3306         var cfg = {
3307             tag: 'a'
3308         };
3309         // anchor's do not require html/href...
3310         if (this.anchor === false) {
3311             cfg.html = html;
3312             cfg.href = this.href || '#';
3313         } else {
3314             cfg.name = this.anchor;
3315             if (this.html !== false || this.fa !== false) {
3316                 cfg.html = html;
3317             }
3318             if (this.href !== false) {
3319                 cfg.href = this.href;
3320             }
3321         }
3322         
3323         if(this.alt !== false){
3324             cfg.alt = this.alt;
3325         }
3326         
3327         
3328         if(this.target !== false) {
3329             cfg.target = this.target;
3330         }
3331         
3332         return cfg;
3333     },
3334     
3335     initEvents: function() {
3336         
3337         if(!this.href || this.preventDefault){
3338             this.el.on('click', this.onClick, this);
3339         }
3340     },
3341     
3342     onClick : function(e)
3343     {
3344         if(this.preventDefault){
3345             e.preventDefault();
3346         }
3347         //Roo.log('img onclick');
3348         this.fireEvent('click', this, e);
3349     }
3350    
3351 });
3352
3353  /*
3354  * - LGPL
3355  *
3356  * header
3357  * 
3358  */
3359
3360 /**
3361  * @class Roo.bootstrap.Header
3362  * @extends Roo.bootstrap.Component
3363  * Bootstrap Header class
3364  * @cfg {String} html content of header
3365  * @cfg {Number} level (1|2|3|4|5|6) default 1
3366  * 
3367  * @constructor
3368  * Create a new Header
3369  * @param {Object} config The config object
3370  */
3371
3372
3373 Roo.bootstrap.Header  = function(config){
3374     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3375 };
3376
3377 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3378     
3379     //href : false,
3380     html : false,
3381     level : 1,
3382     
3383     
3384     
3385     getAutoCreate : function(){
3386         
3387         
3388         
3389         var cfg = {
3390             tag: 'h' + (1 *this.level),
3391             html: this.html || ''
3392         } ;
3393         
3394         return cfg;
3395     }
3396    
3397 });
3398
3399  
3400
3401  /*
3402  * Based on:
3403  * Ext JS Library 1.1.1
3404  * Copyright(c) 2006-2007, Ext JS, LLC.
3405  *
3406  * Originally Released Under LGPL - original licence link has changed is not relivant.
3407  *
3408  * Fork - LGPL
3409  * <script type="text/javascript">
3410  */
3411  
3412 /**
3413  * @class Roo.bootstrap.MenuMgr
3414  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3415  * @singleton
3416  */
3417 Roo.bootstrap.MenuMgr = function(){
3418    var menus, active, groups = {}, attached = false, lastShow = new Date();
3419
3420    // private - called when first menu is created
3421    function init(){
3422        menus = {};
3423        active = new Roo.util.MixedCollection();
3424        Roo.get(document).addKeyListener(27, function(){
3425            if(active.length > 0){
3426                hideAll();
3427            }
3428        });
3429    }
3430
3431    // private
3432    function hideAll(){
3433        if(active && active.length > 0){
3434            var c = active.clone();
3435            c.each(function(m){
3436                m.hide();
3437            });
3438        }
3439    }
3440
3441    // private
3442    function onHide(m){
3443        active.remove(m);
3444        if(active.length < 1){
3445            Roo.get(document).un("mouseup", onMouseDown);
3446             
3447            attached = false;
3448        }
3449    }
3450
3451    // private
3452    function onShow(m){
3453        var last = active.last();
3454        lastShow = new Date();
3455        active.add(m);
3456        if(!attached){
3457           Roo.get(document).on("mouseup", onMouseDown);
3458            
3459            attached = true;
3460        }
3461        if(m.parentMenu){
3462           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3463           m.parentMenu.activeChild = m;
3464        }else if(last && last.isVisible()){
3465           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3466        }
3467    }
3468
3469    // private
3470    function onBeforeHide(m){
3471        if(m.activeChild){
3472            m.activeChild.hide();
3473        }
3474        if(m.autoHideTimer){
3475            clearTimeout(m.autoHideTimer);
3476            delete m.autoHideTimer;
3477        }
3478    }
3479
3480    // private
3481    function onBeforeShow(m){
3482        var pm = m.parentMenu;
3483        if(!pm && !m.allowOtherMenus){
3484            hideAll();
3485        }else if(pm && pm.activeChild && active != m){
3486            pm.activeChild.hide();
3487        }
3488    }
3489
3490    // private this should really trigger on mouseup..
3491    function onMouseDown(e){
3492         Roo.log("on Mouse Up");
3493         
3494         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3495             Roo.log("MenuManager hideAll");
3496             hideAll();
3497             e.stopEvent();
3498         }
3499         
3500         
3501    }
3502
3503    // private
3504    function onBeforeCheck(mi, state){
3505        if(state){
3506            var g = groups[mi.group];
3507            for(var i = 0, l = g.length; i < l; i++){
3508                if(g[i] != mi){
3509                    g[i].setChecked(false);
3510                }
3511            }
3512        }
3513    }
3514
3515    return {
3516
3517        /**
3518         * Hides all menus that are currently visible
3519         */
3520        hideAll : function(){
3521             hideAll();  
3522        },
3523
3524        // private
3525        register : function(menu){
3526            if(!menus){
3527                init();
3528            }
3529            menus[menu.id] = menu;
3530            menu.on("beforehide", onBeforeHide);
3531            menu.on("hide", onHide);
3532            menu.on("beforeshow", onBeforeShow);
3533            menu.on("show", onShow);
3534            var g = menu.group;
3535            if(g && menu.events["checkchange"]){
3536                if(!groups[g]){
3537                    groups[g] = [];
3538                }
3539                groups[g].push(menu);
3540                menu.on("checkchange", onCheck);
3541            }
3542        },
3543
3544         /**
3545          * Returns a {@link Roo.menu.Menu} object
3546          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3547          * be used to generate and return a new Menu instance.
3548          */
3549        get : function(menu){
3550            if(typeof menu == "string"){ // menu id
3551                return menus[menu];
3552            }else if(menu.events){  // menu instance
3553                return menu;
3554            }
3555            /*else if(typeof menu.length == 'number'){ // array of menu items?
3556                return new Roo.bootstrap.Menu({items:menu});
3557            }else{ // otherwise, must be a config
3558                return new Roo.bootstrap.Menu(menu);
3559            }
3560            */
3561            return false;
3562        },
3563
3564        // private
3565        unregister : function(menu){
3566            delete menus[menu.id];
3567            menu.un("beforehide", onBeforeHide);
3568            menu.un("hide", onHide);
3569            menu.un("beforeshow", onBeforeShow);
3570            menu.un("show", onShow);
3571            var g = menu.group;
3572            if(g && menu.events["checkchange"]){
3573                groups[g].remove(menu);
3574                menu.un("checkchange", onCheck);
3575            }
3576        },
3577
3578        // private
3579        registerCheckable : function(menuItem){
3580            var g = menuItem.group;
3581            if(g){
3582                if(!groups[g]){
3583                    groups[g] = [];
3584                }
3585                groups[g].push(menuItem);
3586                menuItem.on("beforecheckchange", onBeforeCheck);
3587            }
3588        },
3589
3590        // private
3591        unregisterCheckable : function(menuItem){
3592            var g = menuItem.group;
3593            if(g){
3594                groups[g].remove(menuItem);
3595                menuItem.un("beforecheckchange", onBeforeCheck);
3596            }
3597        }
3598    };
3599 }();/*
3600  * - LGPL
3601  *
3602  * menu
3603  * 
3604  */
3605
3606 /**
3607  * @class Roo.bootstrap.Menu
3608  * @extends Roo.bootstrap.Component
3609  * Bootstrap Menu class - container for MenuItems
3610  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3611  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3612  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3613  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3614   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3615   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3616  
3617  * @constructor
3618  * Create a new Menu
3619  * @param {Object} config The config object
3620  */
3621
3622
3623 Roo.bootstrap.Menu = function(config){
3624     
3625     if (config.type == 'treeview') {
3626         // normally menu's are drawn attached to the document to handle layering etc..
3627         // however treeview (used by the docs menu is drawn into the parent element)
3628         this.container_method = 'getChildContainer'; 
3629     }
3630     
3631     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3632     if (this.registerMenu && this.type != 'treeview')  {
3633         Roo.bootstrap.MenuMgr.register(this);
3634     }
3635     
3636     
3637     this.addEvents({
3638         /**
3639          * @event beforeshow
3640          * Fires before this menu is displayed (return false to block)
3641          * @param {Roo.menu.Menu} this
3642          */
3643         beforeshow : true,
3644         /**
3645          * @event beforehide
3646          * Fires before this menu is hidden (return false to block)
3647          * @param {Roo.menu.Menu} this
3648          */
3649         beforehide : true,
3650         /**
3651          * @event show
3652          * Fires after this menu is displayed
3653          * @param {Roo.menu.Menu} this
3654          */
3655         show : true,
3656         /**
3657          * @event hide
3658          * Fires after this menu is hidden
3659          * @param {Roo.menu.Menu} this
3660          */
3661         hide : true,
3662         /**
3663          * @event click
3664          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3665          * @param {Roo.menu.Menu} this
3666          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3667          * @param {Roo.EventObject} e
3668          */
3669         click : true,
3670         /**
3671          * @event mouseover
3672          * Fires when the mouse is hovering over this menu
3673          * @param {Roo.menu.Menu} this
3674          * @param {Roo.EventObject} e
3675          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3676          */
3677         mouseover : true,
3678         /**
3679          * @event mouseout
3680          * Fires when the mouse exits this menu
3681          * @param {Roo.menu.Menu} this
3682          * @param {Roo.EventObject} e
3683          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3684          */
3685         mouseout : true,
3686         /**
3687          * @event itemclick
3688          * Fires when a menu item contained in this menu is clicked
3689          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3690          * @param {Roo.EventObject} e
3691          */
3692         itemclick: true
3693     });
3694     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3695 };
3696
3697 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3698     
3699    /// html : false,
3700    
3701     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3702     type: false,
3703     /**
3704      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3705      */
3706     registerMenu : true,
3707     
3708     menuItems :false, // stores the menu items..
3709     
3710     hidden:true,
3711         
3712     parentMenu : false,
3713     
3714     stopEvent : true,
3715     
3716     isLink : false,
3717     
3718     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3719     
3720     hideTrigger : false,
3721     
3722     align : 'tl-bl?',
3723     
3724     
3725     getChildContainer : function() {
3726         return this.el;  
3727     },
3728     
3729     getAutoCreate : function(){
3730          
3731         //if (['right'].indexOf(this.align)!==-1) {
3732         //    cfg.cn[1].cls += ' pull-right'
3733         //}
3734          
3735         var cfg = {
3736             tag : 'ul',
3737             cls : 'dropdown-menu shadow' ,
3738             style : 'z-index:1000'
3739             
3740         };
3741         
3742         if (this.type === 'submenu') {
3743             cfg.cls = 'submenu active';
3744         }
3745         if (this.type === 'treeview') {
3746             cfg.cls = 'treeview-menu';
3747         }
3748         
3749         return cfg;
3750     },
3751     initEvents : function() {
3752         
3753        // Roo.log("ADD event");
3754        // Roo.log(this.triggerEl.dom);
3755         if (this.triggerEl) {
3756             
3757             this.triggerEl.on('click', this.onTriggerClick, this);
3758             
3759             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3760             
3761             if (!this.hideTrigger) {
3762                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3763                     // dropdown toggle on the 'a' in BS4?
3764                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3765                 } else {
3766                     this.triggerEl.addClass('dropdown-toggle');
3767                 }
3768             }
3769         }
3770         
3771         if (Roo.isTouch) {
3772             this.el.on('touchstart'  , this.onTouch, this);
3773         }
3774         this.el.on('click' , this.onClick, this);
3775
3776         this.el.on("mouseover", this.onMouseOver, this);
3777         this.el.on("mouseout", this.onMouseOut, this);
3778         
3779     },
3780     
3781     findTargetItem : function(e)
3782     {
3783         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3784         if(!t){
3785             return false;
3786         }
3787         //Roo.log(t);         Roo.log(t.id);
3788         if(t && t.id){
3789             //Roo.log(this.menuitems);
3790             return this.menuitems.get(t.id);
3791             
3792             //return this.items.get(t.menuItemId);
3793         }
3794         
3795         return false;
3796     },
3797     
3798     onTouch : function(e) 
3799     {
3800         Roo.log("menu.onTouch");
3801         //e.stopEvent(); this make the user popdown broken
3802         this.onClick(e);
3803     },
3804     
3805     onClick : function(e)
3806     {
3807         Roo.log("menu.onClick");
3808         
3809         var t = this.findTargetItem(e);
3810         if(!t || t.isContainer){
3811             return;
3812         }
3813         Roo.log(e);
3814         /*
3815         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3816             if(t == this.activeItem && t.shouldDeactivate(e)){
3817                 this.activeItem.deactivate();
3818                 delete this.activeItem;
3819                 return;
3820             }
3821             if(t.canActivate){
3822                 this.setActiveItem(t, true);
3823             }
3824             return;
3825             
3826             
3827         }
3828         */
3829        
3830         Roo.log('pass click event');
3831         
3832         t.onClick(e);
3833         
3834         this.fireEvent("click", this, t, e);
3835         
3836         var _this = this;
3837         
3838         if(!t.href.length || t.href == '#'){
3839             (function() { _this.hide(); }).defer(100);
3840         }
3841         
3842     },
3843     
3844     onMouseOver : function(e){
3845         var t  = this.findTargetItem(e);
3846         //Roo.log(t);
3847         //if(t){
3848         //    if(t.canActivate && !t.disabled){
3849         //        this.setActiveItem(t, true);
3850         //    }
3851         //}
3852         
3853         this.fireEvent("mouseover", this, e, t);
3854     },
3855     isVisible : function(){
3856         return !this.hidden;
3857     },
3858     onMouseOut : function(e){
3859         var t  = this.findTargetItem(e);
3860         
3861         //if(t ){
3862         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3863         //        this.activeItem.deactivate();
3864         //        delete this.activeItem;
3865         //    }
3866         //}
3867         this.fireEvent("mouseout", this, e, t);
3868     },
3869     
3870     
3871     /**
3872      * Displays this menu relative to another element
3873      * @param {String/HTMLElement/Roo.Element} element The element to align to
3874      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3875      * the element (defaults to this.defaultAlign)
3876      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3877      */
3878     show : function(el, pos, parentMenu)
3879     {
3880         if (false === this.fireEvent("beforeshow", this)) {
3881             Roo.log("show canceled");
3882             return;
3883         }
3884         this.parentMenu = parentMenu;
3885         if(!this.el){
3886             this.render();
3887         }
3888         this.el.addClass('show'); // show otherwise we do not know how big we are..
3889          
3890         var xy = this.el.getAlignToXY(el, pos);
3891         
3892         // bl-tl << left align  below
3893         // tl-bl << left align 
3894         
3895         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3896             // if it goes to far to the right.. -> align left.
3897             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3898         }
3899         if(xy[0] < 0){
3900             // was left align - go right?
3901             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3902         }
3903         
3904         // goes down the bottom
3905         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3906            xy[1]  < 0 ){
3907             var a = this.align.replace('?', '').split('-');
3908             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3909             
3910         }
3911         
3912         this.showAt(  xy , parentMenu, false);
3913     },
3914      /**
3915      * Displays this menu at a specific xy position
3916      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3917      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3918      */
3919     showAt : function(xy, parentMenu, /* private: */_e){
3920         this.parentMenu = parentMenu;
3921         if(!this.el){
3922             this.render();
3923         }
3924         if(_e !== false){
3925             this.fireEvent("beforeshow", this);
3926             //xy = this.el.adjustForConstraints(xy);
3927         }
3928         
3929         //this.el.show();
3930         this.hideMenuItems();
3931         this.hidden = false;
3932         if (this.triggerEl) {
3933             this.triggerEl.addClass('open');
3934         }
3935         
3936         this.el.addClass('show');
3937         
3938         
3939         
3940         // reassign x when hitting right
3941         
3942         // reassign y when hitting bottom
3943         
3944         // but the list may align on trigger left or trigger top... should it be a properity?
3945         
3946         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3947             this.el.setXY(xy);
3948         }
3949         
3950         this.focus();
3951         this.fireEvent("show", this);
3952     },
3953     
3954     focus : function(){
3955         return;
3956         if(!this.hidden){
3957             this.doFocus.defer(50, this);
3958         }
3959     },
3960
3961     doFocus : function(){
3962         if(!this.hidden){
3963             this.focusEl.focus();
3964         }
3965     },
3966
3967     /**
3968      * Hides this menu and optionally all parent menus
3969      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3970      */
3971     hide : function(deep)
3972     {
3973         if (false === this.fireEvent("beforehide", this)) {
3974             Roo.log("hide canceled");
3975             return;
3976         }
3977         this.hideMenuItems();
3978         if(this.el && this.isVisible()){
3979            
3980             if(this.activeItem){
3981                 this.activeItem.deactivate();
3982                 this.activeItem = null;
3983             }
3984             if (this.triggerEl) {
3985                 this.triggerEl.removeClass('open');
3986             }
3987             
3988             this.el.removeClass('show');
3989             this.hidden = true;
3990             this.fireEvent("hide", this);
3991         }
3992         if(deep === true && this.parentMenu){
3993             this.parentMenu.hide(true);
3994         }
3995     },
3996     
3997     onTriggerClick : function(e)
3998     {
3999         Roo.log('trigger click');
4000         
4001         var target = e.getTarget();
4002         
4003         Roo.log(target.nodeName.toLowerCase());
4004         
4005         if(target.nodeName.toLowerCase() === 'i'){
4006             e.preventDefault();
4007         }
4008         
4009     },
4010     
4011     onTriggerPress  : function(e)
4012     {
4013         Roo.log('trigger press');
4014         //Roo.log(e.getTarget());
4015        // Roo.log(this.triggerEl.dom);
4016        
4017         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
4018         var pel = Roo.get(e.getTarget());
4019         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
4020             Roo.log('is treeview or dropdown?');
4021             return;
4022         }
4023         
4024         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
4025             return;
4026         }
4027         
4028         if (this.isVisible()) {
4029             Roo.log('hide');
4030             this.hide();
4031         } else {
4032             Roo.log('show');
4033             
4034             this.show(this.triggerEl, this.align, false);
4035         }
4036         
4037         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4038             e.stopEvent();
4039         }
4040         
4041     },
4042        
4043     
4044     hideMenuItems : function()
4045     {
4046         Roo.log("hide Menu Items");
4047         if (!this.el) { 
4048             return;
4049         }
4050         
4051         this.el.select('.open',true).each(function(aa) {
4052             
4053             aa.removeClass('open');
4054          
4055         });
4056     },
4057     addxtypeChild : function (tree, cntr) {
4058         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4059           
4060         this.menuitems.add(comp);
4061         return comp;
4062
4063     },
4064     getEl : function()
4065     {
4066         Roo.log(this.el);
4067         return this.el;
4068     },
4069     
4070     clear : function()
4071     {
4072         this.getEl().dom.innerHTML = '';
4073         this.menuitems.clear();
4074     }
4075 });
4076
4077  
4078  /*
4079  * - LGPL
4080  *
4081  * menu item
4082  * 
4083  */
4084
4085
4086 /**
4087  * @class Roo.bootstrap.MenuItem
4088  * @extends Roo.bootstrap.Component
4089  * Bootstrap MenuItem class
4090  * @cfg {String} html the menu label
4091  * @cfg {String} href the link
4092  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4093  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4094  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4095  * @cfg {String} fa favicon to show on left of menu item.
4096  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4097  * 
4098  * 
4099  * @constructor
4100  * Create a new MenuItem
4101  * @param {Object} config The config object
4102  */
4103
4104
4105 Roo.bootstrap.MenuItem = function(config){
4106     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4107     this.addEvents({
4108         // raw events
4109         /**
4110          * @event click
4111          * The raw click event for the entire grid.
4112          * @param {Roo.bootstrap.MenuItem} this
4113          * @param {Roo.EventObject} e
4114          */
4115         "click" : true
4116     });
4117 };
4118
4119 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4120     
4121     href : false,
4122     html : false,
4123     preventDefault: false,
4124     isContainer : false,
4125     active : false,
4126     fa: false,
4127     
4128     getAutoCreate : function(){
4129         
4130         if(this.isContainer){
4131             return {
4132                 tag: 'li',
4133                 cls: 'dropdown-menu-item '
4134             };
4135         }
4136         var ctag = {
4137             tag: 'span',
4138             html: 'Link'
4139         };
4140         
4141         var anc = {
4142             tag : 'a',
4143             cls : 'dropdown-item',
4144             href : '#',
4145             cn : [  ]
4146         };
4147         
4148         if (this.fa !== false) {
4149             anc.cn.push({
4150                 tag : 'i',
4151                 cls : 'fa fa-' + this.fa
4152             });
4153         }
4154         
4155         anc.cn.push(ctag);
4156         
4157         
4158         var cfg= {
4159             tag: 'li',
4160             cls: 'dropdown-menu-item',
4161             cn: [ anc ]
4162         };
4163         if (this.parent().type == 'treeview') {
4164             cfg.cls = 'treeview-menu';
4165         }
4166         if (this.active) {
4167             cfg.cls += ' active';
4168         }
4169         
4170         
4171         
4172         anc.href = this.href || cfg.cn[0].href ;
4173         ctag.html = this.html || cfg.cn[0].html ;
4174         return cfg;
4175     },
4176     
4177     initEvents: function()
4178     {
4179         if (this.parent().type == 'treeview') {
4180             this.el.select('a').on('click', this.onClick, this);
4181         }
4182         
4183         if (this.menu) {
4184             this.menu.parentType = this.xtype;
4185             this.menu.triggerEl = this.el;
4186             this.menu = this.addxtype(Roo.apply({}, this.menu));
4187         }
4188         
4189     },
4190     onClick : function(e)
4191     {
4192         Roo.log('item on click ');
4193         
4194         if(this.preventDefault){
4195             e.preventDefault();
4196         }
4197         //this.parent().hideMenuItems();
4198         
4199         this.fireEvent('click', this, e);
4200     },
4201     getEl : function()
4202     {
4203         return this.el;
4204     } 
4205 });
4206
4207  
4208
4209  /*
4210  * - LGPL
4211  *
4212  * menu separator
4213  * 
4214  */
4215
4216
4217 /**
4218  * @class Roo.bootstrap.MenuSeparator
4219  * @extends Roo.bootstrap.Component
4220  * Bootstrap MenuSeparator class
4221  * 
4222  * @constructor
4223  * Create a new MenuItem
4224  * @param {Object} config The config object
4225  */
4226
4227
4228 Roo.bootstrap.MenuSeparator = function(config){
4229     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4230 };
4231
4232 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4233     
4234     getAutoCreate : function(){
4235         var cfg = {
4236             cls: 'divider',
4237             tag : 'li'
4238         };
4239         
4240         return cfg;
4241     }
4242    
4243 });
4244
4245  
4246
4247  
4248 /*
4249 * Licence: LGPL
4250 */
4251
4252 /**
4253  * @class Roo.bootstrap.Modal
4254  * @extends Roo.bootstrap.Component
4255  * Bootstrap Modal class
4256  * @cfg {String} title Title of dialog
4257  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4258  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4259  * @cfg {Boolean} specificTitle default false
4260  * @cfg {Array} buttons Array of buttons or standard button set..
4261  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4262  * @cfg {Boolean} animate default true
4263  * @cfg {Boolean} allow_close default true
4264  * @cfg {Boolean} fitwindow default false
4265  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4266  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4267  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4268  * @cfg {String} size (sm|lg|xl) default empty
4269  * @cfg {Number} max_width set the max width of modal
4270  * @cfg {Boolean} editableTitle can the title be edited
4271
4272  *
4273  *
4274  * @constructor
4275  * Create a new Modal Dialog
4276  * @param {Object} config The config object
4277  */
4278
4279 Roo.bootstrap.Modal = function(config){
4280     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4281     this.addEvents({
4282         // raw events
4283         /**
4284          * @event btnclick
4285          * The raw btnclick event for the button
4286          * @param {Roo.EventObject} e
4287          */
4288         "btnclick" : true,
4289         /**
4290          * @event resize
4291          * Fire when dialog resize
4292          * @param {Roo.bootstrap.Modal} this
4293          * @param {Roo.EventObject} e
4294          */
4295         "resize" : true,
4296         /**
4297          * @event titlechanged
4298          * Fire when the editable title has been changed
4299          * @param {Roo.bootstrap.Modal} this
4300          * @param {Roo.EventObject} value
4301          */
4302         "titlechanged" : true 
4303         
4304     });
4305     this.buttons = this.buttons || [];
4306
4307     if (this.tmpl) {
4308         this.tmpl = Roo.factory(this.tmpl);
4309     }
4310
4311 };
4312
4313 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4314
4315     title : 'test dialog',
4316
4317     buttons : false,
4318
4319     // set on load...
4320
4321     html: false,
4322
4323     tmp: false,
4324
4325     specificTitle: false,
4326
4327     buttonPosition: 'right',
4328
4329     allow_close : true,
4330
4331     animate : true,
4332
4333     fitwindow: false,
4334     
4335      // private
4336     dialogEl: false,
4337     bodyEl:  false,
4338     footerEl:  false,
4339     titleEl:  false,
4340     closeEl:  false,
4341
4342     size: '',
4343     
4344     max_width: 0,
4345     
4346     max_height: 0,
4347     
4348     fit_content: false,
4349     editableTitle  : false,
4350
4351     onRender : function(ct, position)
4352     {
4353         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4354
4355         if(!this.el){
4356             var cfg = Roo.apply({},  this.getAutoCreate());
4357             cfg.id = Roo.id();
4358             //if(!cfg.name){
4359             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4360             //}
4361             //if (!cfg.name.length) {
4362             //    delete cfg.name;
4363            // }
4364             if (this.cls) {
4365                 cfg.cls += ' ' + this.cls;
4366             }
4367             if (this.style) {
4368                 cfg.style = this.style;
4369             }
4370             this.el = Roo.get(document.body).createChild(cfg, position);
4371         }
4372         //var type = this.el.dom.type;
4373
4374
4375         if(this.tabIndex !== undefined){
4376             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4377         }
4378
4379         this.dialogEl = this.el.select('.modal-dialog',true).first();
4380         this.bodyEl = this.el.select('.modal-body',true).first();
4381         this.closeEl = this.el.select('.modal-header .close', true).first();
4382         this.headerEl = this.el.select('.modal-header',true).first();
4383         this.titleEl = this.el.select('.modal-title',true).first();
4384         this.footerEl = this.el.select('.modal-footer',true).first();
4385
4386         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4387         
4388         //this.el.addClass("x-dlg-modal");
4389
4390         if (this.buttons.length) {
4391             Roo.each(this.buttons, function(bb) {
4392                 var b = Roo.apply({}, bb);
4393                 b.xns = b.xns || Roo.bootstrap;
4394                 b.xtype = b.xtype || 'Button';
4395                 if (typeof(b.listeners) == 'undefined') {
4396                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4397                 }
4398
4399                 var btn = Roo.factory(b);
4400
4401                 btn.render(this.getButtonContainer());
4402
4403             },this);
4404         }
4405         // render the children.
4406         var nitems = [];
4407
4408         if(typeof(this.items) != 'undefined'){
4409             var items = this.items;
4410             delete this.items;
4411
4412             for(var i =0;i < items.length;i++) {
4413                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4414             }
4415         }
4416
4417         this.items = nitems;
4418
4419         // where are these used - they used to be body/close/footer
4420
4421
4422         this.initEvents();
4423         //this.el.addClass([this.fieldClass, this.cls]);
4424
4425     },
4426
4427     getAutoCreate : function()
4428     {
4429         // we will default to modal-body-overflow - might need to remove or make optional later.
4430         var bdy = {
4431                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4432                 html : this.html || ''
4433         };
4434
4435         var title = {
4436             tag: 'h5',
4437             cls : 'modal-title',
4438             html : this.title
4439         };
4440
4441         if(this.specificTitle){ // WTF is this?
4442             title = this.title;
4443         }
4444
4445         var header = [];
4446         if (this.allow_close && Roo.bootstrap.version == 3) {
4447             header.push({
4448                 tag: 'button',
4449                 cls : 'close',
4450                 html : '&times'
4451             });
4452         }
4453
4454         header.push(title);
4455
4456         if (this.editableTitle) {
4457             header.push({
4458                 cls: 'form-control roo-editable-title d-none',
4459                 tag: 'input',
4460                 type: 'text'
4461             });
4462         }
4463         
4464         if (this.allow_close && Roo.bootstrap.version == 4) {
4465             header.push({
4466                 tag: 'button',
4467                 cls : 'close',
4468                 html : '&times'
4469             });
4470         }
4471         
4472         var size = '';
4473
4474         if(this.size.length){
4475             size = 'modal-' + this.size;
4476         }
4477         
4478         var footer = Roo.bootstrap.version == 3 ?
4479             {
4480                 cls : 'modal-footer',
4481                 cn : [
4482                     {
4483                         tag: 'div',
4484                         cls: 'btn-' + this.buttonPosition
4485                     }
4486                 ]
4487
4488             } :
4489             {  // BS4 uses mr-auto on left buttons....
4490                 cls : 'modal-footer'
4491             };
4492
4493             
4494
4495         
4496         
4497         var modal = {
4498             cls: "modal",
4499              cn : [
4500                 {
4501                     cls: "modal-dialog " + size,
4502                     cn : [
4503                         {
4504                             cls : "modal-content",
4505                             cn : [
4506                                 {
4507                                     cls : 'modal-header',
4508                                     cn : header
4509                                 },
4510                                 bdy,
4511                                 footer
4512                             ]
4513
4514                         }
4515                     ]
4516
4517                 }
4518             ]
4519         };
4520
4521         if(this.animate){
4522             modal.cls += ' fade';
4523         }
4524
4525         return modal;
4526
4527     },
4528     getChildContainer : function() {
4529
4530          return this.bodyEl;
4531
4532     },
4533     getButtonContainer : function() {
4534         
4535          return Roo.bootstrap.version == 4 ?
4536             this.el.select('.modal-footer',true).first()
4537             : this.el.select('.modal-footer div',true).first();
4538
4539     },
4540     initEvents : function()
4541     {
4542         if (this.allow_close) {
4543             this.closeEl.on('click', this.hide, this);
4544         }
4545         Roo.EventManager.onWindowResize(this.resize, this, true);
4546         if (this.editableTitle) {
4547             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4548             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4549             this.headerEditEl.on('keyup', function(e) {
4550                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4551                         this.toggleHeaderInput(false)
4552                     }
4553                 }, this);
4554             this.headerEditEl.on('blur', function(e) {
4555                 this.toggleHeaderInput(false)
4556             },this);
4557         }
4558
4559     },
4560   
4561
4562     resize : function()
4563     {
4564         this.maskEl.setSize(
4565             Roo.lib.Dom.getViewWidth(true),
4566             Roo.lib.Dom.getViewHeight(true)
4567         );
4568         
4569         if (this.fitwindow) {
4570             
4571            this.dialogEl.setStyle( { 'max-width' : '100%' });
4572             this.setSize(
4573                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4574                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4575             );
4576             return;
4577         }
4578         
4579         if(this.max_width !== 0) {
4580             
4581             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4582             
4583             if(this.height) {
4584                 this.setSize(w, this.height);
4585                 return;
4586             }
4587             
4588             if(this.max_height) {
4589                 this.setSize(w,Math.min(
4590                     this.max_height,
4591                     Roo.lib.Dom.getViewportHeight(true) - 60
4592                 ));
4593                 
4594                 return;
4595             }
4596             
4597             if(!this.fit_content) {
4598                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4599                 return;
4600             }
4601             
4602             this.setSize(w, Math.min(
4603                 60 +
4604                 this.headerEl.getHeight() + 
4605                 this.footerEl.getHeight() + 
4606                 this.getChildHeight(this.bodyEl.dom.childNodes),
4607                 Roo.lib.Dom.getViewportHeight(true) - 60)
4608             );
4609         }
4610         
4611     },
4612
4613     setSize : function(w,h)
4614     {
4615         if (!w && !h) {
4616             return;
4617         }
4618         
4619         this.resizeTo(w,h);
4620     },
4621
4622     show : function() {
4623
4624         if (!this.rendered) {
4625             this.render();
4626         }
4627         this.toggleHeaderInput(false);
4628         //this.el.setStyle('display', 'block');
4629         this.el.removeClass('hideing');
4630         this.el.dom.style.display='block';
4631         
4632         Roo.get(document.body).addClass('modal-open');
4633  
4634         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4635             
4636             (function(){
4637                 this.el.addClass('show');
4638                 this.el.addClass('in');
4639             }).defer(50, this);
4640         }else{
4641             this.el.addClass('show');
4642             this.el.addClass('in');
4643         }
4644
4645         // not sure how we can show data in here..
4646         //if (this.tmpl) {
4647         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4648         //}
4649
4650         Roo.get(document.body).addClass("x-body-masked");
4651         
4652         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4653         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4654         this.maskEl.dom.style.display = 'block';
4655         this.maskEl.addClass('show');
4656         
4657         
4658         this.resize();
4659         
4660         this.fireEvent('show', this);
4661
4662         // set zindex here - otherwise it appears to be ignored...
4663         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4664
4665         (function () {
4666             this.items.forEach( function(e) {
4667                 e.layout ? e.layout() : false;
4668
4669             });
4670         }).defer(100,this);
4671
4672     },
4673     hide : function()
4674     {
4675         if(this.fireEvent("beforehide", this) !== false){
4676             
4677             this.maskEl.removeClass('show');
4678             
4679             this.maskEl.dom.style.display = '';
4680             Roo.get(document.body).removeClass("x-body-masked");
4681             this.el.removeClass('in');
4682             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4683
4684             if(this.animate){ // why
4685                 this.el.addClass('hideing');
4686                 this.el.removeClass('show');
4687                 (function(){
4688                     if (!this.el.hasClass('hideing')) {
4689                         return; // it's been shown again...
4690                     }
4691                     
4692                     this.el.dom.style.display='';
4693
4694                     Roo.get(document.body).removeClass('modal-open');
4695                     this.el.removeClass('hideing');
4696                 }).defer(150,this);
4697                 
4698             }else{
4699                 this.el.removeClass('show');
4700                 this.el.dom.style.display='';
4701                 Roo.get(document.body).removeClass('modal-open');
4702
4703             }
4704             this.fireEvent('hide', this);
4705         }
4706     },
4707     isVisible : function()
4708     {
4709         
4710         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4711         
4712     },
4713
4714     addButton : function(str, cb)
4715     {
4716
4717
4718         var b = Roo.apply({}, { html : str } );
4719         b.xns = b.xns || Roo.bootstrap;
4720         b.xtype = b.xtype || 'Button';
4721         if (typeof(b.listeners) == 'undefined') {
4722             b.listeners = { click : cb.createDelegate(this)  };
4723         }
4724
4725         var btn = Roo.factory(b);
4726
4727         btn.render(this.getButtonContainer());
4728
4729         return btn;
4730
4731     },
4732
4733     setDefaultButton : function(btn)
4734     {
4735         //this.el.select('.modal-footer').()
4736     },
4737
4738     resizeTo: function(w,h)
4739     {
4740         this.dialogEl.setWidth(w);
4741         
4742         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4743
4744         this.bodyEl.setHeight(h - diff);
4745         
4746         this.fireEvent('resize', this);
4747     },
4748     
4749     setContentSize  : function(w, h)
4750     {
4751
4752     },
4753     onButtonClick: function(btn,e)
4754     {
4755         //Roo.log([a,b,c]);
4756         this.fireEvent('btnclick', btn.name, e);
4757     },
4758      /**
4759      * Set the title of the Dialog
4760      * @param {String} str new Title
4761      */
4762     setTitle: function(str) {
4763         this.titleEl.dom.innerHTML = str;
4764         this.title = str;
4765     },
4766     /**
4767      * Set the body of the Dialog
4768      * @param {String} str new Title
4769      */
4770     setBody: function(str) {
4771         this.bodyEl.dom.innerHTML = str;
4772     },
4773     /**
4774      * Set the body of the Dialog using the template
4775      * @param {Obj} data - apply this data to the template and replace the body contents.
4776      */
4777     applyBody: function(obj)
4778     {
4779         if (!this.tmpl) {
4780             Roo.log("Error - using apply Body without a template");
4781             //code
4782         }
4783         this.tmpl.overwrite(this.bodyEl, obj);
4784     },
4785     
4786     getChildHeight : function(child_nodes)
4787     {
4788         if(
4789             !child_nodes ||
4790             child_nodes.length == 0
4791         ) {
4792             return 0;
4793         }
4794         
4795         var child_height = 0;
4796         
4797         for(var i = 0; i < child_nodes.length; i++) {
4798             
4799             /*
4800             * for modal with tabs...
4801             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4802                 
4803                 var layout_childs = child_nodes[i].childNodes;
4804                 
4805                 for(var j = 0; j < layout_childs.length; j++) {
4806                     
4807                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4808                         
4809                         var layout_body_childs = layout_childs[j].childNodes;
4810                         
4811                         for(var k = 0; k < layout_body_childs.length; k++) {
4812                             
4813                             if(layout_body_childs[k].classList.contains('navbar')) {
4814                                 child_height += layout_body_childs[k].offsetHeight;
4815                                 continue;
4816                             }
4817                             
4818                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4819                                 
4820                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4821                                 
4822                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4823                                     
4824                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4825                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4826                                         continue;
4827                                     }
4828                                     
4829                                 }
4830                                 
4831                             }
4832                             
4833                         }
4834                     }
4835                 }
4836                 continue;
4837             }
4838             */
4839             
4840             child_height += child_nodes[i].offsetHeight;
4841             // Roo.log(child_nodes[i].offsetHeight);
4842         }
4843         
4844         return child_height;
4845     },
4846     toggleHeaderInput : function(is_edit)
4847     {
4848         if (!this.editableTitle) {
4849             return; // not editable.
4850         }
4851         if (is_edit && this.is_header_editing) {
4852             return; // already editing..
4853         }
4854         if (is_edit) {
4855     
4856             this.headerEditEl.dom.value = this.title;
4857             this.headerEditEl.removeClass('d-none');
4858             this.headerEditEl.dom.focus();
4859             this.titleEl.addClass('d-none');
4860             
4861             this.is_header_editing = true;
4862             return
4863         }
4864         // flip back to not editing.
4865         this.title = this.headerEditEl.dom.value;
4866         this.headerEditEl.addClass('d-none');
4867         this.titleEl.removeClass('d-none');
4868         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4869         this.is_header_editing = false;
4870         this.fireEvent('titlechanged', this, this.title);
4871     
4872             
4873         
4874     }
4875
4876 });
4877
4878
4879 Roo.apply(Roo.bootstrap.Modal,  {
4880     /**
4881          * Button config that displays a single OK button
4882          * @type Object
4883          */
4884         OK :  [{
4885             name : 'ok',
4886             weight : 'primary',
4887             html : 'OK'
4888         }],
4889         /**
4890          * Button config that displays Yes and No buttons
4891          * @type Object
4892          */
4893         YESNO : [
4894             {
4895                 name  : 'no',
4896                 html : 'No'
4897             },
4898             {
4899                 name  :'yes',
4900                 weight : 'primary',
4901                 html : 'Yes'
4902             }
4903         ],
4904
4905         /**
4906          * Button config that displays OK and Cancel buttons
4907          * @type Object
4908          */
4909         OKCANCEL : [
4910             {
4911                name : 'cancel',
4912                 html : 'Cancel'
4913             },
4914             {
4915                 name : 'ok',
4916                 weight : 'primary',
4917                 html : 'OK'
4918             }
4919         ],
4920         /**
4921          * Button config that displays Yes, No and Cancel buttons
4922          * @type Object
4923          */
4924         YESNOCANCEL : [
4925             {
4926                 name : 'yes',
4927                 weight : 'primary',
4928                 html : 'Yes'
4929             },
4930             {
4931                 name : 'no',
4932                 html : 'No'
4933             },
4934             {
4935                 name : 'cancel',
4936                 html : 'Cancel'
4937             }
4938         ],
4939         
4940         zIndex : 10001
4941 });
4942
4943 /*
4944  * - LGPL
4945  *
4946  * messagebox - can be used as a replace
4947  * 
4948  */
4949 /**
4950  * @class Roo.MessageBox
4951  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4952  * Example usage:
4953  *<pre><code>
4954 // Basic alert:
4955 Roo.Msg.alert('Status', 'Changes saved successfully.');
4956
4957 // Prompt for user data:
4958 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4959     if (btn == 'ok'){
4960         // process text value...
4961     }
4962 });
4963
4964 // Show a dialog using config options:
4965 Roo.Msg.show({
4966    title:'Save Changes?',
4967    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4968    buttons: Roo.Msg.YESNOCANCEL,
4969    fn: processResult,
4970    animEl: 'elId'
4971 });
4972 </code></pre>
4973  * @singleton
4974  */
4975 Roo.bootstrap.MessageBox = function(){
4976     var dlg, opt, mask, waitTimer;
4977     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4978     var buttons, activeTextEl, bwidth;
4979
4980     
4981     // private
4982     var handleButton = function(button){
4983         dlg.hide();
4984         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4985     };
4986
4987     // private
4988     var handleHide = function(){
4989         if(opt && opt.cls){
4990             dlg.el.removeClass(opt.cls);
4991         }
4992         //if(waitTimer){
4993         //    Roo.TaskMgr.stop(waitTimer);
4994         //    waitTimer = null;
4995         //}
4996     };
4997
4998     // private
4999     var updateButtons = function(b){
5000         var width = 0;
5001         if(!b){
5002             buttons["ok"].hide();
5003             buttons["cancel"].hide();
5004             buttons["yes"].hide();
5005             buttons["no"].hide();
5006             dlg.footerEl.hide();
5007             
5008             return width;
5009         }
5010         dlg.footerEl.show();
5011         for(var k in buttons){
5012             if(typeof buttons[k] != "function"){
5013                 if(b[k]){
5014                     buttons[k].show();
5015                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
5016                     width += buttons[k].el.getWidth()+15;
5017                 }else{
5018                     buttons[k].hide();
5019                 }
5020             }
5021         }
5022         return width;
5023     };
5024
5025     // private
5026     var handleEsc = function(d, k, e){
5027         if(opt && opt.closable !== false){
5028             dlg.hide();
5029         }
5030         if(e){
5031             e.stopEvent();
5032         }
5033     };
5034
5035     return {
5036         /**
5037          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5038          * @return {Roo.BasicDialog} The BasicDialog element
5039          */
5040         getDialog : function(){
5041            if(!dlg){
5042                 dlg = new Roo.bootstrap.Modal( {
5043                     //draggable: true,
5044                     //resizable:false,
5045                     //constraintoviewport:false,
5046                     //fixedcenter:true,
5047                     //collapsible : false,
5048                     //shim:true,
5049                     //modal: true,
5050                 //    width: 'auto',
5051                   //  height:100,
5052                     //buttonAlign:"center",
5053                     closeClick : function(){
5054                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5055                             handleButton("no");
5056                         }else{
5057                             handleButton("cancel");
5058                         }
5059                     }
5060                 });
5061                 dlg.render();
5062                 dlg.on("hide", handleHide);
5063                 mask = dlg.mask;
5064                 //dlg.addKeyListener(27, handleEsc);
5065                 buttons = {};
5066                 this.buttons = buttons;
5067                 var bt = this.buttonText;
5068                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5069                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5070                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5071                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5072                 //Roo.log(buttons);
5073                 bodyEl = dlg.bodyEl.createChild({
5074
5075                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5076                         '<textarea class="roo-mb-textarea"></textarea>' +
5077                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5078                 });
5079                 msgEl = bodyEl.dom.firstChild;
5080                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5081                 textboxEl.enableDisplayMode();
5082                 textboxEl.addKeyListener([10,13], function(){
5083                     if(dlg.isVisible() && opt && opt.buttons){
5084                         if(opt.buttons.ok){
5085                             handleButton("ok");
5086                         }else if(opt.buttons.yes){
5087                             handleButton("yes");
5088                         }
5089                     }
5090                 });
5091                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5092                 textareaEl.enableDisplayMode();
5093                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5094                 progressEl.enableDisplayMode();
5095                 
5096                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5097                 var pf = progressEl.dom.firstChild;
5098                 if (pf) {
5099                     pp = Roo.get(pf.firstChild);
5100                     pp.setHeight(pf.offsetHeight);
5101                 }
5102                 
5103             }
5104             return dlg;
5105         },
5106
5107         /**
5108          * Updates the message box body text
5109          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5110          * the XHTML-compliant non-breaking space character '&amp;#160;')
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         updateText : function(text)
5114         {
5115             if(!dlg.isVisible() && !opt.width){
5116                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5117                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5118             }
5119             msgEl.innerHTML = text || '&#160;';
5120       
5121             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5122             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5123             var w = Math.max(
5124                     Math.min(opt.width || cw , this.maxWidth), 
5125                     Math.max(opt.minWidth || this.minWidth, bwidth)
5126             );
5127             if(opt.prompt){
5128                 activeTextEl.setWidth(w);
5129             }
5130             if(dlg.isVisible()){
5131                 dlg.fixedcenter = false;
5132             }
5133             // to big, make it scroll. = But as usual stupid IE does not support
5134             // !important..
5135             
5136             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5137                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5138                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5139             } else {
5140                 bodyEl.dom.style.height = '';
5141                 bodyEl.dom.style.overflowY = '';
5142             }
5143             if (cw > w) {
5144                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5145             } else {
5146                 bodyEl.dom.style.overflowX = '';
5147             }
5148             
5149             dlg.setContentSize(w, bodyEl.getHeight());
5150             if(dlg.isVisible()){
5151                 dlg.fixedcenter = true;
5152             }
5153             return this;
5154         },
5155
5156         /**
5157          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5158          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5159          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5160          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5161          * @return {Roo.MessageBox} This message box
5162          */
5163         updateProgress : function(value, text){
5164             if(text){
5165                 this.updateText(text);
5166             }
5167             
5168             if (pp) { // weird bug on my firefox - for some reason this is not defined
5169                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5170                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5171             }
5172             return this;
5173         },        
5174
5175         /**
5176          * Returns true if the message box is currently displayed
5177          * @return {Boolean} True if the message box is visible, else false
5178          */
5179         isVisible : function(){
5180             return dlg && dlg.isVisible();  
5181         },
5182
5183         /**
5184          * Hides the message box if it is displayed
5185          */
5186         hide : function(){
5187             if(this.isVisible()){
5188                 dlg.hide();
5189             }  
5190         },
5191
5192         /**
5193          * Displays a new message box, or reinitializes an existing message box, based on the config options
5194          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5195          * The following config object properties are supported:
5196          * <pre>
5197 Property    Type             Description
5198 ----------  ---------------  ------------------------------------------------------------------------------------
5199 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5200                                    closes (defaults to undefined)
5201 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5202                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5203 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5204                                    progress and wait dialogs will ignore this property and always hide the
5205                                    close button as they can only be closed programmatically.
5206 cls               String           A custom CSS class to apply to the message box element
5207 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5208                                    displayed (defaults to 75)
5209 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5210                                    function will be btn (the name of the button that was clicked, if applicable,
5211                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5212                                    Progress and wait dialogs will ignore this option since they do not respond to
5213                                    user actions and can only be closed programmatically, so any required function
5214                                    should be called by the same code after it closes the dialog.
5215 icon              String           A CSS class that provides a background image to be used as an icon for
5216                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5217 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5218 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5219 modal             Boolean          False to allow user interaction with the page while the message box is
5220                                    displayed (defaults to true)
5221 msg               String           A string that will replace the existing message box body text (defaults
5222                                    to the XHTML-compliant non-breaking space character '&#160;')
5223 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5224 progress          Boolean          True to display a progress bar (defaults to false)
5225 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5226 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5227 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5228 title             String           The title text
5229 value             String           The string value to set into the active textbox element if displayed
5230 wait              Boolean          True to display a progress bar (defaults to false)
5231 width             Number           The width of the dialog in pixels
5232 </pre>
5233          *
5234          * Example usage:
5235          * <pre><code>
5236 Roo.Msg.show({
5237    title: 'Address',
5238    msg: 'Please enter your address:',
5239    width: 300,
5240    buttons: Roo.MessageBox.OKCANCEL,
5241    multiline: true,
5242    fn: saveAddress,
5243    animEl: 'addAddressBtn'
5244 });
5245 </code></pre>
5246          * @param {Object} config Configuration options
5247          * @return {Roo.MessageBox} This message box
5248          */
5249         show : function(options)
5250         {
5251             
5252             // this causes nightmares if you show one dialog after another
5253             // especially on callbacks..
5254              
5255             if(this.isVisible()){
5256                 
5257                 this.hide();
5258                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5259                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5260                 Roo.log("New Dialog Message:" +  options.msg )
5261                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5262                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5263                 
5264             }
5265             var d = this.getDialog();
5266             opt = options;
5267             d.setTitle(opt.title || "&#160;");
5268             d.closeEl.setDisplayed(opt.closable !== false);
5269             activeTextEl = textboxEl;
5270             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5271             if(opt.prompt){
5272                 if(opt.multiline){
5273                     textboxEl.hide();
5274                     textareaEl.show();
5275                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5276                         opt.multiline : this.defaultTextHeight);
5277                     activeTextEl = textareaEl;
5278                 }else{
5279                     textboxEl.show();
5280                     textareaEl.hide();
5281                 }
5282             }else{
5283                 textboxEl.hide();
5284                 textareaEl.hide();
5285             }
5286             progressEl.setDisplayed(opt.progress === true);
5287             if (opt.progress) {
5288                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5289             }
5290             this.updateProgress(0);
5291             activeTextEl.dom.value = opt.value || "";
5292             if(opt.prompt){
5293                 dlg.setDefaultButton(activeTextEl);
5294             }else{
5295                 var bs = opt.buttons;
5296                 var db = null;
5297                 if(bs && bs.ok){
5298                     db = buttons["ok"];
5299                 }else if(bs && bs.yes){
5300                     db = buttons["yes"];
5301                 }
5302                 dlg.setDefaultButton(db);
5303             }
5304             bwidth = updateButtons(opt.buttons);
5305             this.updateText(opt.msg);
5306             if(opt.cls){
5307                 d.el.addClass(opt.cls);
5308             }
5309             d.proxyDrag = opt.proxyDrag === true;
5310             d.modal = opt.modal !== false;
5311             d.mask = opt.modal !== false ? mask : false;
5312             if(!d.isVisible()){
5313                 // force it to the end of the z-index stack so it gets a cursor in FF
5314                 document.body.appendChild(dlg.el.dom);
5315                 d.animateTarget = null;
5316                 d.show(options.animEl);
5317             }
5318             return this;
5319         },
5320
5321         /**
5322          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5323          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5324          * and closing the message box when the process is complete.
5325          * @param {String} title The title bar text
5326          * @param {String} msg The message box body text
5327          * @return {Roo.MessageBox} This message box
5328          */
5329         progress : function(title, msg){
5330             this.show({
5331                 title : title,
5332                 msg : msg,
5333                 buttons: false,
5334                 progress:true,
5335                 closable:false,
5336                 minWidth: this.minProgressWidth,
5337                 modal : true
5338             });
5339             return this;
5340         },
5341
5342         /**
5343          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5344          * If a callback function is passed it will be called after the user clicks the button, and the
5345          * id of the button that was clicked will be passed as the only parameter to the callback
5346          * (could also be the top-right close button).
5347          * @param {String} title The title bar text
5348          * @param {String} msg The message box body text
5349          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5350          * @param {Object} scope (optional) The scope of the callback function
5351          * @return {Roo.MessageBox} This message box
5352          */
5353         alert : function(title, msg, fn, scope)
5354         {
5355             this.show({
5356                 title : title,
5357                 msg : msg,
5358                 buttons: this.OK,
5359                 fn: fn,
5360                 closable : false,
5361                 scope : scope,
5362                 modal : true
5363             });
5364             return this;
5365         },
5366
5367         /**
5368          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5369          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5370          * You are responsible for closing the message box when the process is complete.
5371          * @param {String} msg The message box body text
5372          * @param {String} title (optional) The title bar text
5373          * @return {Roo.MessageBox} This message box
5374          */
5375         wait : function(msg, title){
5376             this.show({
5377                 title : title,
5378                 msg : msg,
5379                 buttons: false,
5380                 closable:false,
5381                 progress:true,
5382                 modal:true,
5383                 width:300,
5384                 wait:true
5385             });
5386             waitTimer = Roo.TaskMgr.start({
5387                 run: function(i){
5388                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5389                 },
5390                 interval: 1000
5391             });
5392             return this;
5393         },
5394
5395         /**
5396          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5397          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5398          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5399          * @param {String} title The title bar text
5400          * @param {String} msg The message box body text
5401          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5402          * @param {Object} scope (optional) The scope of the callback function
5403          * @return {Roo.MessageBox} This message box
5404          */
5405         confirm : function(title, msg, fn, scope){
5406             this.show({
5407                 title : title,
5408                 msg : msg,
5409                 buttons: this.YESNO,
5410                 fn: fn,
5411                 scope : scope,
5412                 modal : true
5413             });
5414             return this;
5415         },
5416
5417         /**
5418          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5419          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5420          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5421          * (could also be the top-right close button) and the text that was entered will be passed as the two
5422          * parameters to the callback.
5423          * @param {String} title The title bar text
5424          * @param {String} msg The message box body text
5425          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5426          * @param {Object} scope (optional) The scope of the callback function
5427          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5428          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5429          * @return {Roo.MessageBox} This message box
5430          */
5431         prompt : function(title, msg, fn, scope, multiline){
5432             this.show({
5433                 title : title,
5434                 msg : msg,
5435                 buttons: this.OKCANCEL,
5436                 fn: fn,
5437                 minWidth:250,
5438                 scope : scope,
5439                 prompt:true,
5440                 multiline: multiline,
5441                 modal : true
5442             });
5443             return this;
5444         },
5445
5446         /**
5447          * Button config that displays a single OK button
5448          * @type Object
5449          */
5450         OK : {ok:true},
5451         /**
5452          * Button config that displays Yes and No buttons
5453          * @type Object
5454          */
5455         YESNO : {yes:true, no:true},
5456         /**
5457          * Button config that displays OK and Cancel buttons
5458          * @type Object
5459          */
5460         OKCANCEL : {ok:true, cancel:true},
5461         /**
5462          * Button config that displays Yes, No and Cancel buttons
5463          * @type Object
5464          */
5465         YESNOCANCEL : {yes:true, no:true, cancel:true},
5466
5467         /**
5468          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5469          * @type Number
5470          */
5471         defaultTextHeight : 75,
5472         /**
5473          * The maximum width in pixels of the message box (defaults to 600)
5474          * @type Number
5475          */
5476         maxWidth : 600,
5477         /**
5478          * The minimum width in pixels of the message box (defaults to 100)
5479          * @type Number
5480          */
5481         minWidth : 100,
5482         /**
5483          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5484          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5485          * @type Number
5486          */
5487         minProgressWidth : 250,
5488         /**
5489          * An object containing the default button text strings that can be overriden for localized language support.
5490          * Supported properties are: ok, cancel, yes and no.
5491          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5492          * @type Object
5493          */
5494         buttonText : {
5495             ok : "OK",
5496             cancel : "Cancel",
5497             yes : "Yes",
5498             no : "No"
5499         }
5500     };
5501 }();
5502
5503 /**
5504  * Shorthand for {@link Roo.MessageBox}
5505  */
5506 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5507 Roo.Msg = Roo.Msg || Roo.MessageBox;
5508 /*
5509  * - LGPL
5510  *
5511  * navbar
5512  * 
5513  */
5514
5515 /**
5516  * @class Roo.bootstrap.Navbar
5517  * @extends Roo.bootstrap.Component
5518  * Bootstrap Navbar class
5519
5520  * @constructor
5521  * Create a new Navbar
5522  * @param {Object} config The config object
5523  */
5524
5525
5526 Roo.bootstrap.Navbar = function(config){
5527     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5528     this.addEvents({
5529         // raw events
5530         /**
5531          * @event beforetoggle
5532          * Fire before toggle the menu
5533          * @param {Roo.EventObject} e
5534          */
5535         "beforetoggle" : true
5536     });
5537 };
5538
5539 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5540     
5541     
5542    
5543     // private
5544     navItems : false,
5545     loadMask : false,
5546     
5547     
5548     getAutoCreate : function(){
5549         
5550         
5551         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5552         
5553     },
5554     
5555     initEvents :function ()
5556     {
5557         //Roo.log(this.el.select('.navbar-toggle',true));
5558         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5559         
5560         var mark = {
5561             tag: "div",
5562             cls:"x-dlg-mask"
5563         };
5564         
5565         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5566         
5567         var size = this.el.getSize();
5568         this.maskEl.setSize(size.width, size.height);
5569         this.maskEl.enableDisplayMode("block");
5570         this.maskEl.hide();
5571         
5572         if(this.loadMask){
5573             this.maskEl.show();
5574         }
5575     },
5576     
5577     
5578     getChildContainer : function()
5579     {
5580         if (this.el && this.el.select('.collapse').getCount()) {
5581             return this.el.select('.collapse',true).first();
5582         }
5583         
5584         return this.el;
5585     },
5586     
5587     mask : function()
5588     {
5589         this.maskEl.show();
5590     },
5591     
5592     unmask : function()
5593     {
5594         this.maskEl.hide();
5595     },
5596     onToggle : function()
5597     {
5598         
5599         if(this.fireEvent('beforetoggle', this) === false){
5600             return;
5601         }
5602         var ce = this.el.select('.navbar-collapse',true).first();
5603       
5604         if (!ce.hasClass('show')) {
5605            this.expand();
5606         } else {
5607             this.collapse();
5608         }
5609         
5610         
5611     
5612     },
5613     /**
5614      * Expand the navbar pulldown 
5615      */
5616     expand : function ()
5617     {
5618        
5619         var ce = this.el.select('.navbar-collapse',true).first();
5620         if (ce.hasClass('collapsing')) {
5621             return;
5622         }
5623         ce.dom.style.height = '';
5624                // show it...
5625         ce.addClass('in'); // old...
5626         ce.removeClass('collapse');
5627         ce.addClass('show');
5628         var h = ce.getHeight();
5629         Roo.log(h);
5630         ce.removeClass('show');
5631         // at this point we should be able to see it..
5632         ce.addClass('collapsing');
5633         
5634         ce.setHeight(0); // resize it ...
5635         ce.on('transitionend', function() {
5636             //Roo.log('done transition');
5637             ce.removeClass('collapsing');
5638             ce.addClass('show');
5639             ce.removeClass('collapse');
5640
5641             ce.dom.style.height = '';
5642         }, this, { single: true} );
5643         ce.setHeight(h);
5644         ce.dom.scrollTop = 0;
5645     },
5646     /**
5647      * Collapse the navbar pulldown 
5648      */
5649     collapse : function()
5650     {
5651          var ce = this.el.select('.navbar-collapse',true).first();
5652        
5653         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5654             // it's collapsed or collapsing..
5655             return;
5656         }
5657         ce.removeClass('in'); // old...
5658         ce.setHeight(ce.getHeight());
5659         ce.removeClass('show');
5660         ce.addClass('collapsing');
5661         
5662         ce.on('transitionend', function() {
5663             ce.dom.style.height = '';
5664             ce.removeClass('collapsing');
5665             ce.addClass('collapse');
5666         }, this, { single: true} );
5667         ce.setHeight(0);
5668     }
5669     
5670     
5671     
5672 });
5673
5674
5675
5676  
5677
5678  /*
5679  * - LGPL
5680  *
5681  * navbar
5682  * 
5683  */
5684
5685 /**
5686  * @class Roo.bootstrap.NavSimplebar
5687  * @extends Roo.bootstrap.Navbar
5688  * Bootstrap Sidebar class
5689  *
5690  * @cfg {Boolean} inverse is inverted color
5691  * 
5692  * @cfg {String} type (nav | pills | tabs)
5693  * @cfg {Boolean} arrangement stacked | justified
5694  * @cfg {String} align (left | right) alignment
5695  * 
5696  * @cfg {Boolean} main (true|false) main nav bar? default false
5697  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5698  * 
5699  * @cfg {String} tag (header|footer|nav|div) default is nav 
5700
5701  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5702  * 
5703  * 
5704  * @constructor
5705  * Create a new Sidebar
5706  * @param {Object} config The config object
5707  */
5708
5709
5710 Roo.bootstrap.NavSimplebar = function(config){
5711     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5712 };
5713
5714 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5715     
5716     inverse: false,
5717     
5718     type: false,
5719     arrangement: '',
5720     align : false,
5721     
5722     weight : 'light',
5723     
5724     main : false,
5725     
5726     
5727     tag : false,
5728     
5729     
5730     getAutoCreate : function(){
5731         
5732         
5733         var cfg = {
5734             tag : this.tag || 'div',
5735             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5736         };
5737         if (['light','white'].indexOf(this.weight) > -1) {
5738             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5739         }
5740         cfg.cls += ' bg-' + this.weight;
5741         
5742         if (this.inverse) {
5743             cfg.cls += ' navbar-inverse';
5744             
5745         }
5746         
5747         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5748         
5749         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5750             return cfg;
5751         }
5752         
5753         
5754     
5755         
5756         cfg.cn = [
5757             {
5758                 cls: 'nav nav-' + this.xtype,
5759                 tag : 'ul'
5760             }
5761         ];
5762         
5763          
5764         this.type = this.type || 'nav';
5765         if (['tabs','pills'].indexOf(this.type) != -1) {
5766             cfg.cn[0].cls += ' nav-' + this.type
5767         
5768         
5769         } else {
5770             if (this.type!=='nav') {
5771                 Roo.log('nav type must be nav/tabs/pills')
5772             }
5773             cfg.cn[0].cls += ' navbar-nav'
5774         }
5775         
5776         
5777         
5778         
5779         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5780             cfg.cn[0].cls += ' nav-' + this.arrangement;
5781         }
5782         
5783         
5784         if (this.align === 'right') {
5785             cfg.cn[0].cls += ' navbar-right';
5786         }
5787         
5788         
5789         
5790         
5791         return cfg;
5792     
5793         
5794     }
5795     
5796     
5797     
5798 });
5799
5800
5801
5802  
5803
5804  
5805        /*
5806  * - LGPL
5807  *
5808  * navbar
5809  * navbar-fixed-top
5810  * navbar-expand-md  fixed-top 
5811  */
5812
5813 /**
5814  * @class Roo.bootstrap.NavHeaderbar
5815  * @extends Roo.bootstrap.NavSimplebar
5816  * Bootstrap Sidebar class
5817  *
5818  * @cfg {String} brand what is brand
5819  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5820  * @cfg {String} brand_href href of the brand
5821  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5822  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5823  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5824  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5825  * 
5826  * @constructor
5827  * Create a new Sidebar
5828  * @param {Object} config The config object
5829  */
5830
5831
5832 Roo.bootstrap.NavHeaderbar = function(config){
5833     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5834       
5835 };
5836
5837 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5838     
5839     position: '',
5840     brand: '',
5841     brand_href: false,
5842     srButton : true,
5843     autohide : false,
5844     desktopCenter : false,
5845    
5846     
5847     getAutoCreate : function(){
5848         
5849         var   cfg = {
5850             tag: this.nav || 'nav',
5851             cls: 'navbar navbar-expand-md',
5852             role: 'navigation',
5853             cn: []
5854         };
5855         
5856         var cn = cfg.cn;
5857         if (this.desktopCenter) {
5858             cn.push({cls : 'container', cn : []});
5859             cn = cn[0].cn;
5860         }
5861         
5862         if(this.srButton){
5863             var btn = {
5864                 tag: 'button',
5865                 type: 'button',
5866                 cls: 'navbar-toggle navbar-toggler',
5867                 'data-toggle': 'collapse',
5868                 cn: [
5869                     {
5870                         tag: 'span',
5871                         cls: 'sr-only',
5872                         html: 'Toggle navigation'
5873                     },
5874                     {
5875                         tag: 'span',
5876                         cls: 'icon-bar navbar-toggler-icon'
5877                     },
5878                     {
5879                         tag: 'span',
5880                         cls: 'icon-bar'
5881                     },
5882                     {
5883                         tag: 'span',
5884                         cls: 'icon-bar'
5885                     }
5886                 ]
5887             };
5888             
5889             cn.push( Roo.bootstrap.version == 4 ? btn : {
5890                 tag: 'div',
5891                 cls: 'navbar-header',
5892                 cn: [
5893                     btn
5894                 ]
5895             });
5896         }
5897         
5898         cn.push({
5899             tag: 'div',
5900             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5901             cn : []
5902         });
5903         
5904         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5905         
5906         if (['light','white'].indexOf(this.weight) > -1) {
5907             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5908         }
5909         cfg.cls += ' bg-' + this.weight;
5910         
5911         
5912         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5913             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5914             
5915             // tag can override this..
5916             
5917             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5918         }
5919         
5920         if (this.brand !== '') {
5921             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5922             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5923                 tag: 'a',
5924                 href: this.brand_href ? this.brand_href : '#',
5925                 cls: 'navbar-brand',
5926                 cn: [
5927                 this.brand
5928                 ]
5929             });
5930         }
5931         
5932         if(this.main){
5933             cfg.cls += ' main-nav';
5934         }
5935         
5936         
5937         return cfg;
5938
5939         
5940     },
5941     getHeaderChildContainer : function()
5942     {
5943         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5944             return this.el.select('.navbar-header',true).first();
5945         }
5946         
5947         return this.getChildContainer();
5948     },
5949     
5950     getChildContainer : function()
5951     {
5952          
5953         return this.el.select('.roo-navbar-collapse',true).first();
5954          
5955         
5956     },
5957     
5958     initEvents : function()
5959     {
5960         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5961         
5962         if (this.autohide) {
5963             
5964             var prevScroll = 0;
5965             var ft = this.el;
5966             
5967             Roo.get(document).on('scroll',function(e) {
5968                 var ns = Roo.get(document).getScroll().top;
5969                 var os = prevScroll;
5970                 prevScroll = ns;
5971                 
5972                 if(ns > os){
5973                     ft.removeClass('slideDown');
5974                     ft.addClass('slideUp');
5975                     return;
5976                 }
5977                 ft.removeClass('slideUp');
5978                 ft.addClass('slideDown');
5979                  
5980               
5981           },this);
5982         }
5983     }    
5984     
5985 });
5986
5987
5988
5989  
5990
5991  /*
5992  * - LGPL
5993  *
5994  * navbar
5995  * 
5996  */
5997
5998 /**
5999  * @class Roo.bootstrap.NavSidebar
6000  * @extends Roo.bootstrap.Navbar
6001  * Bootstrap Sidebar class
6002  * 
6003  * @constructor
6004  * Create a new Sidebar
6005  * @param {Object} config The config object
6006  */
6007
6008
6009 Roo.bootstrap.NavSidebar = function(config){
6010     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
6011 };
6012
6013 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
6014     
6015     sidebar : true, // used by Navbar Item and NavbarGroup at present...
6016     
6017     getAutoCreate : function(){
6018         
6019         
6020         return  {
6021             tag: 'div',
6022             cls: 'sidebar sidebar-nav'
6023         };
6024     
6025         
6026     }
6027     
6028     
6029     
6030 });
6031
6032
6033
6034  
6035
6036  /*
6037  * - LGPL
6038  *
6039  * nav group
6040  * 
6041  */
6042
6043 /**
6044  * @class Roo.bootstrap.NavGroup
6045  * @extends Roo.bootstrap.Component
6046  * Bootstrap NavGroup class
6047  * @cfg {String} align (left|right)
6048  * @cfg {Boolean} inverse
6049  * @cfg {String} type (nav|pills|tab) default nav
6050  * @cfg {String} navId - reference Id for navbar.
6051  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6052  * 
6053  * @constructor
6054  * Create a new nav group
6055  * @param {Object} config The config object
6056  */
6057
6058 Roo.bootstrap.NavGroup = function(config){
6059     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6060     this.navItems = [];
6061    
6062     Roo.bootstrap.NavGroup.register(this);
6063      this.addEvents({
6064         /**
6065              * @event changed
6066              * Fires when the active item changes
6067              * @param {Roo.bootstrap.NavGroup} this
6068              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6069              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6070          */
6071         'changed': true
6072      });
6073     
6074 };
6075
6076 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6077     
6078     align: '',
6079     inverse: false,
6080     form: false,
6081     type: 'nav',
6082     navId : '',
6083     // private
6084     pilltype : true,
6085     
6086     navItems : false, 
6087     
6088     getAutoCreate : function()
6089     {
6090         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6091         
6092         cfg = {
6093             tag : 'ul',
6094             cls: 'nav' 
6095         };
6096         if (Roo.bootstrap.version == 4) {
6097             if (['tabs','pills'].indexOf(this.type) != -1) {
6098                 cfg.cls += ' nav-' + this.type; 
6099             } else {
6100                 // trying to remove so header bar can right align top?
6101                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6102                     // do not use on header bar... 
6103                     cfg.cls += ' navbar-nav';
6104                 }
6105             }
6106             
6107         } else {
6108             if (['tabs','pills'].indexOf(this.type) != -1) {
6109                 cfg.cls += ' nav-' + this.type
6110             } else {
6111                 if (this.type !== 'nav') {
6112                     Roo.log('nav type must be nav/tabs/pills')
6113                 }
6114                 cfg.cls += ' navbar-nav'
6115             }
6116         }
6117         
6118         if (this.parent() && this.parent().sidebar) {
6119             cfg = {
6120                 tag: 'ul',
6121                 cls: 'dashboard-menu sidebar-menu'
6122             };
6123             
6124             return cfg;
6125         }
6126         
6127         if (this.form === true) {
6128             cfg = {
6129                 tag: 'form',
6130                 cls: 'navbar-form form-inline'
6131             };
6132             //nav navbar-right ml-md-auto
6133             if (this.align === 'right') {
6134                 cfg.cls += ' navbar-right ml-md-auto';
6135             } else {
6136                 cfg.cls += ' navbar-left';
6137             }
6138         }
6139         
6140         if (this.align === 'right') {
6141             cfg.cls += ' navbar-right ml-md-auto';
6142         } else {
6143             cfg.cls += ' mr-auto';
6144         }
6145         
6146         if (this.inverse) {
6147             cfg.cls += ' navbar-inverse';
6148             
6149         }
6150         
6151         
6152         return cfg;
6153     },
6154     /**
6155     * sets the active Navigation item
6156     * @param {Roo.bootstrap.NavItem} the new current navitem
6157     */
6158     setActiveItem : function(item)
6159     {
6160         var prev = false;
6161         Roo.each(this.navItems, function(v){
6162             if (v == item) {
6163                 return ;
6164             }
6165             if (v.isActive()) {
6166                 v.setActive(false, true);
6167                 prev = v;
6168                 
6169             }
6170             
6171         });
6172
6173         item.setActive(true, true);
6174         this.fireEvent('changed', this, item, prev);
6175         
6176         
6177     },
6178     /**
6179     * gets the active Navigation item
6180     * @return {Roo.bootstrap.NavItem} the current navitem
6181     */
6182     getActive : function()
6183     {
6184         
6185         var prev = false;
6186         Roo.each(this.navItems, function(v){
6187             
6188             if (v.isActive()) {
6189                 prev = v;
6190                 
6191             }
6192             
6193         });
6194         return prev;
6195     },
6196     
6197     indexOfNav : function()
6198     {
6199         
6200         var prev = false;
6201         Roo.each(this.navItems, function(v,i){
6202             
6203             if (v.isActive()) {
6204                 prev = i;
6205                 
6206             }
6207             
6208         });
6209         return prev;
6210     },
6211     /**
6212     * adds a Navigation item
6213     * @param {Roo.bootstrap.NavItem} the navitem to add
6214     */
6215     addItem : function(cfg)
6216     {
6217         if (this.form && Roo.bootstrap.version == 4) {
6218             cfg.tag = 'div';
6219         }
6220         var cn = new Roo.bootstrap.NavItem(cfg);
6221         this.register(cn);
6222         cn.parentId = this.id;
6223         cn.onRender(this.el, null);
6224         return cn;
6225     },
6226     /**
6227     * register a Navigation item
6228     * @param {Roo.bootstrap.NavItem} the navitem to add
6229     */
6230     register : function(item)
6231     {
6232         this.navItems.push( item);
6233         item.navId = this.navId;
6234     
6235     },
6236     
6237     /**
6238     * clear all the Navigation item
6239     */
6240    
6241     clearAll : function()
6242     {
6243         this.navItems = [];
6244         this.el.dom.innerHTML = '';
6245     },
6246     
6247     getNavItem: function(tabId)
6248     {
6249         var ret = false;
6250         Roo.each(this.navItems, function(e) {
6251             if (e.tabId == tabId) {
6252                ret =  e;
6253                return false;
6254             }
6255             return true;
6256             
6257         });
6258         return ret;
6259     },
6260     
6261     setActiveNext : function()
6262     {
6263         var i = this.indexOfNav(this.getActive());
6264         if (i > this.navItems.length) {
6265             return;
6266         }
6267         this.setActiveItem(this.navItems[i+1]);
6268     },
6269     setActivePrev : function()
6270     {
6271         var i = this.indexOfNav(this.getActive());
6272         if (i  < 1) {
6273             return;
6274         }
6275         this.setActiveItem(this.navItems[i-1]);
6276     },
6277     clearWasActive : function(except) {
6278         Roo.each(this.navItems, function(e) {
6279             if (e.tabId != except.tabId && e.was_active) {
6280                e.was_active = false;
6281                return false;
6282             }
6283             return true;
6284             
6285         });
6286     },
6287     getWasActive : function ()
6288     {
6289         var r = false;
6290         Roo.each(this.navItems, function(e) {
6291             if (e.was_active) {
6292                r = e;
6293                return false;
6294             }
6295             return true;
6296             
6297         });
6298         return r;
6299     }
6300     
6301     
6302 });
6303
6304  
6305 Roo.apply(Roo.bootstrap.NavGroup, {
6306     
6307     groups: {},
6308      /**
6309     * register a Navigation Group
6310     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6311     */
6312     register : function(navgrp)
6313     {
6314         this.groups[navgrp.navId] = navgrp;
6315         
6316     },
6317     /**
6318     * fetch a Navigation Group based on the navigation ID
6319     * @param {string} the navgroup to add
6320     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6321     */
6322     get: function(navId) {
6323         if (typeof(this.groups[navId]) == 'undefined') {
6324             return false;
6325             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6326         }
6327         return this.groups[navId] ;
6328     }
6329     
6330     
6331     
6332 });
6333
6334  /*
6335  * - LGPL
6336  *
6337  * row
6338  * 
6339  */
6340
6341 /**
6342  * @class Roo.bootstrap.NavItem
6343  * @extends Roo.bootstrap.Component
6344  * Bootstrap Navbar.NavItem class
6345  * @cfg {String} href  link to
6346  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6347  * @cfg {Boolean} button_outline show and outlined button
6348  * @cfg {String} html content of button
6349  * @cfg {String} badge text inside badge
6350  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6351  * @cfg {String} glyphicon DEPRICATED - use fa
6352  * @cfg {String} icon DEPRICATED - use fa
6353  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6354  * @cfg {Boolean} active Is item active
6355  * @cfg {Boolean} disabled Is item disabled
6356  * @cfg {String} linkcls  Link Class
6357  * @cfg {Boolean} preventDefault (true | false) default false
6358  * @cfg {String} tabId the tab that this item activates.
6359  * @cfg {String} tagtype (a|span) render as a href or span?
6360  * @cfg {Boolean} animateRef (true|false) link to element default false  
6361   
6362  * @constructor
6363  * Create a new Navbar Item
6364  * @param {Object} config The config object
6365  */
6366 Roo.bootstrap.NavItem = function(config){
6367     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6368     this.addEvents({
6369         // raw events
6370         /**
6371          * @event click
6372          * The raw click event for the entire grid.
6373          * @param {Roo.EventObject} e
6374          */
6375         "click" : true,
6376          /**
6377             * @event changed
6378             * Fires when the active item active state changes
6379             * @param {Roo.bootstrap.NavItem} this
6380             * @param {boolean} state the new state
6381              
6382          */
6383         'changed': true,
6384         /**
6385             * @event scrollto
6386             * Fires when scroll to element
6387             * @param {Roo.bootstrap.NavItem} this
6388             * @param {Object} options
6389             * @param {Roo.EventObject} e
6390              
6391          */
6392         'scrollto': true
6393     });
6394    
6395 };
6396
6397 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6398     
6399     href: false,
6400     html: '',
6401     badge: '',
6402     icon: false,
6403     fa : false,
6404     glyphicon: false,
6405     active: false,
6406     preventDefault : false,
6407     tabId : false,
6408     tagtype : 'a',
6409     tag: 'li',
6410     disabled : false,
6411     animateRef : false,
6412     was_active : false,
6413     button_weight : '',
6414     button_outline : false,
6415     linkcls : '',
6416     navLink: false,
6417     
6418     getAutoCreate : function(){
6419          
6420         var cfg = {
6421             tag: this.tag,
6422             cls: 'nav-item'
6423         };
6424         
6425         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6426         
6427         if (this.active) {
6428             cfg.cls +=  ' active' ;
6429         }
6430         if (this.disabled) {
6431             cfg.cls += ' disabled';
6432         }
6433         
6434         // BS4 only?
6435         if (this.button_weight.length) {
6436             cfg.tag = this.href ? 'a' : 'button';
6437             cfg.html = this.html || '';
6438             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6439             if (this.href) {
6440                 cfg.href = this.href;
6441             }
6442             if (this.fa) {
6443                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6444             } else {
6445                 cfg.cls += " nav-html";
6446             }
6447             
6448             // menu .. should add dropdown-menu class - so no need for carat..
6449             
6450             if (this.badge !== '') {
6451                  
6452                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6453             }
6454             return cfg;
6455         }
6456         
6457         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6458             cfg.cn = [
6459                 {
6460                     tag: this.tagtype,
6461                     href : this.href || "#",
6462                     html: this.html || '',
6463                     cls : ''
6464                 }
6465             ];
6466             if (this.tagtype == 'a') {
6467                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6468         
6469             }
6470             if (this.icon) {
6471                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6472             } else  if (this.fa) {
6473                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6474             } else if(this.glyphicon) {
6475                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6476             } else {
6477                 cfg.cn[0].cls += " nav-html";
6478             }
6479             
6480             if (this.menu) {
6481                 cfg.cn[0].html += " <span class='caret'></span>";
6482              
6483             }
6484             
6485             if (this.badge !== '') {
6486                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6487             }
6488         }
6489         
6490         
6491         
6492         return cfg;
6493     },
6494     onRender : function(ct, position)
6495     {
6496        // Roo.log("Call onRender: " + this.xtype);
6497         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6498             this.tag = 'div';
6499         }
6500         
6501         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6502         this.navLink = this.el.select('.nav-link',true).first();
6503         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6504         return ret;
6505     },
6506       
6507     
6508     initEvents: function() 
6509     {
6510         if (typeof (this.menu) != 'undefined') {
6511             this.menu.parentType = this.xtype;
6512             this.menu.triggerEl = this.el;
6513             this.menu = this.addxtype(Roo.apply({}, this.menu));
6514         }
6515         
6516         this.el.on('click', this.onClick, this);
6517         
6518         //if(this.tagtype == 'span'){
6519         //    this.el.select('span',true).on('click', this.onClick, this);
6520         //}
6521        
6522         // at this point parent should be available..
6523         this.parent().register(this);
6524     },
6525     
6526     onClick : function(e)
6527     {
6528         if (e.getTarget('.dropdown-menu-item')) {
6529             // did you click on a menu itemm.... - then don't trigger onclick..
6530             return;
6531         }
6532         
6533         if(
6534                 this.preventDefault || 
6535                 this.href == '#' 
6536         ){
6537             Roo.log("NavItem - prevent Default?");
6538             e.preventDefault();
6539         }
6540         
6541         if (this.disabled) {
6542             return;
6543         }
6544         
6545         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6546         if (tg && tg.transition) {
6547             Roo.log("waiting for the transitionend");
6548             return;
6549         }
6550         
6551         
6552         
6553         //Roo.log("fire event clicked");
6554         if(this.fireEvent('click', this, e) === false){
6555             return;
6556         };
6557         
6558         if(this.tagtype == 'span'){
6559             return;
6560         }
6561         
6562         //Roo.log(this.href);
6563         var ael = this.el.select('a',true).first();
6564         //Roo.log(ael);
6565         
6566         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6567             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6568             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6569                 return; // ignore... - it's a 'hash' to another page.
6570             }
6571             Roo.log("NavItem - prevent Default?");
6572             e.preventDefault();
6573             this.scrollToElement(e);
6574         }
6575         
6576         
6577         var p =  this.parent();
6578    
6579         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6580             if (typeof(p.setActiveItem) !== 'undefined') {
6581                 p.setActiveItem(this);
6582             }
6583         }
6584         
6585         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6586         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6587             // remove the collapsed menu expand...
6588             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6589         }
6590     },
6591     
6592     isActive: function () {
6593         return this.active
6594     },
6595     setActive : function(state, fire, is_was_active)
6596     {
6597         if (this.active && !state && this.navId) {
6598             this.was_active = true;
6599             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6600             if (nv) {
6601                 nv.clearWasActive(this);
6602             }
6603             
6604         }
6605         this.active = state;
6606         
6607         if (!state ) {
6608             this.el.removeClass('active');
6609             this.navLink ? this.navLink.removeClass('active') : false;
6610         } else if (!this.el.hasClass('active')) {
6611             
6612             this.el.addClass('active');
6613             if (Roo.bootstrap.version == 4 && this.navLink ) {
6614                 this.navLink.addClass('active');
6615             }
6616             
6617         }
6618         if (fire) {
6619             this.fireEvent('changed', this, state);
6620         }
6621         
6622         // show a panel if it's registered and related..
6623         
6624         if (!this.navId || !this.tabId || !state || is_was_active) {
6625             return;
6626         }
6627         
6628         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6629         if (!tg) {
6630             return;
6631         }
6632         var pan = tg.getPanelByName(this.tabId);
6633         if (!pan) {
6634             return;
6635         }
6636         // if we can not flip to new panel - go back to old nav highlight..
6637         if (false == tg.showPanel(pan)) {
6638             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6639             if (nv) {
6640                 var onav = nv.getWasActive();
6641                 if (onav) {
6642                     onav.setActive(true, false, true);
6643                 }
6644             }
6645             
6646         }
6647         
6648         
6649         
6650     },
6651      // this should not be here...
6652     setDisabled : function(state)
6653     {
6654         this.disabled = state;
6655         if (!state ) {
6656             this.el.removeClass('disabled');
6657         } else if (!this.el.hasClass('disabled')) {
6658             this.el.addClass('disabled');
6659         }
6660         
6661     },
6662     
6663     /**
6664      * Fetch the element to display the tooltip on.
6665      * @return {Roo.Element} defaults to this.el
6666      */
6667     tooltipEl : function()
6668     {
6669         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6670     },
6671     
6672     scrollToElement : function(e)
6673     {
6674         var c = document.body;
6675         
6676         /*
6677          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6678          */
6679         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6680             c = document.documentElement;
6681         }
6682         
6683         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6684         
6685         if(!target){
6686             return;
6687         }
6688
6689         var o = target.calcOffsetsTo(c);
6690         
6691         var options = {
6692             target : target,
6693             value : o[1]
6694         };
6695         
6696         this.fireEvent('scrollto', this, options, e);
6697         
6698         Roo.get(c).scrollTo('top', options.value, true);
6699         
6700         return;
6701     },
6702     /**
6703      * Set the HTML (text content) of the item
6704      * @param {string} html  content for the nav item
6705      */
6706     setHtml : function(html)
6707     {
6708         this.html = html;
6709         this.htmlEl.dom.innerHTML = html;
6710         
6711     } 
6712 });
6713  
6714
6715  /*
6716  * - LGPL
6717  *
6718  * sidebar item
6719  *
6720  *  li
6721  *    <span> icon </span>
6722  *    <span> text </span>
6723  *    <span>badge </span>
6724  */
6725
6726 /**
6727  * @class Roo.bootstrap.NavSidebarItem
6728  * @extends Roo.bootstrap.NavItem
6729  * Bootstrap Navbar.NavSidebarItem class
6730  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6731  * {Boolean} open is the menu open
6732  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6733  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6734  * {String} buttonSize (sm|md|lg)the extra classes for the button
6735  * {Boolean} showArrow show arrow next to the text (default true)
6736  * @constructor
6737  * Create a new Navbar Button
6738  * @param {Object} config The config object
6739  */
6740 Roo.bootstrap.NavSidebarItem = function(config){
6741     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6742     this.addEvents({
6743         // raw events
6744         /**
6745          * @event click
6746          * The raw click event for the entire grid.
6747          * @param {Roo.EventObject} e
6748          */
6749         "click" : true,
6750          /**
6751             * @event changed
6752             * Fires when the active item active state changes
6753             * @param {Roo.bootstrap.NavSidebarItem} this
6754             * @param {boolean} state the new state
6755              
6756          */
6757         'changed': true
6758     });
6759    
6760 };
6761
6762 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6763     
6764     badgeWeight : 'default',
6765     
6766     open: false,
6767     
6768     buttonView : false,
6769     
6770     buttonWeight : 'default',
6771     
6772     buttonSize : 'md',
6773     
6774     showArrow : true,
6775     
6776     getAutoCreate : function(){
6777         
6778         
6779         var a = {
6780                 tag: 'a',
6781                 href : this.href || '#',
6782                 cls: '',
6783                 html : '',
6784                 cn : []
6785         };
6786         
6787         if(this.buttonView){
6788             a = {
6789                 tag: 'button',
6790                 href : this.href || '#',
6791                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6792                 html : this.html,
6793                 cn : []
6794             };
6795         }
6796         
6797         var cfg = {
6798             tag: 'li',
6799             cls: '',
6800             cn: [ a ]
6801         };
6802         
6803         if (this.active) {
6804             cfg.cls += ' active';
6805         }
6806         
6807         if (this.disabled) {
6808             cfg.cls += ' disabled';
6809         }
6810         if (this.open) {
6811             cfg.cls += ' open x-open';
6812         }
6813         // left icon..
6814         if (this.glyphicon || this.icon) {
6815             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6816             a.cn.push({ tag : 'i', cls : c }) ;
6817         }
6818         
6819         if(!this.buttonView){
6820             var span = {
6821                 tag: 'span',
6822                 html : this.html || ''
6823             };
6824
6825             a.cn.push(span);
6826             
6827         }
6828         
6829         if (this.badge !== '') {
6830             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6831         }
6832         
6833         if (this.menu) {
6834             
6835             if(this.showArrow){
6836                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6837             }
6838             
6839             a.cls += ' dropdown-toggle treeview' ;
6840         }
6841         
6842         return cfg;
6843     },
6844     
6845     initEvents : function()
6846     { 
6847         if (typeof (this.menu) != 'undefined') {
6848             this.menu.parentType = this.xtype;
6849             this.menu.triggerEl = this.el;
6850             this.menu = this.addxtype(Roo.apply({}, this.menu));
6851         }
6852         
6853         this.el.on('click', this.onClick, this);
6854         
6855         if(this.badge !== ''){
6856             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6857         }
6858         
6859     },
6860     
6861     onClick : function(e)
6862     {
6863         if(this.disabled){
6864             e.preventDefault();
6865             return;
6866         }
6867         
6868         if(this.preventDefault){
6869             e.preventDefault();
6870         }
6871         
6872         this.fireEvent('click', this, e);
6873     },
6874     
6875     disable : function()
6876     {
6877         this.setDisabled(true);
6878     },
6879     
6880     enable : function()
6881     {
6882         this.setDisabled(false);
6883     },
6884     
6885     setDisabled : function(state)
6886     {
6887         if(this.disabled == state){
6888             return;
6889         }
6890         
6891         this.disabled = state;
6892         
6893         if (state) {
6894             this.el.addClass('disabled');
6895             return;
6896         }
6897         
6898         this.el.removeClass('disabled');
6899         
6900         return;
6901     },
6902     
6903     setActive : function(state)
6904     {
6905         if(this.active == state){
6906             return;
6907         }
6908         
6909         this.active = state;
6910         
6911         if (state) {
6912             this.el.addClass('active');
6913             return;
6914         }
6915         
6916         this.el.removeClass('active');
6917         
6918         return;
6919     },
6920     
6921     isActive: function () 
6922     {
6923         return this.active;
6924     },
6925     
6926     setBadge : function(str)
6927     {
6928         if(!this.badgeEl){
6929             return;
6930         }
6931         
6932         this.badgeEl.dom.innerHTML = str;
6933     }
6934     
6935    
6936      
6937  
6938 });
6939  
6940
6941  /*
6942  * - LGPL
6943  *
6944  *  Breadcrumb Nav
6945  * 
6946  */
6947 Roo.namespace('Roo.bootstrap.breadcrumb');
6948
6949
6950 /**
6951  * @class Roo.bootstrap.breadcrumb.Nav
6952  * @extends Roo.bootstrap.Component
6953  * Bootstrap Breadcrumb Nav Class
6954  *  
6955  * @children Roo.bootstrap.breadcrumb.Item
6956  * 
6957  * @constructor
6958  * Create a new breadcrumb.Nav
6959  * @param {Object} config The config object
6960  */
6961
6962
6963 Roo.bootstrap.breadcrumb.Nav = function(config){
6964     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6965     
6966     
6967 };
6968
6969 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6970     
6971     getAutoCreate : function()
6972     {
6973
6974         var cfg = {
6975             tag: 'nav',
6976             cn : [
6977                 {
6978                     tag : 'ol',
6979                     cls : 'breadcrumb'
6980                 }
6981             ]
6982             
6983         };
6984           
6985         return cfg;
6986     },
6987     
6988     initEvents: function()
6989     {
6990         this.olEl = this.el.select('ol',true).first();    
6991     },
6992     getChildContainer : function()
6993     {
6994         return this.olEl;  
6995     }
6996     
6997 });
6998
6999  /*
7000  * - LGPL
7001  *
7002  *  Breadcrumb Item
7003  * 
7004  */
7005
7006
7007 /**
7008  * @class Roo.bootstrap.breadcrumb.Nav
7009  * @extends Roo.bootstrap.Component
7010  * Bootstrap Breadcrumb Nav Class
7011  *  
7012  * @children Roo.bootstrap.breadcrumb.Component
7013  * @cfg {String} html the content of the link.
7014  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
7015  * @cfg {Boolean} active is it active
7016
7017  * 
7018  * @constructor
7019  * Create a new breadcrumb.Nav
7020  * @param {Object} config The config object
7021  */
7022
7023 Roo.bootstrap.breadcrumb.Item = function(config){
7024     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
7025     this.addEvents({
7026         // img events
7027         /**
7028          * @event click
7029          * The img click event for the img.
7030          * @param {Roo.EventObject} e
7031          */
7032         "click" : true
7033     });
7034     
7035 };
7036
7037 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7038     
7039     href: false,
7040     html : '',
7041     
7042     getAutoCreate : function()
7043     {
7044
7045         var cfg = {
7046             tag: 'li',
7047             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7048         };
7049         if (this.href !== false) {
7050             cfg.cn = [{
7051                 tag : 'a',
7052                 href : this.href,
7053                 html : this.html
7054             }];
7055         } else {
7056             cfg.html = this.html;
7057         }
7058         
7059         return cfg;
7060     },
7061     
7062     initEvents: function()
7063     {
7064         if (this.href) {
7065             this.el.select('a', true).first().on('click',this.onClick, this)
7066         }
7067         
7068     },
7069     onClick : function(e)
7070     {
7071         e.preventDefault();
7072         this.fireEvent('click',this,  e);
7073     }
7074     
7075 });
7076
7077  /*
7078  * - LGPL
7079  *
7080  * row
7081  * 
7082  */
7083
7084 /**
7085  * @class Roo.bootstrap.Row
7086  * @extends Roo.bootstrap.Component
7087  * Bootstrap Row class (contains columns...)
7088  * 
7089  * @constructor
7090  * Create a new Row
7091  * @param {Object} config The config object
7092  */
7093
7094 Roo.bootstrap.Row = function(config){
7095     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7096 };
7097
7098 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7099     
7100     getAutoCreate : function(){
7101        return {
7102             cls: 'row clearfix'
7103        };
7104     }
7105     
7106     
7107 });
7108
7109  
7110
7111  /*
7112  * - LGPL
7113  *
7114  * pagination
7115  * 
7116  */
7117
7118 /**
7119  * @class Roo.bootstrap.Pagination
7120  * @extends Roo.bootstrap.Component
7121  * Bootstrap Pagination class
7122  * @cfg {String} size xs | sm | md | lg
7123  * @cfg {Boolean} inverse false | true
7124  * 
7125  * @constructor
7126  * Create a new Pagination
7127  * @param {Object} config The config object
7128  */
7129
7130 Roo.bootstrap.Pagination = function(config){
7131     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7132 };
7133
7134 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7135     
7136     cls: false,
7137     size: false,
7138     inverse: false,
7139     
7140     getAutoCreate : function(){
7141         var cfg = {
7142             tag: 'ul',
7143                 cls: 'pagination'
7144         };
7145         if (this.inverse) {
7146             cfg.cls += ' inverse';
7147         }
7148         if (this.html) {
7149             cfg.html=this.html;
7150         }
7151         if (this.cls) {
7152             cfg.cls += " " + this.cls;
7153         }
7154         return cfg;
7155     }
7156    
7157 });
7158
7159  
7160
7161  /*
7162  * - LGPL
7163  *
7164  * Pagination item
7165  * 
7166  */
7167
7168
7169 /**
7170  * @class Roo.bootstrap.PaginationItem
7171  * @extends Roo.bootstrap.Component
7172  * Bootstrap PaginationItem class
7173  * @cfg {String} html text
7174  * @cfg {String} href the link
7175  * @cfg {Boolean} preventDefault (true | false) default true
7176  * @cfg {Boolean} active (true | false) default false
7177  * @cfg {Boolean} disabled default false
7178  * 
7179  * 
7180  * @constructor
7181  * Create a new PaginationItem
7182  * @param {Object} config The config object
7183  */
7184
7185
7186 Roo.bootstrap.PaginationItem = function(config){
7187     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7188     this.addEvents({
7189         // raw events
7190         /**
7191          * @event click
7192          * The raw click event for the entire grid.
7193          * @param {Roo.EventObject} e
7194          */
7195         "click" : true
7196     });
7197 };
7198
7199 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7200     
7201     href : false,
7202     html : false,
7203     preventDefault: true,
7204     active : false,
7205     cls : false,
7206     disabled: false,
7207     
7208     getAutoCreate : function(){
7209         var cfg= {
7210             tag: 'li',
7211             cn: [
7212                 {
7213                     tag : 'a',
7214                     href : this.href ? this.href : '#',
7215                     html : this.html ? this.html : ''
7216                 }
7217             ]
7218         };
7219         
7220         if(this.cls){
7221             cfg.cls = this.cls;
7222         }
7223         
7224         if(this.disabled){
7225             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7226         }
7227         
7228         if(this.active){
7229             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7230         }
7231         
7232         return cfg;
7233     },
7234     
7235     initEvents: function() {
7236         
7237         this.el.on('click', this.onClick, this);
7238         
7239     },
7240     onClick : function(e)
7241     {
7242         Roo.log('PaginationItem on click ');
7243         if(this.preventDefault){
7244             e.preventDefault();
7245         }
7246         
7247         if(this.disabled){
7248             return;
7249         }
7250         
7251         this.fireEvent('click', this, e);
7252     }
7253    
7254 });
7255
7256  
7257
7258  /*
7259  * - LGPL
7260  *
7261  * slider
7262  * 
7263  */
7264
7265
7266 /**
7267  * @class Roo.bootstrap.Slider
7268  * @extends Roo.bootstrap.Component
7269  * Bootstrap Slider class
7270  *    
7271  * @constructor
7272  * Create a new Slider
7273  * @param {Object} config The config object
7274  */
7275
7276 Roo.bootstrap.Slider = function(config){
7277     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7278 };
7279
7280 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7281     
7282     getAutoCreate : function(){
7283         
7284         var cfg = {
7285             tag: 'div',
7286             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7287             cn: [
7288                 {
7289                     tag: 'a',
7290                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7291                 }
7292             ]
7293         };
7294         
7295         return cfg;
7296     }
7297    
7298 });
7299
7300  /*
7301  * Based on:
7302  * Ext JS Library 1.1.1
7303  * Copyright(c) 2006-2007, Ext JS, LLC.
7304  *
7305  * Originally Released Under LGPL - original licence link has changed is not relivant.
7306  *
7307  * Fork - LGPL
7308  * <script type="text/javascript">
7309  */
7310
7311 /**
7312  * @class Roo.grid.AbstractSelectionModel
7313  * @extends Roo.util.Observable
7314  * Abstract base class for grid SelectionModels.  It provides the interface that should be
7315  * implemented by descendant classes.  This class should not be directly instantiated.
7316  * @constructor
7317  */
7318 Roo.grid.AbstractSelectionModel = function(){
7319     this.locked = false;
7320     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
7321 };
7322
7323 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
7324     /** @ignore Called by the grid automatically. Do not call directly. */
7325     init : function(grid){
7326         this.grid = grid;
7327         this.initEvents();
7328     },
7329
7330     /**
7331      * Locks the selections.
7332      */
7333     lock : function(){
7334         this.locked = true;
7335     },
7336
7337     /**
7338      * Unlocks the selections.
7339      */
7340     unlock : function(){
7341         this.locked = false;
7342     },
7343
7344     /**
7345      * Returns true if the selections are locked.
7346      * @return {Boolean}
7347      */
7348     isLocked : function(){
7349         return this.locked;
7350     }
7351 });/*
7352  * Based on:
7353  * Ext JS Library 1.1.1
7354  * Copyright(c) 2006-2007, Ext JS, LLC.
7355  *
7356  * Originally Released Under LGPL - original licence link has changed is not relivant.
7357  *
7358  * Fork - LGPL
7359  * <script type="text/javascript">
7360  */
7361 /**
7362  * @extends Roo.grid.AbstractSelectionModel
7363  * @class Roo.grid.RowSelectionModel
7364  * The default SelectionModel used by {@link Roo.grid.Grid}.
7365  * It supports multiple selections and keyboard selection/navigation. 
7366  * @constructor
7367  * @param {Object} config
7368  */
7369 Roo.grid.RowSelectionModel = function(config){
7370     Roo.apply(this, config);
7371     this.selections = new Roo.util.MixedCollection(false, function(o){
7372         return o.id;
7373     });
7374
7375     this.last = false;
7376     this.lastActive = false;
7377
7378     this.addEvents({
7379         /**
7380         * @event selectionchange
7381         * Fires when the selection changes
7382         * @param {SelectionModel} this
7383         */
7384        "selectionchange" : true,
7385        /**
7386         * @event afterselectionchange
7387         * Fires after the selection changes (eg. by key press or clicking)
7388         * @param {SelectionModel} this
7389         */
7390        "afterselectionchange" : true,
7391        /**
7392         * @event beforerowselect
7393         * Fires when a row is selected being selected, return false to cancel.
7394         * @param {SelectionModel} this
7395         * @param {Number} rowIndex The selected index
7396         * @param {Boolean} keepExisting False if other selections will be cleared
7397         */
7398        "beforerowselect" : true,
7399        /**
7400         * @event rowselect
7401         * Fires when a row is selected.
7402         * @param {SelectionModel} this
7403         * @param {Number} rowIndex The selected index
7404         * @param {Roo.data.Record} r The record
7405         */
7406        "rowselect" : true,
7407        /**
7408         * @event rowdeselect
7409         * Fires when a row is deselected.
7410         * @param {SelectionModel} this
7411         * @param {Number} rowIndex The selected index
7412         */
7413         "rowdeselect" : true
7414     });
7415     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
7416     this.locked = false;
7417 };
7418
7419 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
7420     /**
7421      * @cfg {Boolean} singleSelect
7422      * True to allow selection of only one row at a time (defaults to false)
7423      */
7424     singleSelect : false,
7425
7426     // private
7427     initEvents : function(){
7428
7429         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
7430             this.grid.on("mousedown", this.handleMouseDown, this);
7431         }else{ // allow click to work like normal
7432             this.grid.on("rowclick", this.handleDragableRowClick, this);
7433         }
7434         // bootstrap does not have a view..
7435         var view = this.grid.view ? this.grid.view : this.grid;
7436         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
7437             "up" : function(e){
7438                 if(!e.shiftKey){
7439                     this.selectPrevious(e.shiftKey);
7440                 }else if(this.last !== false && this.lastActive !== false){
7441                     var last = this.last;
7442                     this.selectRange(this.last,  this.lastActive-1);
7443                     view.focusRow(this.lastActive);
7444                     if(last !== false){
7445                         this.last = last;
7446                     }
7447                 }else{
7448                     this.selectFirstRow();
7449                 }
7450                 this.fireEvent("afterselectionchange", this);
7451             },
7452             "down" : function(e){
7453                 if(!e.shiftKey){
7454                     this.selectNext(e.shiftKey);
7455                 }else if(this.last !== false && this.lastActive !== false){
7456                     var last = this.last;
7457                     this.selectRange(this.last,  this.lastActive+1);
7458                     view.focusRow(this.lastActive);
7459                     if(last !== false){
7460                         this.last = last;
7461                     }
7462                 }else{
7463                     this.selectFirstRow();
7464                 }
7465                 this.fireEvent("afterselectionchange", this);
7466             },
7467             scope: this
7468         });
7469
7470          
7471         view.on("refresh", this.onRefresh, this);
7472         view.on("rowupdated", this.onRowUpdated, this);
7473         view.on("rowremoved", this.onRemove, this);
7474     },
7475
7476     // private
7477     onRefresh : function(){
7478         var ds = this.grid.ds, i, v = this.grid.view;
7479         var s = this.selections;
7480         s.each(function(r){
7481             if((i = ds.indexOfId(r.id)) != -1){
7482                 v.onRowSelect(i);
7483                 s.add(ds.getAt(i)); // updating the selection relate data
7484             }else{
7485                 s.remove(r);
7486             }
7487         });
7488     },
7489
7490     // private
7491     onRemove : function(v, index, r){
7492         this.selections.remove(r);
7493     },
7494
7495     // private
7496     onRowUpdated : function(v, index, r){
7497         if(this.isSelected(r)){
7498             v.onRowSelect(index);
7499         }
7500     },
7501
7502     /**
7503      * Select records.
7504      * @param {Array} records The records to select
7505      * @param {Boolean} keepExisting (optional) True to keep existing selections
7506      */
7507     selectRecords : function(records, keepExisting){
7508         if(!keepExisting){
7509             this.clearSelections();
7510         }
7511         var ds = this.grid.ds;
7512         for(var i = 0, len = records.length; i < len; i++){
7513             this.selectRow(ds.indexOf(records[i]), true);
7514         }
7515     },
7516
7517     /**
7518      * Gets the number of selected rows.
7519      * @return {Number}
7520      */
7521     getCount : function(){
7522         return this.selections.length;
7523     },
7524
7525     /**
7526      * Selects the first row in the grid.
7527      */
7528     selectFirstRow : function(){
7529         this.selectRow(0);
7530     },
7531
7532     /**
7533      * Select the last row.
7534      * @param {Boolean} keepExisting (optional) True to keep existing selections
7535      */
7536     selectLastRow : function(keepExisting){
7537         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
7538     },
7539
7540     /**
7541      * Selects the row immediately following the last selected row.
7542      * @param {Boolean} keepExisting (optional) True to keep existing selections
7543      */
7544     selectNext : function(keepExisting){
7545         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
7546             this.selectRow(this.last+1, keepExisting);
7547             var view = this.grid.view ? this.grid.view : this.grid;
7548             view.focusRow(this.last);
7549         }
7550     },
7551
7552     /**
7553      * Selects the row that precedes the last selected row.
7554      * @param {Boolean} keepExisting (optional) True to keep existing selections
7555      */
7556     selectPrevious : function(keepExisting){
7557         if(this.last){
7558             this.selectRow(this.last-1, keepExisting);
7559             var view = this.grid.view ? this.grid.view : this.grid;
7560             view.focusRow(this.last);
7561         }
7562     },
7563
7564     /**
7565      * Returns the selected records
7566      * @return {Array} Array of selected records
7567      */
7568     getSelections : function(){
7569         return [].concat(this.selections.items);
7570     },
7571
7572     /**
7573      * Returns the first selected record.
7574      * @return {Record}
7575      */
7576     getSelected : function(){
7577         return this.selections.itemAt(0);
7578     },
7579
7580
7581     /**
7582      * Clears all selections.
7583      */
7584     clearSelections : function(fast){
7585         if(this.locked) {
7586             return;
7587         }
7588         if(fast !== true){
7589             var ds = this.grid.ds;
7590             var s = this.selections;
7591             s.each(function(r){
7592                 this.deselectRow(ds.indexOfId(r.id));
7593             }, this);
7594             s.clear();
7595         }else{
7596             this.selections.clear();
7597         }
7598         this.last = false;
7599     },
7600
7601
7602     /**
7603      * Selects all rows.
7604      */
7605     selectAll : function(){
7606         if(this.locked) {
7607             return;
7608         }
7609         this.selections.clear();
7610         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
7611             this.selectRow(i, true);
7612         }
7613     },
7614
7615     /**
7616      * Returns True if there is a selection.
7617      * @return {Boolean}
7618      */
7619     hasSelection : function(){
7620         return this.selections.length > 0;
7621     },
7622
7623     /**
7624      * Returns True if the specified row is selected.
7625      * @param {Number/Record} record The record or index of the record to check
7626      * @return {Boolean}
7627      */
7628     isSelected : function(index){
7629         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
7630         return (r && this.selections.key(r.id) ? true : false);
7631     },
7632
7633     /**
7634      * Returns True if the specified record id is selected.
7635      * @param {String} id The id of record to check
7636      * @return {Boolean}
7637      */
7638     isIdSelected : function(id){
7639         return (this.selections.key(id) ? true : false);
7640     },
7641
7642     // private
7643     handleMouseDown : function(e, t)
7644     {
7645         var view = this.grid.view ? this.grid.view : this.grid;
7646         var rowIndex;
7647         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
7648             return;
7649         };
7650         if(e.shiftKey && this.last !== false){
7651             var last = this.last;
7652             this.selectRange(last, rowIndex, e.ctrlKey);
7653             this.last = last; // reset the last
7654             view.focusRow(rowIndex);
7655         }else{
7656             var isSelected = this.isSelected(rowIndex);
7657             if(e.button !== 0 && isSelected){
7658                 view.focusRow(rowIndex);
7659             }else if(e.ctrlKey && isSelected){
7660                 this.deselectRow(rowIndex);
7661             }else if(!isSelected){
7662                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
7663                 view.focusRow(rowIndex);
7664             }
7665         }
7666         this.fireEvent("afterselectionchange", this);
7667     },
7668     // private
7669     handleDragableRowClick :  function(grid, rowIndex, e) 
7670     {
7671         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
7672             this.selectRow(rowIndex, false);
7673             var view = this.grid.view ? this.grid.view : this.grid;
7674             view.focusRow(rowIndex);
7675              this.fireEvent("afterselectionchange", this);
7676         }
7677     },
7678     
7679     /**
7680      * Selects multiple rows.
7681      * @param {Array} rows Array of the indexes of the row to select
7682      * @param {Boolean} keepExisting (optional) True to keep existing selections
7683      */
7684     selectRows : function(rows, keepExisting){
7685         if(!keepExisting){
7686             this.clearSelections();
7687         }
7688         for(var i = 0, len = rows.length; i < len; i++){
7689             this.selectRow(rows[i], true);
7690         }
7691     },
7692
7693     /**
7694      * Selects a range of rows. All rows in between startRow and endRow are also selected.
7695      * @param {Number} startRow The index of the first row in the range
7696      * @param {Number} endRow The index of the last row in the range
7697      * @param {Boolean} keepExisting (optional) True to retain existing selections
7698      */
7699     selectRange : function(startRow, endRow, keepExisting){
7700         if(this.locked) {
7701             return;
7702         }
7703         if(!keepExisting){
7704             this.clearSelections();
7705         }
7706         if(startRow <= endRow){
7707             for(var i = startRow; i <= endRow; i++){
7708                 this.selectRow(i, true);
7709             }
7710         }else{
7711             for(var i = startRow; i >= endRow; i--){
7712                 this.selectRow(i, true);
7713             }
7714         }
7715     },
7716
7717     /**
7718      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
7719      * @param {Number} startRow The index of the first row in the range
7720      * @param {Number} endRow The index of the last row in the range
7721      */
7722     deselectRange : function(startRow, endRow, preventViewNotify){
7723         if(this.locked) {
7724             return;
7725         }
7726         for(var i = startRow; i <= endRow; i++){
7727             this.deselectRow(i, preventViewNotify);
7728         }
7729     },
7730
7731     /**
7732      * Selects a row.
7733      * @param {Number} row The index of the row to select
7734      * @param {Boolean} keepExisting (optional) True to keep existing selections
7735      */
7736     selectRow : function(index, keepExisting, preventViewNotify){
7737         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
7738             return;
7739         }
7740         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
7741             if(!keepExisting || this.singleSelect){
7742                 this.clearSelections();
7743             }
7744             var r = this.grid.ds.getAt(index);
7745             this.selections.add(r);
7746             this.last = this.lastActive = index;
7747             if(!preventViewNotify){
7748                 var view = this.grid.view ? this.grid.view : this.grid;
7749                 view.onRowSelect(index);
7750             }
7751             this.fireEvent("rowselect", this, index, r);
7752             this.fireEvent("selectionchange", this);
7753         }
7754     },
7755
7756     /**
7757      * Deselects a row.
7758      * @param {Number} row The index of the row to deselect
7759      */
7760     deselectRow : function(index, preventViewNotify){
7761         if(this.locked) {
7762             return;
7763         }
7764         if(this.last == index){
7765             this.last = false;
7766         }
7767         if(this.lastActive == index){
7768             this.lastActive = false;
7769         }
7770         var r = this.grid.ds.getAt(index);
7771         this.selections.remove(r);
7772         if(!preventViewNotify){
7773             var view = this.grid.view ? this.grid.view : this.grid;
7774             view.onRowDeselect(index);
7775         }
7776         this.fireEvent("rowdeselect", this, index);
7777         this.fireEvent("selectionchange", this);
7778     },
7779
7780     // private
7781     restoreLast : function(){
7782         if(this._last){
7783             this.last = this._last;
7784         }
7785     },
7786
7787     // private
7788     acceptsNav : function(row, col, cm){
7789         return !cm.isHidden(col) && cm.isCellEditable(col, row);
7790     },
7791
7792     // private
7793     onEditorKey : function(field, e){
7794         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
7795         if(k == e.TAB){
7796             e.stopEvent();
7797             ed.completeEdit();
7798             if(e.shiftKey){
7799                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
7800             }else{
7801                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
7802             }
7803         }else if(k == e.ENTER && !e.ctrlKey){
7804             e.stopEvent();
7805             ed.completeEdit();
7806             if(e.shiftKey){
7807                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
7808             }else{
7809                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
7810             }
7811         }else if(k == e.ESC){
7812             ed.cancelEdit();
7813         }
7814         if(newCell){
7815             g.startEditing(newCell[0], newCell[1]);
7816         }
7817     }
7818 });/*
7819  * Based on:
7820  * Ext JS Library 1.1.1
7821  * Copyright(c) 2006-2007, Ext JS, LLC.
7822  *
7823  * Originally Released Under LGPL - original licence link has changed is not relivant.
7824  *
7825  * Fork - LGPL
7826  * <script type="text/javascript">
7827  */
7828  
7829
7830 /**
7831  * @class Roo.grid.ColumnModel
7832  * @extends Roo.util.Observable
7833  * This is the default implementation of a ColumnModel used by the Grid. It defines
7834  * the columns in the grid.
7835  * <br>Usage:<br>
7836  <pre><code>
7837  var colModel = new Roo.grid.ColumnModel([
7838         {header: "Ticker", width: 60, sortable: true, locked: true},
7839         {header: "Company Name", width: 150, sortable: true},
7840         {header: "Market Cap.", width: 100, sortable: true},
7841         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7842         {header: "Employees", width: 100, sortable: true, resizable: false}
7843  ]);
7844  </code></pre>
7845  * <p>
7846  
7847  * The config options listed for this class are options which may appear in each
7848  * individual column definition.
7849  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7850  * @constructor
7851  * @param {Object} config An Array of column config objects. See this class's
7852  * config objects for details.
7853 */
7854 Roo.grid.ColumnModel = function(config){
7855         /**
7856      * The config passed into the constructor
7857      */
7858     this.config = []; //config;
7859     this.lookup = {};
7860
7861     // if no id, create one
7862     // if the column does not have a dataIndex mapping,
7863     // map it to the order it is in the config
7864     for(var i = 0, len = config.length; i < len; i++){
7865         this.addColumn(config[i]);
7866         
7867     }
7868
7869     /**
7870      * The width of columns which have no width specified (defaults to 100)
7871      * @type Number
7872      */
7873     this.defaultWidth = 100;
7874
7875     /**
7876      * Default sortable of columns which have no sortable specified (defaults to false)
7877      * @type Boolean
7878      */
7879     this.defaultSortable = false;
7880
7881     this.addEvents({
7882         /**
7883              * @event widthchange
7884              * Fires when the width of a column changes.
7885              * @param {ColumnModel} this
7886              * @param {Number} columnIndex The column index
7887              * @param {Number} newWidth The new width
7888              */
7889             "widthchange": true,
7890         /**
7891              * @event headerchange
7892              * Fires when the text of a header changes.
7893              * @param {ColumnModel} this
7894              * @param {Number} columnIndex The column index
7895              * @param {Number} newText The new header text
7896              */
7897             "headerchange": true,
7898         /**
7899              * @event hiddenchange
7900              * Fires when a column is hidden or "unhidden".
7901              * @param {ColumnModel} this
7902              * @param {Number} columnIndex The column index
7903              * @param {Boolean} hidden true if hidden, false otherwise
7904              */
7905             "hiddenchange": true,
7906             /**
7907          * @event columnmoved
7908          * Fires when a column is moved.
7909          * @param {ColumnModel} this
7910          * @param {Number} oldIndex
7911          * @param {Number} newIndex
7912          */
7913         "columnmoved" : true,
7914         /**
7915          * @event columlockchange
7916          * Fires when a column's locked state is changed
7917          * @param {ColumnModel} this
7918          * @param {Number} colIndex
7919          * @param {Boolean} locked true if locked
7920          */
7921         "columnlockchange" : true
7922     });
7923     Roo.grid.ColumnModel.superclass.constructor.call(this);
7924 };
7925 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7926     /**
7927      * @cfg {String} header The header text to display in the Grid view.
7928      */
7929     /**
7930      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7931      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7932      * specified, the column's index is used as an index into the Record's data Array.
7933      */
7934     /**
7935      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7936      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7937      */
7938     /**
7939      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7940      * Defaults to the value of the {@link #defaultSortable} property.
7941      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7942      */
7943     /**
7944      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7945      */
7946     /**
7947      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7948      */
7949     /**
7950      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7951      */
7952     /**
7953      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7954      */
7955     /**
7956      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7957      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7958      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7959      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7960      */
7961        /**
7962      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7963      */
7964     /**
7965      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7966      */
7967     /**
7968      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7969      */
7970     /**
7971      * @cfg {String} cursor (Optional)
7972      */
7973     /**
7974      * @cfg {String} tooltip (Optional)
7975      */
7976     /**
7977      * @cfg {Number} xs (Optional)
7978      */
7979     /**
7980      * @cfg {Number} sm (Optional)
7981      */
7982     /**
7983      * @cfg {Number} md (Optional)
7984      */
7985     /**
7986      * @cfg {Number} lg (Optional)
7987      */
7988     /**
7989      * Returns the id of the column at the specified index.
7990      * @param {Number} index The column index
7991      * @return {String} the id
7992      */
7993     getColumnId : function(index){
7994         return this.config[index].id;
7995     },
7996
7997     /**
7998      * Returns the column for a specified id.
7999      * @param {String} id The column id
8000      * @return {Object} the column
8001      */
8002     getColumnById : function(id){
8003         return this.lookup[id];
8004     },
8005
8006     
8007     /**
8008      * Returns the column Object for a specified dataIndex.
8009      * @param {String} dataIndex The column dataIndex
8010      * @return {Object|Boolean} the column or false if not found
8011      */
8012     getColumnByDataIndex: function(dataIndex){
8013         var index = this.findColumnIndex(dataIndex);
8014         return index > -1 ? this.config[index] : false;
8015     },
8016     
8017     /**
8018      * Returns the index for a specified column id.
8019      * @param {String} id The column id
8020      * @return {Number} the index, or -1 if not found
8021      */
8022     getIndexById : function(id){
8023         for(var i = 0, len = this.config.length; i < len; i++){
8024             if(this.config[i].id == id){
8025                 return i;
8026             }
8027         }
8028         return -1;
8029     },
8030     
8031     /**
8032      * Returns the index for a specified column dataIndex.
8033      * @param {String} dataIndex The column dataIndex
8034      * @return {Number} the index, or -1 if not found
8035      */
8036     
8037     findColumnIndex : function(dataIndex){
8038         for(var i = 0, len = this.config.length; i < len; i++){
8039             if(this.config[i].dataIndex == dataIndex){
8040                 return i;
8041             }
8042         }
8043         return -1;
8044     },
8045     
8046     
8047     moveColumn : function(oldIndex, newIndex){
8048         var c = this.config[oldIndex];
8049         this.config.splice(oldIndex, 1);
8050         this.config.splice(newIndex, 0, c);
8051         this.dataMap = null;
8052         this.fireEvent("columnmoved", this, oldIndex, newIndex);
8053     },
8054
8055     isLocked : function(colIndex){
8056         return this.config[colIndex].locked === true;
8057     },
8058
8059     setLocked : function(colIndex, value, suppressEvent){
8060         if(this.isLocked(colIndex) == value){
8061             return;
8062         }
8063         this.config[colIndex].locked = value;
8064         if(!suppressEvent){
8065             this.fireEvent("columnlockchange", this, colIndex, value);
8066         }
8067     },
8068
8069     getTotalLockedWidth : function(){
8070         var totalWidth = 0;
8071         for(var i = 0; i < this.config.length; i++){
8072             if(this.isLocked(i) && !this.isHidden(i)){
8073                 this.totalWidth += this.getColumnWidth(i);
8074             }
8075         }
8076         return totalWidth;
8077     },
8078
8079     getLockedCount : function(){
8080         for(var i = 0, len = this.config.length; i < len; i++){
8081             if(!this.isLocked(i)){
8082                 return i;
8083             }
8084         }
8085         
8086         return this.config.length;
8087     },
8088
8089     /**
8090      * Returns the number of columns.
8091      * @return {Number}
8092      */
8093     getColumnCount : function(visibleOnly){
8094         if(visibleOnly === true){
8095             var c = 0;
8096             for(var i = 0, len = this.config.length; i < len; i++){
8097                 if(!this.isHidden(i)){
8098                     c++;
8099                 }
8100             }
8101             return c;
8102         }
8103         return this.config.length;
8104     },
8105
8106     /**
8107      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
8108      * @param {Function} fn
8109      * @param {Object} scope (optional)
8110      * @return {Array} result
8111      */
8112     getColumnsBy : function(fn, scope){
8113         var r = [];
8114         for(var i = 0, len = this.config.length; i < len; i++){
8115             var c = this.config[i];
8116             if(fn.call(scope||this, c, i) === true){
8117                 r[r.length] = c;
8118             }
8119         }
8120         return r;
8121     },
8122
8123     /**
8124      * Returns true if the specified column is sortable.
8125      * @param {Number} col The column index
8126      * @return {Boolean}
8127      */
8128     isSortable : function(col){
8129         if(typeof this.config[col].sortable == "undefined"){
8130             return this.defaultSortable;
8131         }
8132         return this.config[col].sortable;
8133     },
8134
8135     /**
8136      * Returns the rendering (formatting) function defined for the column.
8137      * @param {Number} col The column index.
8138      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
8139      */
8140     getRenderer : function(col){
8141         if(!this.config[col].renderer){
8142             return Roo.grid.ColumnModel.defaultRenderer;
8143         }
8144         return this.config[col].renderer;
8145     },
8146
8147     /**
8148      * Sets the rendering (formatting) function for a column.
8149      * @param {Number} col The column index
8150      * @param {Function} fn The function to use to process the cell's raw data
8151      * to return HTML markup for the grid view. The render function is called with
8152      * the following parameters:<ul>
8153      * <li>Data value.</li>
8154      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
8155      * <li>css A CSS style string to apply to the table cell.</li>
8156      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
8157      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
8158      * <li>Row index</li>
8159      * <li>Column index</li>
8160      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
8161      */
8162     setRenderer : function(col, fn){
8163         this.config[col].renderer = fn;
8164     },
8165
8166     /**
8167      * Returns the width for the specified column.
8168      * @param {Number} col The column index
8169      * @return {Number}
8170      */
8171     getColumnWidth : function(col){
8172         return this.config[col].width * 1 || this.defaultWidth;
8173     },
8174
8175     /**
8176      * Sets the width for a column.
8177      * @param {Number} col The column index
8178      * @param {Number} width The new width
8179      */
8180     setColumnWidth : function(col, width, suppressEvent){
8181         this.config[col].width = width;
8182         this.totalWidth = null;
8183         if(!suppressEvent){
8184              this.fireEvent("widthchange", this, col, width);
8185         }
8186     },
8187
8188     /**
8189      * Returns the total width of all columns.
8190      * @param {Boolean} includeHidden True to include hidden column widths
8191      * @return {Number}
8192      */
8193     getTotalWidth : function(includeHidden){
8194         if(!this.totalWidth){
8195             this.totalWidth = 0;
8196             for(var i = 0, len = this.config.length; i < len; i++){
8197                 if(includeHidden || !this.isHidden(i)){
8198                     this.totalWidth += this.getColumnWidth(i);
8199                 }
8200             }
8201         }
8202         return this.totalWidth;
8203     },
8204
8205     /**
8206      * Returns the header for the specified column.
8207      * @param {Number} col The column index
8208      * @return {String}
8209      */
8210     getColumnHeader : function(col){
8211         return this.config[col].header;
8212     },
8213
8214     /**
8215      * Sets the header for a column.
8216      * @param {Number} col The column index
8217      * @param {String} header The new header
8218      */
8219     setColumnHeader : function(col, header){
8220         this.config[col].header = header;
8221         this.fireEvent("headerchange", this, col, header);
8222     },
8223
8224     /**
8225      * Returns the tooltip for the specified column.
8226      * @param {Number} col The column index
8227      * @return {String}
8228      */
8229     getColumnTooltip : function(col){
8230             return this.config[col].tooltip;
8231     },
8232     /**
8233      * Sets the tooltip for a column.
8234      * @param {Number} col The column index
8235      * @param {String} tooltip The new tooltip
8236      */
8237     setColumnTooltip : function(col, tooltip){
8238             this.config[col].tooltip = tooltip;
8239     },
8240
8241     /**
8242      * Returns the dataIndex for the specified column.
8243      * @param {Number} col The column index
8244      * @return {Number}
8245      */
8246     getDataIndex : function(col){
8247         return this.config[col].dataIndex;
8248     },
8249
8250     /**
8251      * Sets the dataIndex for a column.
8252      * @param {Number} col The column index
8253      * @param {Number} dataIndex The new dataIndex
8254      */
8255     setDataIndex : function(col, dataIndex){
8256         this.config[col].dataIndex = dataIndex;
8257     },
8258
8259     
8260     
8261     /**
8262      * Returns true if the cell is editable.
8263      * @param {Number} colIndex The column index
8264      * @param {Number} rowIndex The row index - this is nto actually used..?
8265      * @return {Boolean}
8266      */
8267     isCellEditable : function(colIndex, rowIndex){
8268         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
8269     },
8270
8271     /**
8272      * Returns the editor defined for the cell/column.
8273      * return false or null to disable editing.
8274      * @param {Number} colIndex The column index
8275      * @param {Number} rowIndex The row index
8276      * @return {Object}
8277      */
8278     getCellEditor : function(colIndex, rowIndex){
8279         return this.config[colIndex].editor;
8280     },
8281
8282     /**
8283      * Sets if a column is editable.
8284      * @param {Number} col The column index
8285      * @param {Boolean} editable True if the column is editable
8286      */
8287     setEditable : function(col, editable){
8288         this.config[col].editable = editable;
8289     },
8290
8291
8292     /**
8293      * Returns true if the column is hidden.
8294      * @param {Number} colIndex The column index
8295      * @return {Boolean}
8296      */
8297     isHidden : function(colIndex){
8298         return this.config[colIndex].hidden;
8299     },
8300
8301
8302     /**
8303      * Returns true if the column width cannot be changed
8304      */
8305     isFixed : function(colIndex){
8306         return this.config[colIndex].fixed;
8307     },
8308
8309     /**
8310      * Returns true if the column can be resized
8311      * @return {Boolean}
8312      */
8313     isResizable : function(colIndex){
8314         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
8315     },
8316     /**
8317      * Sets if a column is hidden.
8318      * @param {Number} colIndex The column index
8319      * @param {Boolean} hidden True if the column is hidden
8320      */
8321     setHidden : function(colIndex, hidden){
8322         this.config[colIndex].hidden = hidden;
8323         this.totalWidth = null;
8324         this.fireEvent("hiddenchange", this, colIndex, hidden);
8325     },
8326
8327     /**
8328      * Sets the editor for a column.
8329      * @param {Number} col The column index
8330      * @param {Object} editor The editor object
8331      */
8332     setEditor : function(col, editor){
8333         this.config[col].editor = editor;
8334     },
8335     /**
8336      * Add a column (experimental...) - defaults to adding to the end..
8337      * @param {Object} config 
8338     */
8339     addColumn : function(c)
8340     {
8341     
8342         var i = this.config.length;
8343         this.config[i] = c;
8344         
8345         if(typeof c.dataIndex == "undefined"){
8346             c.dataIndex = i;
8347         }
8348         if(typeof c.renderer == "string"){
8349             c.renderer = Roo.util.Format[c.renderer];
8350         }
8351         if(typeof c.id == "undefined"){
8352             c.id = Roo.id();
8353         }
8354         if(c.editor && c.editor.xtype){
8355             c.editor  = Roo.factory(c.editor, Roo.grid);
8356         }
8357         if(c.editor && c.editor.isFormField){
8358             c.editor = new Roo.grid.GridEditor(c.editor);
8359         }
8360         this.lookup[c.id] = c;
8361     }
8362     
8363 });
8364
8365 Roo.grid.ColumnModel.defaultRenderer = function(value)
8366 {
8367     if(typeof value == "object") {
8368         return value;
8369     }
8370         if(typeof value == "string" && value.length < 1){
8371             return "&#160;";
8372         }
8373     
8374         return String.format("{0}", value);
8375 };
8376
8377 // Alias for backwards compatibility
8378 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
8379 /*
8380  * Based on:
8381  * Ext JS Library 1.1.1
8382  * Copyright(c) 2006-2007, Ext JS, LLC.
8383  *
8384  * Originally Released Under LGPL - original licence link has changed is not relivant.
8385  *
8386  * Fork - LGPL
8387  * <script type="text/javascript">
8388  */
8389  
8390 /**
8391  * @class Roo.LoadMask
8392  * A simple utility class for generically masking elements while loading data.  If the element being masked has
8393  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
8394  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
8395  * element's UpdateManager load indicator and will be destroyed after the initial load.
8396  * @constructor
8397  * Create a new LoadMask
8398  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
8399  * @param {Object} config The config object
8400  */
8401 Roo.LoadMask = function(el, config){
8402     this.el = Roo.get(el);
8403     Roo.apply(this, config);
8404     if(this.store){
8405         this.store.on('beforeload', this.onBeforeLoad, this);
8406         this.store.on('load', this.onLoad, this);
8407         this.store.on('loadexception', this.onLoadException, this);
8408         this.removeMask = false;
8409     }else{
8410         var um = this.el.getUpdateManager();
8411         um.showLoadIndicator = false; // disable the default indicator
8412         um.on('beforeupdate', this.onBeforeLoad, this);
8413         um.on('update', this.onLoad, this);
8414         um.on('failure', this.onLoad, this);
8415         this.removeMask = true;
8416     }
8417 };
8418
8419 Roo.LoadMask.prototype = {
8420     /**
8421      * @cfg {Boolean} removeMask
8422      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
8423      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
8424      */
8425     /**
8426      * @cfg {String} msg
8427      * The text to display in a centered loading message box (defaults to 'Loading...')
8428      */
8429     msg : 'Loading...',
8430     /**
8431      * @cfg {String} msgCls
8432      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
8433      */
8434     msgCls : 'x-mask-loading',
8435
8436     /**
8437      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
8438      * @type Boolean
8439      */
8440     disabled: false,
8441
8442     /**
8443      * Disables the mask to prevent it from being displayed
8444      */
8445     disable : function(){
8446        this.disabled = true;
8447     },
8448
8449     /**
8450      * Enables the mask so that it can be displayed
8451      */
8452     enable : function(){
8453         this.disabled = false;
8454     },
8455     
8456     onLoadException : function()
8457     {
8458         Roo.log(arguments);
8459         
8460         if (typeof(arguments[3]) != 'undefined') {
8461             Roo.MessageBox.alert("Error loading",arguments[3]);
8462         } 
8463         /*
8464         try {
8465             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
8466                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
8467             }   
8468         } catch(e) {
8469             
8470         }
8471         */
8472     
8473         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8474     },
8475     // private
8476     onLoad : function()
8477     {
8478         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
8479     },
8480
8481     // private
8482     onBeforeLoad : function(){
8483         if(!this.disabled){
8484             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
8485         }
8486     },
8487
8488     // private
8489     destroy : function(){
8490         if(this.store){
8491             this.store.un('beforeload', this.onBeforeLoad, this);
8492             this.store.un('load', this.onLoad, this);
8493             this.store.un('loadexception', this.onLoadException, this);
8494         }else{
8495             var um = this.el.getUpdateManager();
8496             um.un('beforeupdate', this.onBeforeLoad, this);
8497             um.un('update', this.onLoad, this);
8498             um.un('failure', this.onLoad, this);
8499         }
8500     }
8501 };/**
8502  * @class Roo.bootstrap.Table
8503  * @licence LGBL
8504  * @extends Roo.bootstrap.Component
8505  * Bootstrap Table class.  This class represents the primary interface of a component based grid control.
8506  * Similar to Roo.grid.Grid
8507  * <pre><code>
8508  var table = Roo.factory({
8509     xtype : 'Table',
8510     xns : Roo.bootstrap,
8511     autoSizeColumns: true,
8512     
8513     
8514     store : {
8515         xtype : 'Store',
8516         xns : Roo.data,
8517         remoteSort : true,
8518         sortInfo : { direction : 'ASC', field: 'name' },
8519         proxy : {
8520            xtype : 'HttpProxy',
8521            xns : Roo.data,
8522            method : 'GET',
8523            url : 'https://example.com/some.data.url.json'
8524         },
8525         reader : {
8526            xtype : 'JsonReader',
8527            xns : Roo.data,
8528            fields : [ 'id', 'name', whatever' ],
8529            id : 'id',
8530            root : 'data'
8531         }
8532     },
8533     cm : [
8534         {
8535             xtype : 'ColumnModel',
8536             xns : Roo.grid,
8537             align : 'center',
8538             cursor : 'pointer',
8539             dataIndex : 'is_in_group',
8540             header : "Name",
8541             sortable : true,
8542             renderer : function(v, x , r) {  
8543             
8544                 return String.format("{0}", v)
8545             }
8546             width : 3
8547         } // more columns..
8548     ],
8549     selModel : {
8550         xtype : 'RowSelectionModel',
8551         xns : Roo.bootstrap.Table
8552         // you can add listeners to catch selection change here....
8553     }
8554      
8555
8556  });
8557  // set any options
8558  grid.render(Roo.get("some-div"));
8559 </code></pre>
8560
8561 Currently the Table  uses multiple headers to try and handle XL / Medium etc... styling
8562
8563
8564
8565  *
8566  * @cfg {Roo.grid.RowSelectionModel|Roo.grid.CellSelectionModel} sm The selection model to use (cell selection is not supported yet)
8567  * @cfg {Roo.data.Store|Roo.data.SimpleStore} store The data store to use
8568  * @cfg {Roo.grid.ColumnModel} cm[] A column for th grid.
8569  * 
8570  * @cfg {String} cls table class
8571  *
8572  * 
8573  * @cfg {boolean} striped Should the rows be alternative striped
8574  * @cfg {boolean} bordered Add borders to the table
8575  * @cfg {boolean} hover Add hover highlighting
8576  * @cfg {boolean} condensed Format condensed
8577  * @cfg {boolean} responsive Format condensed
8578  * @cfg {Boolean} loadMask (true|false) default false
8579  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
8580  * @cfg {Boolean} headerShow (true|false) generate thead, default true
8581  * @cfg {Boolean} rowSelection (true|false) default false
8582  * @cfg {Boolean} cellSelection (true|false) default false
8583  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
8584  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
8585  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
8586  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
8587  
8588  * 
8589  * @constructor
8590  * Create a new Table
8591  * @param {Object} config The config object
8592  */
8593
8594 Roo.bootstrap.Table = function(config)
8595 {
8596     Roo.bootstrap.Table.superclass.constructor.call(this, config);
8597      
8598     // BC...
8599     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8600     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8601     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8602     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8603     
8604     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8605     if (this.sm) {
8606         this.sm.grid = this;
8607         this.selModel = Roo.factory(this.sm, Roo.grid);
8608         this.sm = this.selModel;
8609         this.sm.xmodule = this.xmodule || false;
8610     }
8611     
8612     if (this.cm && typeof(this.cm.config) == 'undefined') {
8613         this.colModel = new Roo.grid.ColumnModel(this.cm);
8614         this.cm = this.colModel;
8615         this.cm.xmodule = this.xmodule || false;
8616     }
8617     if (this.store) {
8618         this.store= Roo.factory(this.store, Roo.data);
8619         this.ds = this.store;
8620         this.ds.xmodule = this.xmodule || false;
8621          
8622     }
8623     if (this.footer && this.store) {
8624         this.footer.dataSource = this.ds;
8625         this.footer = Roo.factory(this.footer);
8626     }
8627     
8628     /** @private */
8629     this.addEvents({
8630         /**
8631          * @event cellclick
8632          * Fires when a cell is clicked
8633          * @param {Roo.bootstrap.Table} this
8634          * @param {Roo.Element} el
8635          * @param {Number} rowIndex
8636          * @param {Number} columnIndex
8637          * @param {Roo.EventObject} e
8638          */
8639         "cellclick" : true,
8640         /**
8641          * @event celldblclick
8642          * Fires when a cell is double clicked
8643          * @param {Roo.bootstrap.Table} this
8644          * @param {Roo.Element} el
8645          * @param {Number} rowIndex
8646          * @param {Number} columnIndex
8647          * @param {Roo.EventObject} e
8648          */
8649         "celldblclick" : true,
8650         /**
8651          * @event rowclick
8652          * Fires when a row is clicked
8653          * @param {Roo.bootstrap.Table} this
8654          * @param {Roo.Element} el
8655          * @param {Number} rowIndex
8656          * @param {Roo.EventObject} e
8657          */
8658         "rowclick" : true,
8659         /**
8660          * @event rowdblclick
8661          * Fires when a row is double clicked
8662          * @param {Roo.bootstrap.Table} this
8663          * @param {Roo.Element} el
8664          * @param {Number} rowIndex
8665          * @param {Roo.EventObject} e
8666          */
8667         "rowdblclick" : true,
8668         /**
8669          * @event mouseover
8670          * Fires when a mouseover occur
8671          * @param {Roo.bootstrap.Table} this
8672          * @param {Roo.Element} el
8673          * @param {Number} rowIndex
8674          * @param {Number} columnIndex
8675          * @param {Roo.EventObject} e
8676          */
8677         "mouseover" : true,
8678         /**
8679          * @event mouseout
8680          * Fires when a mouseout occur
8681          * @param {Roo.bootstrap.Table} this
8682          * @param {Roo.Element} el
8683          * @param {Number} rowIndex
8684          * @param {Number} columnIndex
8685          * @param {Roo.EventObject} e
8686          */
8687         "mouseout" : true,
8688         /**
8689          * @event rowclass
8690          * Fires when a row is rendered, so you can change add a style to it.
8691          * @param {Roo.bootstrap.Table} this
8692          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8693          */
8694         'rowclass' : true,
8695           /**
8696          * @event rowsrendered
8697          * Fires when all the  rows have been rendered
8698          * @param {Roo.bootstrap.Table} this
8699          */
8700         'rowsrendered' : true,
8701         /**
8702          * @event contextmenu
8703          * The raw contextmenu event for the entire grid.
8704          * @param {Roo.EventObject} e
8705          */
8706         "contextmenu" : true,
8707         /**
8708          * @event rowcontextmenu
8709          * Fires when a row is right clicked
8710          * @param {Roo.bootstrap.Table} this
8711          * @param {Number} rowIndex
8712          * @param {Roo.EventObject} e
8713          */
8714         "rowcontextmenu" : true,
8715         /**
8716          * @event cellcontextmenu
8717          * Fires when a cell is right clicked
8718          * @param {Roo.bootstrap.Table} this
8719          * @param {Number} rowIndex
8720          * @param {Number} cellIndex
8721          * @param {Roo.EventObject} e
8722          */
8723          "cellcontextmenu" : true,
8724          /**
8725          * @event headercontextmenu
8726          * Fires when a header is right clicked
8727          * @param {Roo.bootstrap.Table} this
8728          * @param {Number} columnIndex
8729          * @param {Roo.EventObject} e
8730          */
8731         "headercontextmenu" : true,
8732         /**
8733          * @event mousedown
8734          * The raw mousedown event for the entire grid.
8735          * @param {Roo.EventObject} e
8736          */
8737         "mousedown" : true
8738         
8739     });
8740 };
8741
8742 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8743     
8744     cls: false,
8745     
8746     striped : false,
8747     scrollBody : false,
8748     bordered: false,
8749     hover:  false,
8750     condensed : false,
8751     responsive : false,
8752     sm : false,
8753     cm : false,
8754     store : false,
8755     loadMask : false,
8756     footerShow : true,
8757     headerShow : true,
8758   
8759     rowSelection : false,
8760     cellSelection : false,
8761     layout : false,
8762     
8763     // Roo.Element - the tbody
8764     mainBody: false,
8765     // Roo.Element - thead element
8766     mainHead: false,
8767     
8768     container: false, // used by gridpanel...
8769     
8770     lazyLoad : false,
8771     
8772     CSS : Roo.util.CSS,
8773     
8774     auto_hide_footer : false,
8775     
8776     getAutoCreate : function()
8777     {
8778         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8779         
8780         cfg = {
8781             tag: 'table',
8782             cls : 'table',
8783             cn : []
8784         };
8785         // this get's auto added by panel.Grid
8786         if (this.scrollBody) {
8787             cfg.cls += ' table-body-fixed';
8788         }    
8789         if (this.striped) {
8790             cfg.cls += ' table-striped';
8791         }
8792         
8793         if (this.hover) {
8794             cfg.cls += ' table-hover';
8795         }
8796         if (this.bordered) {
8797             cfg.cls += ' table-bordered';
8798         }
8799         if (this.condensed) {
8800             cfg.cls += ' table-condensed';
8801         }
8802         
8803         if (this.responsive) {
8804             cfg.cls += ' table-responsive';
8805         }
8806         
8807         if (this.cls) {
8808             cfg.cls+=  ' ' +this.cls;
8809         }
8810         
8811         
8812         
8813         if (this.layout) {
8814             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8815         }
8816         
8817         if(this.store || this.cm){
8818             if(this.headerShow){
8819                 cfg.cn.push(this.renderHeader());
8820             }
8821             
8822             cfg.cn.push(this.renderBody());
8823             
8824             if(this.footerShow){
8825                 cfg.cn.push(this.renderFooter());
8826             }
8827             // where does this come from?
8828             //cfg.cls+=  ' TableGrid';
8829         }
8830         
8831         return { cn : [ cfg ] };
8832     },
8833     
8834     initEvents : function()
8835     {   
8836         if(!this.store || !this.cm){
8837             return;
8838         }
8839         if (this.selModel) {
8840             this.selModel.initEvents();
8841         }
8842         
8843         
8844         //Roo.log('initEvents with ds!!!!');
8845         
8846         this.mainBody = this.el.select('tbody', true).first();
8847         this.mainHead = this.el.select('thead', true).first();
8848         this.mainFoot = this.el.select('tfoot', true).first();
8849         
8850         
8851         
8852         
8853         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8854             e.on('click', this.sort, this);
8855         }, this);
8856         
8857         
8858         // why is this done????? = it breaks dialogs??
8859         //this.parent().el.setStyle('position', 'relative');
8860         
8861         
8862         if (this.footer) {
8863             this.footer.parentId = this.id;
8864             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8865             
8866             if(this.lazyLoad){
8867                 this.el.select('tfoot tr td').first().addClass('hide');
8868             }
8869         } 
8870         
8871         if(this.loadMask) {
8872             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8873         }
8874         
8875         this.store.on('load', this.onLoad, this);
8876         this.store.on('beforeload', this.onBeforeLoad, this);
8877         this.store.on('update', this.onUpdate, this);
8878         this.store.on('add', this.onAdd, this);
8879         this.store.on("clear", this.clear, this);
8880         
8881         this.el.on("contextmenu", this.onContextMenu, this);
8882         
8883         
8884         this.cm.on("headerchange", this.onHeaderChange, this);
8885         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8886
8887         
8888         this.mainBody.on("click", this.onClick, this);
8889         this.mainBody.on("dblclick", this.onDblClick, this);        
8890         this.mainBody.on('scroll', this.onBodyScroll, this);
8891
8892         // guessing mainbody will work - this relays usually caught by selmodel at present.
8893         this.relayEvents(this.mainBody, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
8894   
8895         
8896         
8897         
8898     },
8899     
8900     onContextMenu : function(e, t)
8901     {
8902         this.processEvent("contextmenu", e);
8903     },
8904     
8905     processEvent : function(name, e)
8906     {
8907         if (name != 'touchstart' ) {
8908             this.fireEvent(name, e);    
8909         }
8910         
8911         var t = e.getTarget();
8912         
8913         var cell = Roo.get(t);
8914         
8915         if(!cell){
8916             return;
8917         }
8918         
8919         if(cell.findParent('tfoot', false, true)){
8920             return;
8921         }
8922         
8923         if(cell.findParent('thead', false, true)){
8924             
8925             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8926                 cell = Roo.get(t).findParent('th', false, true);
8927                 if (!cell) {
8928                     Roo.log("failed to find th in thead?");
8929                     Roo.log(e.getTarget());
8930                     return;
8931                 }
8932             }
8933             
8934             var cellIndex = cell.dom.cellIndex;
8935             
8936             var ename = name == 'touchstart' ? 'click' : name;
8937             this.fireEvent("header" + ename, this, cellIndex, e);
8938             
8939             return;
8940         }
8941         
8942         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8943             cell = Roo.get(t).findParent('td', false, true);
8944             if (!cell) {
8945                 Roo.log("failed to find th in tbody?");
8946                 Roo.log(e.getTarget());
8947                 return;
8948             }
8949         }
8950         
8951         var row = cell.findParent('tr', false, true);
8952         var cellIndex = cell.dom.cellIndex;
8953         var rowIndex = row.dom.rowIndex - 1;
8954         
8955         if(row !== false){
8956             
8957             this.fireEvent("row" + name, this, rowIndex, e);
8958             
8959             if(cell !== false){
8960             
8961                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8962             }
8963         }
8964         
8965     },
8966     
8967     onMouseover : function(e, el)
8968     {
8969         var cell = Roo.get(el);
8970         
8971         if(!cell){
8972             return;
8973         }
8974         
8975         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8976             cell = cell.findParent('td', false, true);
8977         }
8978         
8979         var row = cell.findParent('tr', false, true);
8980         var cellIndex = cell.dom.cellIndex;
8981         var rowIndex = row.dom.rowIndex - 1; // start from 0
8982         
8983         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8984         
8985     },
8986     
8987     onMouseout : function(e, el)
8988     {
8989         var cell = Roo.get(el);
8990         
8991         if(!cell){
8992             return;
8993         }
8994         
8995         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8996             cell = cell.findParent('td', false, true);
8997         }
8998         
8999         var row = cell.findParent('tr', false, true);
9000         var cellIndex = cell.dom.cellIndex;
9001         var rowIndex = row.dom.rowIndex - 1; // start from 0
9002         
9003         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
9004         
9005     },
9006     
9007     onClick : function(e, el)
9008     {
9009         var cell = Roo.get(el);
9010         
9011         if(!cell || (!this.cellSelection && !this.rowSelection)){
9012             return;
9013         }
9014         
9015         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9016             cell = cell.findParent('td', false, true);
9017         }
9018         
9019         if(!cell || typeof(cell) == 'undefined'){
9020             return;
9021         }
9022         
9023         var row = cell.findParent('tr', false, true);
9024         
9025         if(!row || typeof(row) == 'undefined'){
9026             return;
9027         }
9028         
9029         var cellIndex = cell.dom.cellIndex;
9030         var rowIndex = this.getRowIndex(row);
9031         
9032         // why??? - should these not be based on SelectionModel?
9033         //if(this.cellSelection){
9034             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
9035         //}
9036         
9037         //if(this.rowSelection){
9038             this.fireEvent('rowclick', this, row, rowIndex, e);
9039         //}
9040          
9041     },
9042         
9043     onDblClick : function(e,el)
9044     {
9045         var cell = Roo.get(el);
9046         
9047         if(!cell || (!this.cellSelection && !this.rowSelection)){
9048             return;
9049         }
9050         
9051         if(e.getTarget().nodeName.toLowerCase() != 'td'){
9052             cell = cell.findParent('td', false, true);
9053         }
9054         
9055         if(!cell || typeof(cell) == 'undefined'){
9056             return;
9057         }
9058         
9059         var row = cell.findParent('tr', false, true);
9060         
9061         if(!row || typeof(row) == 'undefined'){
9062             return;
9063         }
9064         
9065         var cellIndex = cell.dom.cellIndex;
9066         var rowIndex = this.getRowIndex(row);
9067         
9068         if(this.cellSelection){
9069             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
9070         }
9071         
9072         if(this.rowSelection){
9073             this.fireEvent('rowdblclick', this, row, rowIndex, e);
9074         }
9075     },
9076     findRowIndex : function(el)
9077     {
9078         var cell = Roo.get(el);
9079         if(!cell) {
9080             return false;
9081         }
9082         var row = cell.findParent('tr', false, true);
9083         
9084         if(!row || typeof(row) == 'undefined'){
9085             return false;
9086         }
9087         return this.getRowIndex(row);
9088     },
9089     sort : function(e,el)
9090     {
9091         var col = Roo.get(el);
9092         
9093         if(!col.hasClass('sortable')){
9094             return;
9095         }
9096         
9097         var sort = col.attr('sort');
9098         var dir = 'ASC';
9099         
9100         if(col.select('i', true).first().hasClass('fa-arrow-up')){
9101             dir = 'DESC';
9102         }
9103         
9104         this.store.sortInfo = {field : sort, direction : dir};
9105         
9106         if (this.footer) {
9107             Roo.log("calling footer first");
9108             this.footer.onClick('first');
9109         } else {
9110         
9111             this.store.load({ params : { start : 0 } });
9112         }
9113     },
9114     
9115     renderHeader : function()
9116     {
9117         var header = {
9118             tag: 'thead',
9119             cn : []
9120         };
9121         
9122         var cm = this.cm;
9123         this.totalWidth = 0;
9124         
9125         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9126             
9127             var config = cm.config[i];
9128             
9129             var c = {
9130                 tag: 'th',
9131                 cls : 'x-hcol-' + i,
9132                 style : '',
9133                 
9134                 html: cm.getColumnHeader(i)
9135             };
9136             
9137             var tooltip = cm.getColumnTooltip(i);
9138             if (tooltip) {
9139                 c.tooltip = tooltip;
9140             }
9141             
9142             
9143             var hh = '';
9144             
9145             if(typeof(config.sortable) != 'undefined' && config.sortable){
9146                 c.cls = 'sortable';
9147                 c.html = '<i class="fa"></i>' + c.html;
9148             }
9149             
9150             // could use BS4 hidden-..-down 
9151             
9152             if(typeof(config.lgHeader) != 'undefined'){
9153                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
9154             }
9155             
9156             if(typeof(config.mdHeader) != 'undefined'){
9157                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
9158             }
9159             
9160             if(typeof(config.smHeader) != 'undefined'){
9161                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
9162             }
9163             
9164             if(typeof(config.xsHeader) != 'undefined'){
9165                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
9166             }
9167             
9168             if(hh.length){
9169                 c.html = hh;
9170             }
9171             
9172             if(typeof(config.tooltip) != 'undefined'){
9173                 c.tooltip = config.tooltip;
9174             }
9175             
9176             if(typeof(config.colspan) != 'undefined'){
9177                 c.colspan = config.colspan;
9178             }
9179             
9180             if(typeof(config.hidden) != 'undefined' && config.hidden){
9181                 c.style += ' display:none;';
9182             }
9183             
9184             if(typeof(config.dataIndex) != 'undefined'){
9185                 c.sort = config.dataIndex;
9186             }
9187             
9188            
9189             
9190             if(typeof(config.align) != 'undefined' && config.align.length){
9191                 c.style += ' text-align:' + config.align + ';';
9192             }
9193             
9194             if(typeof(config.width) != 'undefined'){
9195                 c.style += ' width:' + config.width + 'px;';
9196                 this.totalWidth += config.width;
9197             } else {
9198                 this.totalWidth += 100; // assume minimum of 100 per column?
9199             }
9200             
9201             if(typeof(config.cls) != 'undefined'){
9202                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
9203             }
9204             
9205             ['xs','sm','md','lg'].map(function(size){
9206                 
9207                 if(typeof(config[size]) == 'undefined'){
9208                     return;
9209                 }
9210                  
9211                 if (!config[size]) { // 0 = hidden
9212                     // BS 4 '0' is treated as hide that column and below.
9213                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
9214                     return;
9215                 }
9216                 
9217                 c.cls += ' col-' + size + '-' + config[size] + (
9218                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
9219                 );
9220                 
9221                 
9222             });
9223             
9224             header.cn.push(c)
9225         }
9226         
9227         return header;
9228     },
9229     
9230     renderBody : function()
9231     {
9232         var body = {
9233             tag: 'tbody',
9234             cn : [
9235                 {
9236                     tag: 'tr',
9237                     cn : [
9238                         {
9239                             tag : 'td',
9240                             colspan :  this.cm.getColumnCount()
9241                         }
9242                     ]
9243                 }
9244             ]
9245         };
9246         
9247         return body;
9248     },
9249     
9250     renderFooter : function()
9251     {
9252         var footer = {
9253             tag: 'tfoot',
9254             cn : [
9255                 {
9256                     tag: 'tr',
9257                     cn : [
9258                         {
9259                             tag : 'td',
9260                             colspan :  this.cm.getColumnCount()
9261                         }
9262                     ]
9263                 }
9264             ]
9265         };
9266         
9267         return footer;
9268     },
9269     
9270     
9271     
9272     onLoad : function()
9273     {
9274 //        Roo.log('ds onload');
9275         this.clear();
9276         
9277         var _this = this;
9278         var cm = this.cm;
9279         var ds = this.store;
9280         
9281         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9282             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
9283             if (_this.store.sortInfo) {
9284                     
9285                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
9286                     e.select('i', true).addClass(['fa-arrow-up']);
9287                 }
9288                 
9289                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
9290                     e.select('i', true).addClass(['fa-arrow-down']);
9291                 }
9292             }
9293         });
9294         
9295         var tbody =  this.mainBody;
9296               
9297         if(ds.getCount() > 0){
9298             ds.data.each(function(d,rowIndex){
9299                 var row =  this.renderRow(cm, ds, rowIndex);
9300                 
9301                 tbody.createChild(row);
9302                 
9303                 var _this = this;
9304                 
9305                 if(row.cellObjects.length){
9306                     Roo.each(row.cellObjects, function(r){
9307                         _this.renderCellObject(r);
9308                     })
9309                 }
9310                 
9311             }, this);
9312         }
9313         
9314         var tfoot = this.el.select('tfoot', true).first();
9315         
9316         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
9317             
9318             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
9319             
9320             var total = this.ds.getTotalCount();
9321             
9322             if(this.footer.pageSize < total){
9323                 this.mainFoot.show();
9324             }
9325         }
9326         
9327         Roo.each(this.el.select('tbody td', true).elements, function(e){
9328             e.on('mouseover', _this.onMouseover, _this);
9329         });
9330         
9331         Roo.each(this.el.select('tbody td', true).elements, function(e){
9332             e.on('mouseout', _this.onMouseout, _this);
9333         });
9334         this.fireEvent('rowsrendered', this);
9335         
9336         this.autoSize();
9337     },
9338     
9339     
9340     onUpdate : function(ds,record)
9341     {
9342         this.refreshRow(record);
9343         this.autoSize();
9344     },
9345     
9346     onRemove : function(ds, record, index, isUpdate){
9347         if(isUpdate !== true){
9348             this.fireEvent("beforerowremoved", this, index, record);
9349         }
9350         var bt = this.mainBody.dom;
9351         
9352         var rows = this.el.select('tbody > tr', true).elements;
9353         
9354         if(typeof(rows[index]) != 'undefined'){
9355             bt.removeChild(rows[index].dom);
9356         }
9357         
9358 //        if(bt.rows[index]){
9359 //            bt.removeChild(bt.rows[index]);
9360 //        }
9361         
9362         if(isUpdate !== true){
9363             //this.stripeRows(index);
9364             //this.syncRowHeights(index, index);
9365             //this.layout();
9366             this.fireEvent("rowremoved", this, index, record);
9367         }
9368     },
9369     
9370     onAdd : function(ds, records, rowIndex)
9371     {
9372         //Roo.log('on Add called');
9373         // - note this does not handle multiple adding very well..
9374         var bt = this.mainBody.dom;
9375         for (var i =0 ; i < records.length;i++) {
9376             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
9377             //Roo.log(records[i]);
9378             //Roo.log(this.store.getAt(rowIndex+i));
9379             this.insertRow(this.store, rowIndex + i, false);
9380             return;
9381         }
9382         
9383     },
9384     
9385     
9386     refreshRow : function(record){
9387         var ds = this.store, index;
9388         if(typeof record == 'number'){
9389             index = record;
9390             record = ds.getAt(index);
9391         }else{
9392             index = ds.indexOf(record);
9393             if (index < 0) {
9394                 return; // should not happen - but seems to 
9395             }
9396         }
9397         this.insertRow(ds, index, true);
9398         this.autoSize();
9399         this.onRemove(ds, record, index+1, true);
9400         this.autoSize();
9401         //this.syncRowHeights(index, index);
9402         //this.layout();
9403         this.fireEvent("rowupdated", this, index, record);
9404     },
9405     
9406     insertRow : function(dm, rowIndex, isUpdate){
9407         
9408         if(!isUpdate){
9409             this.fireEvent("beforerowsinserted", this, rowIndex);
9410         }
9411             //var s = this.getScrollState();
9412         var row = this.renderRow(this.cm, this.store, rowIndex);
9413         // insert before rowIndex..
9414         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
9415         
9416         var _this = this;
9417                 
9418         if(row.cellObjects.length){
9419             Roo.each(row.cellObjects, function(r){
9420                 _this.renderCellObject(r);
9421             })
9422         }
9423             
9424         if(!isUpdate){
9425             this.fireEvent("rowsinserted", this, rowIndex);
9426             //this.syncRowHeights(firstRow, lastRow);
9427             //this.stripeRows(firstRow);
9428             //this.layout();
9429         }
9430         
9431     },
9432     
9433     
9434     getRowDom : function(rowIndex)
9435     {
9436         var rows = this.el.select('tbody > tr', true).elements;
9437         
9438         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
9439         
9440     },
9441     // returns the object tree for a tr..
9442   
9443     
9444     renderRow : function(cm, ds, rowIndex) 
9445     {
9446         var d = ds.getAt(rowIndex);
9447         
9448         var row = {
9449             tag : 'tr',
9450             cls : 'x-row-' + rowIndex,
9451             cn : []
9452         };
9453             
9454         var cellObjects = [];
9455         
9456         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
9457             var config = cm.config[i];
9458             
9459             var renderer = cm.getRenderer(i);
9460             var value = '';
9461             var id = false;
9462             
9463             if(typeof(renderer) !== 'undefined'){
9464                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
9465             }
9466             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
9467             // and are rendered into the cells after the row is rendered - using the id for the element.
9468             
9469             if(typeof(value) === 'object'){
9470                 id = Roo.id();
9471                 cellObjects.push({
9472                     container : id,
9473                     cfg : value 
9474                 })
9475             }
9476             
9477             var rowcfg = {
9478                 record: d,
9479                 rowIndex : rowIndex,
9480                 colIndex : i,
9481                 rowClass : ''
9482             };
9483
9484             this.fireEvent('rowclass', this, rowcfg);
9485             
9486             var td = {
9487                 tag: 'td',
9488                 // this might end up displaying HTML?
9489                 // this is too messy... - better to only do it on columsn you know are going to be too long
9490                 //tooltip : (typeof(value) === 'object') ? '' : value,
9491                 cls : rowcfg.rowClass + ' x-col-' + i,
9492                 style: '',
9493                 html: (typeof(value) === 'object') ? '' : value
9494             };
9495             
9496             if (id) {
9497                 td.id = id;
9498             }
9499             
9500             if(typeof(config.colspan) != 'undefined'){
9501                 td.colspan = config.colspan;
9502             }
9503             
9504             if(typeof(config.hidden) != 'undefined' && config.hidden){
9505                 td.style += ' display:none;';
9506             }
9507             
9508             if(typeof(config.align) != 'undefined' && config.align.length){
9509                 td.style += ' text-align:' + config.align + ';';
9510             }
9511             if(typeof(config.valign) != 'undefined' && config.valign.length){
9512                 td.style += ' vertical-align:' + config.valign + ';';
9513             }
9514             
9515             if(typeof(config.width) != 'undefined'){
9516                 td.style += ' width:' +  config.width + 'px;';
9517             }
9518             
9519             if(typeof(config.cursor) != 'undefined'){
9520                 td.style += ' cursor:' +  config.cursor + ';';
9521             }
9522             
9523             if(typeof(config.cls) != 'undefined'){
9524                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
9525             }
9526             
9527             ['xs','sm','md','lg'].map(function(size){
9528                 
9529                 if(typeof(config[size]) == 'undefined'){
9530                     return;
9531                 }
9532                 
9533                 
9534                   
9535                 if (!config[size]) { // 0 = hidden
9536                     // BS 4 '0' is treated as hide that column and below.
9537                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
9538                     return;
9539                 }
9540                 
9541                 td.cls += ' col-' + size + '-' + config[size] + (
9542                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
9543                 );
9544                  
9545
9546             });
9547             
9548             row.cn.push(td);
9549            
9550         }
9551         
9552         row.cellObjects = cellObjects;
9553         
9554         return row;
9555           
9556     },
9557     
9558     
9559     
9560     onBeforeLoad : function()
9561     {
9562         
9563     },
9564      /**
9565      * Remove all rows
9566      */
9567     clear : function()
9568     {
9569         this.el.select('tbody', true).first().dom.innerHTML = '';
9570     },
9571     /**
9572      * Show or hide a row.
9573      * @param {Number} rowIndex to show or hide
9574      * @param {Boolean} state hide
9575      */
9576     setRowVisibility : function(rowIndex, state)
9577     {
9578         var bt = this.mainBody.dom;
9579         
9580         var rows = this.el.select('tbody > tr', true).elements;
9581         
9582         if(typeof(rows[rowIndex]) == 'undefined'){
9583             return;
9584         }
9585         rows[rowIndex].dom.style.display = state ? '' : 'none';
9586     },
9587     
9588     
9589     getSelectionModel : function(){
9590         if(!this.selModel){
9591             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
9592         }
9593         return this.selModel;
9594     },
9595     /*
9596      * Render the Roo.bootstrap object from renderder
9597      */
9598     renderCellObject : function(r)
9599     {
9600         var _this = this;
9601         
9602         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9603         
9604         var t = r.cfg.render(r.container);
9605         
9606         if(r.cfg.cn){
9607             Roo.each(r.cfg.cn, function(c){
9608                 var child = {
9609                     container: t.getChildContainer(),
9610                     cfg: c
9611                 };
9612                 _this.renderCellObject(child);
9613             })
9614         }
9615     },
9616     
9617     getRowIndex : function(row)
9618     {
9619         var rowIndex = -1;
9620         
9621         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9622             if(el != row){
9623                 return;
9624             }
9625             
9626             rowIndex = index;
9627         });
9628         
9629         return rowIndex;
9630     },
9631      /**
9632      * Returns the grid's underlying element = used by panel.Grid
9633      * @return {Element} The element
9634      */
9635     getGridEl : function(){
9636         return this.el;
9637     },
9638      /**
9639      * Forces a resize - used by panel.Grid
9640      * @return {Element} The element
9641      */
9642     autoSize : function()
9643     {
9644         //var ctr = Roo.get(this.container.dom.parentElement);
9645         var ctr = Roo.get(this.el.dom);
9646         
9647         var thd = this.getGridEl().select('thead',true).first();
9648         var tbd = this.getGridEl().select('tbody', true).first();
9649         var tfd = this.getGridEl().select('tfoot', true).first();
9650         
9651         var cw = ctr.getWidth();
9652         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9653         
9654         if (tbd) {
9655             
9656             tbd.setWidth(ctr.getWidth());
9657             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9658             // this needs fixing for various usage - currently only hydra job advers I think..
9659             //tdb.setHeight(
9660             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9661             //); 
9662             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9663             cw -= barsize;
9664         }
9665         cw = Math.max(cw, this.totalWidth);
9666         this.getGridEl().select('tbody tr',true).setWidth(cw);
9667         
9668         // resize 'expandable coloumn?
9669         
9670         return; // we doe not have a view in this design..
9671         
9672     },
9673     onBodyScroll: function()
9674     {
9675         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9676         if(this.mainHead){
9677             this.mainHead.setStyle({
9678                 'position' : 'relative',
9679                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9680             });
9681         }
9682         
9683         if(this.lazyLoad){
9684             
9685             var scrollHeight = this.mainBody.dom.scrollHeight;
9686             
9687             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9688             
9689             var height = this.mainBody.getHeight();
9690             
9691             if(scrollHeight - height == scrollTop) {
9692                 
9693                 var total = this.ds.getTotalCount();
9694                 
9695                 if(this.footer.cursor + this.footer.pageSize < total){
9696                     
9697                     this.footer.ds.load({
9698                         params : {
9699                             start : this.footer.cursor + this.footer.pageSize,
9700                             limit : this.footer.pageSize
9701                         },
9702                         add : true
9703                     });
9704                 }
9705             }
9706             
9707         }
9708     },
9709     
9710     onHeaderChange : function()
9711     {
9712         var header = this.renderHeader();
9713         var table = this.el.select('table', true).first();
9714         
9715         this.mainHead.remove();
9716         this.mainHead = table.createChild(header, this.mainBody, false);
9717         
9718         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9719             e.on('click', this.sort, this);
9720         }, this);
9721         
9722         
9723     },
9724     
9725     onHiddenChange : function(colModel, colIndex, hidden)
9726     {
9727         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9728         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9729         
9730         this.CSS.updateRule(thSelector, "display", "");
9731         this.CSS.updateRule(tdSelector, "display", "");
9732         
9733         if(hidden){
9734             this.CSS.updateRule(thSelector, "display", "none");
9735             this.CSS.updateRule(tdSelector, "display", "none");
9736         }
9737         
9738         this.onHeaderChange();
9739         this.onLoad();
9740     },
9741     
9742     setColumnWidth: function(col_index, width)
9743     {
9744         // width = "md-2 xs-2..."
9745         if(!this.colModel.config[col_index]) {
9746             return;
9747         }
9748         
9749         var w = width.split(" ");
9750         
9751         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9752         
9753         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9754         
9755         
9756         for(var j = 0; j < w.length; j++) {
9757             
9758             if(!w[j]) {
9759                 continue;
9760             }
9761             
9762             var size_cls = w[j].split("-");
9763             
9764             if(!Number.isInteger(size_cls[1] * 1)) {
9765                 continue;
9766             }
9767             
9768             if(!this.colModel.config[col_index][size_cls[0]]) {
9769                 continue;
9770             }
9771             
9772             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9773                 continue;
9774             }
9775             
9776             h_row[0].classList.replace(
9777                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9778                 "col-"+size_cls[0]+"-"+size_cls[1]
9779             );
9780             
9781             for(var i = 0; i < rows.length; i++) {
9782                 
9783                 var size_cls = w[j].split("-");
9784                 
9785                 if(!Number.isInteger(size_cls[1] * 1)) {
9786                     continue;
9787                 }
9788                 
9789                 if(!this.colModel.config[col_index][size_cls[0]]) {
9790                     continue;
9791                 }
9792                 
9793                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9794                     continue;
9795                 }
9796                 
9797                 rows[i].classList.replace(
9798                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9799                     "col-"+size_cls[0]+"-"+size_cls[1]
9800                 );
9801             }
9802             
9803             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9804         }
9805     }
9806 });
9807
9808  
9809
9810 /**
9811  * @depricated
9812 */
9813 Roo.bootstrap.Table.AbstractSelectionModel = Roo.grid.AbstractSelectionModel;
9814 Roo.bootstrap.Table.RowSelectionModel = Roo.grid.RowSelectionModel;/*
9815  * - LGPL
9816  *
9817  * table cell
9818  * 
9819  */
9820
9821 /**
9822  * @class Roo.bootstrap.TableCell
9823  * @extends Roo.bootstrap.Component
9824  * Bootstrap TableCell class
9825  * @cfg {String} html cell contain text
9826  * @cfg {String} cls cell class
9827  * @cfg {String} tag cell tag (td|th) default td
9828  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9829  * @cfg {String} align Aligns the content in a cell
9830  * @cfg {String} axis Categorizes cells
9831  * @cfg {String} bgcolor Specifies the background color of a cell
9832  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9833  * @cfg {Number} colspan Specifies the number of columns a cell should span
9834  * @cfg {String} headers Specifies one or more header cells a cell is related to
9835  * @cfg {Number} height Sets the height of a cell
9836  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9837  * @cfg {Number} rowspan Sets the number of rows a cell should span
9838  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9839  * @cfg {String} valign Vertical aligns the content in a cell
9840  * @cfg {Number} width Specifies the width of a cell
9841  * 
9842  * @constructor
9843  * Create a new TableCell
9844  * @param {Object} config The config object
9845  */
9846
9847 Roo.bootstrap.TableCell = function(config){
9848     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9849 };
9850
9851 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9852     
9853     html: false,
9854     cls: false,
9855     tag: false,
9856     abbr: false,
9857     align: false,
9858     axis: false,
9859     bgcolor: false,
9860     charoff: false,
9861     colspan: false,
9862     headers: false,
9863     height: false,
9864     nowrap: false,
9865     rowspan: false,
9866     scope: false,
9867     valign: false,
9868     width: false,
9869     
9870     
9871     getAutoCreate : function(){
9872         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9873         
9874         cfg = {
9875             tag: 'td'
9876         };
9877         
9878         if(this.tag){
9879             cfg.tag = this.tag;
9880         }
9881         
9882         if (this.html) {
9883             cfg.html=this.html
9884         }
9885         if (this.cls) {
9886             cfg.cls=this.cls
9887         }
9888         if (this.abbr) {
9889             cfg.abbr=this.abbr
9890         }
9891         if (this.align) {
9892             cfg.align=this.align
9893         }
9894         if (this.axis) {
9895             cfg.axis=this.axis
9896         }
9897         if (this.bgcolor) {
9898             cfg.bgcolor=this.bgcolor
9899         }
9900         if (this.charoff) {
9901             cfg.charoff=this.charoff
9902         }
9903         if (this.colspan) {
9904             cfg.colspan=this.colspan
9905         }
9906         if (this.headers) {
9907             cfg.headers=this.headers
9908         }
9909         if (this.height) {
9910             cfg.height=this.height
9911         }
9912         if (this.nowrap) {
9913             cfg.nowrap=this.nowrap
9914         }
9915         if (this.rowspan) {
9916             cfg.rowspan=this.rowspan
9917         }
9918         if (this.scope) {
9919             cfg.scope=this.scope
9920         }
9921         if (this.valign) {
9922             cfg.valign=this.valign
9923         }
9924         if (this.width) {
9925             cfg.width=this.width
9926         }
9927         
9928         
9929         return cfg;
9930     }
9931    
9932 });
9933
9934  
9935
9936  /*
9937  * - LGPL
9938  *
9939  * table row
9940  * 
9941  */
9942
9943 /**
9944  * @class Roo.bootstrap.TableRow
9945  * @extends Roo.bootstrap.Component
9946  * Bootstrap TableRow class
9947  * @cfg {String} cls row class
9948  * @cfg {String} align Aligns the content in a table row
9949  * @cfg {String} bgcolor Specifies a background color for a table row
9950  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9951  * @cfg {String} valign Vertical aligns the content in a table row
9952  * 
9953  * @constructor
9954  * Create a new TableRow
9955  * @param {Object} config The config object
9956  */
9957
9958 Roo.bootstrap.TableRow = function(config){
9959     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9960 };
9961
9962 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9963     
9964     cls: false,
9965     align: false,
9966     bgcolor: false,
9967     charoff: false,
9968     valign: false,
9969     
9970     getAutoCreate : function(){
9971         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9972         
9973         cfg = {
9974             tag: 'tr'
9975         };
9976             
9977         if(this.cls){
9978             cfg.cls = this.cls;
9979         }
9980         if(this.align){
9981             cfg.align = this.align;
9982         }
9983         if(this.bgcolor){
9984             cfg.bgcolor = this.bgcolor;
9985         }
9986         if(this.charoff){
9987             cfg.charoff = this.charoff;
9988         }
9989         if(this.valign){
9990             cfg.valign = this.valign;
9991         }
9992         
9993         return cfg;
9994     }
9995    
9996 });
9997
9998  
9999
10000  /*
10001  * - LGPL
10002  *
10003  * table body
10004  * 
10005  */
10006
10007 /**
10008  * @class Roo.bootstrap.TableBody
10009  * @extends Roo.bootstrap.Component
10010  * Bootstrap TableBody class
10011  * @cfg {String} cls element class
10012  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
10013  * @cfg {String} align Aligns the content inside the element
10014  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
10015  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
10016  * 
10017  * @constructor
10018  * Create a new TableBody
10019  * @param {Object} config The config object
10020  */
10021
10022 Roo.bootstrap.TableBody = function(config){
10023     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
10024 };
10025
10026 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
10027     
10028     cls: false,
10029     tag: false,
10030     align: false,
10031     charoff: false,
10032     valign: false,
10033     
10034     getAutoCreate : function(){
10035         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
10036         
10037         cfg = {
10038             tag: 'tbody'
10039         };
10040             
10041         if (this.cls) {
10042             cfg.cls=this.cls
10043         }
10044         if(this.tag){
10045             cfg.tag = this.tag;
10046         }
10047         
10048         if(this.align){
10049             cfg.align = this.align;
10050         }
10051         if(this.charoff){
10052             cfg.charoff = this.charoff;
10053         }
10054         if(this.valign){
10055             cfg.valign = this.valign;
10056         }
10057         
10058         return cfg;
10059     }
10060     
10061     
10062 //    initEvents : function()
10063 //    {
10064 //        
10065 //        if(!this.store){
10066 //            return;
10067 //        }
10068 //        
10069 //        this.store = Roo.factory(this.store, Roo.data);
10070 //        this.store.on('load', this.onLoad, this);
10071 //        
10072 //        this.store.load();
10073 //        
10074 //    },
10075 //    
10076 //    onLoad: function () 
10077 //    {   
10078 //        this.fireEvent('load', this);
10079 //    }
10080 //    
10081 //   
10082 });
10083
10084  
10085
10086  /*
10087  * Based on:
10088  * Ext JS Library 1.1.1
10089  * Copyright(c) 2006-2007, Ext JS, LLC.
10090  *
10091  * Originally Released Under LGPL - original licence link has changed is not relivant.
10092  *
10093  * Fork - LGPL
10094  * <script type="text/javascript">
10095  */
10096
10097 // as we use this in bootstrap.
10098 Roo.namespace('Roo.form');
10099  /**
10100  * @class Roo.form.Action
10101  * Internal Class used to handle form actions
10102  * @constructor
10103  * @param {Roo.form.BasicForm} el The form element or its id
10104  * @param {Object} config Configuration options
10105  */
10106
10107  
10108  
10109 // define the action interface
10110 Roo.form.Action = function(form, options){
10111     this.form = form;
10112     this.options = options || {};
10113 };
10114 /**
10115  * Client Validation Failed
10116  * @const 
10117  */
10118 Roo.form.Action.CLIENT_INVALID = 'client';
10119 /**
10120  * Server Validation Failed
10121  * @const 
10122  */
10123 Roo.form.Action.SERVER_INVALID = 'server';
10124  /**
10125  * Connect to Server Failed
10126  * @const 
10127  */
10128 Roo.form.Action.CONNECT_FAILURE = 'connect';
10129 /**
10130  * Reading Data from Server Failed
10131  * @const 
10132  */
10133 Roo.form.Action.LOAD_FAILURE = 'load';
10134
10135 Roo.form.Action.prototype = {
10136     type : 'default',
10137     failureType : undefined,
10138     response : undefined,
10139     result : undefined,
10140
10141     // interface method
10142     run : function(options){
10143
10144     },
10145
10146     // interface method
10147     success : function(response){
10148
10149     },
10150
10151     // interface method
10152     handleResponse : function(response){
10153
10154     },
10155
10156     // default connection failure
10157     failure : function(response){
10158         
10159         this.response = response;
10160         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10161         this.form.afterAction(this, false);
10162     },
10163
10164     processResponse : function(response){
10165         this.response = response;
10166         if(!response.responseText){
10167             return true;
10168         }
10169         this.result = this.handleResponse(response);
10170         return this.result;
10171     },
10172
10173     // utility functions used internally
10174     getUrl : function(appendParams){
10175         var url = this.options.url || this.form.url || this.form.el.dom.action;
10176         if(appendParams){
10177             var p = this.getParams();
10178             if(p){
10179                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
10180             }
10181         }
10182         return url;
10183     },
10184
10185     getMethod : function(){
10186         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
10187     },
10188
10189     getParams : function(){
10190         var bp = this.form.baseParams;
10191         var p = this.options.params;
10192         if(p){
10193             if(typeof p == "object"){
10194                 p = Roo.urlEncode(Roo.applyIf(p, bp));
10195             }else if(typeof p == 'string' && bp){
10196                 p += '&' + Roo.urlEncode(bp);
10197             }
10198         }else if(bp){
10199             p = Roo.urlEncode(bp);
10200         }
10201         return p;
10202     },
10203
10204     createCallback : function(){
10205         return {
10206             success: this.success,
10207             failure: this.failure,
10208             scope: this,
10209             timeout: (this.form.timeout*1000),
10210             upload: this.form.fileUpload ? this.success : undefined
10211         };
10212     }
10213 };
10214
10215 Roo.form.Action.Submit = function(form, options){
10216     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
10217 };
10218
10219 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
10220     type : 'submit',
10221
10222     haveProgress : false,
10223     uploadComplete : false,
10224     
10225     // uploadProgress indicator.
10226     uploadProgress : function()
10227     {
10228         if (!this.form.progressUrl) {
10229             return;
10230         }
10231         
10232         if (!this.haveProgress) {
10233             Roo.MessageBox.progress("Uploading", "Uploading");
10234         }
10235         if (this.uploadComplete) {
10236            Roo.MessageBox.hide();
10237            return;
10238         }
10239         
10240         this.haveProgress = true;
10241    
10242         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
10243         
10244         var c = new Roo.data.Connection();
10245         c.request({
10246             url : this.form.progressUrl,
10247             params: {
10248                 id : uid
10249             },
10250             method: 'GET',
10251             success : function(req){
10252                //console.log(data);
10253                 var rdata = false;
10254                 var edata;
10255                 try  {
10256                    rdata = Roo.decode(req.responseText)
10257                 } catch (e) {
10258                     Roo.log("Invalid data from server..");
10259                     Roo.log(edata);
10260                     return;
10261                 }
10262                 if (!rdata || !rdata.success) {
10263                     Roo.log(rdata);
10264                     Roo.MessageBox.alert(Roo.encode(rdata));
10265                     return;
10266                 }
10267                 var data = rdata.data;
10268                 
10269                 if (this.uploadComplete) {
10270                    Roo.MessageBox.hide();
10271                    return;
10272                 }
10273                    
10274                 if (data){
10275                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
10276                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
10277                     );
10278                 }
10279                 this.uploadProgress.defer(2000,this);
10280             },
10281        
10282             failure: function(data) {
10283                 Roo.log('progress url failed ');
10284                 Roo.log(data);
10285             },
10286             scope : this
10287         });
10288            
10289     },
10290     
10291     
10292     run : function()
10293     {
10294         // run get Values on the form, so it syncs any secondary forms.
10295         this.form.getValues();
10296         
10297         var o = this.options;
10298         var method = this.getMethod();
10299         var isPost = method == 'POST';
10300         if(o.clientValidation === false || this.form.isValid()){
10301             
10302             if (this.form.progressUrl) {
10303                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
10304                     (new Date() * 1) + '' + Math.random());
10305                     
10306             } 
10307             
10308             
10309             Roo.Ajax.request(Roo.apply(this.createCallback(), {
10310                 form:this.form.el.dom,
10311                 url:this.getUrl(!isPost),
10312                 method: method,
10313                 params:isPost ? this.getParams() : null,
10314                 isUpload: this.form.fileUpload,
10315                 formData : this.form.formData
10316             }));
10317             
10318             this.uploadProgress();
10319
10320         }else if (o.clientValidation !== false){ // client validation failed
10321             this.failureType = Roo.form.Action.CLIENT_INVALID;
10322             this.form.afterAction(this, false);
10323         }
10324     },
10325
10326     success : function(response)
10327     {
10328         this.uploadComplete= true;
10329         if (this.haveProgress) {
10330             Roo.MessageBox.hide();
10331         }
10332         
10333         
10334         var result = this.processResponse(response);
10335         if(result === true || result.success){
10336             this.form.afterAction(this, true);
10337             return;
10338         }
10339         if(result.errors){
10340             this.form.markInvalid(result.errors);
10341             this.failureType = Roo.form.Action.SERVER_INVALID;
10342         }
10343         this.form.afterAction(this, false);
10344     },
10345     failure : function(response)
10346     {
10347         this.uploadComplete= true;
10348         if (this.haveProgress) {
10349             Roo.MessageBox.hide();
10350         }
10351         
10352         this.response = response;
10353         this.failureType = Roo.form.Action.CONNECT_FAILURE;
10354         this.form.afterAction(this, false);
10355     },
10356     
10357     handleResponse : function(response){
10358         if(this.form.errorReader){
10359             var rs = this.form.errorReader.read(response);
10360             var errors = [];
10361             if(rs.records){
10362                 for(var i = 0, len = rs.records.length; i < len; i++) {
10363                     var r = rs.records[i];
10364                     errors[i] = r.data;
10365                 }
10366             }
10367             if(errors.length < 1){
10368                 errors = null;
10369             }
10370             return {
10371                 success : rs.success,
10372                 errors : errors
10373             };
10374         }
10375         var ret = false;
10376         try {
10377             ret = Roo.decode(response.responseText);
10378         } catch (e) {
10379             ret = {
10380                 success: false,
10381                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
10382                 errors : []
10383             };
10384         }
10385         return ret;
10386         
10387     }
10388 });
10389
10390
10391 Roo.form.Action.Load = function(form, options){
10392     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
10393     this.reader = this.form.reader;
10394 };
10395
10396 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
10397     type : 'load',
10398
10399     run : function(){
10400         
10401         Roo.Ajax.request(Roo.apply(
10402                 this.createCallback(), {
10403                     method:this.getMethod(),
10404                     url:this.getUrl(false),
10405                     params:this.getParams()
10406         }));
10407     },
10408
10409     success : function(response){
10410         
10411         var result = this.processResponse(response);
10412         if(result === true || !result.success || !result.data){
10413             this.failureType = Roo.form.Action.LOAD_FAILURE;
10414             this.form.afterAction(this, false);
10415             return;
10416         }
10417         this.form.clearInvalid();
10418         this.form.setValues(result.data);
10419         this.form.afterAction(this, true);
10420     },
10421
10422     handleResponse : function(response){
10423         if(this.form.reader){
10424             var rs = this.form.reader.read(response);
10425             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
10426             return {
10427                 success : rs.success,
10428                 data : data
10429             };
10430         }
10431         return Roo.decode(response.responseText);
10432     }
10433 });
10434
10435 Roo.form.Action.ACTION_TYPES = {
10436     'load' : Roo.form.Action.Load,
10437     'submit' : Roo.form.Action.Submit
10438 };/*
10439  * - LGPL
10440  *
10441  * form
10442  *
10443  */
10444
10445 /**
10446  * @class Roo.bootstrap.Form
10447  * @extends Roo.bootstrap.Component
10448  * Bootstrap Form class
10449  * @cfg {String} method  GET | POST (default POST)
10450  * @cfg {String} labelAlign top | left (default top)
10451  * @cfg {String} align left  | right - for navbars
10452  * @cfg {Boolean} loadMask load mask when submit (default true)
10453
10454  *
10455  * @constructor
10456  * Create a new Form
10457  * @param {Object} config The config object
10458  */
10459
10460
10461 Roo.bootstrap.Form = function(config){
10462     
10463     Roo.bootstrap.Form.superclass.constructor.call(this, config);
10464     
10465     Roo.bootstrap.Form.popover.apply();
10466     
10467     this.addEvents({
10468         /**
10469          * @event clientvalidation
10470          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
10471          * @param {Form} this
10472          * @param {Boolean} valid true if the form has passed client-side validation
10473          */
10474         clientvalidation: true,
10475         /**
10476          * @event beforeaction
10477          * Fires before any action is performed. Return false to cancel the action.
10478          * @param {Form} this
10479          * @param {Action} action The action to be performed
10480          */
10481         beforeaction: true,
10482         /**
10483          * @event actionfailed
10484          * Fires when an action fails.
10485          * @param {Form} this
10486          * @param {Action} action The action that failed
10487          */
10488         actionfailed : true,
10489         /**
10490          * @event actioncomplete
10491          * Fires when an action is completed.
10492          * @param {Form} this
10493          * @param {Action} action The action that completed
10494          */
10495         actioncomplete : true
10496     });
10497 };
10498
10499 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
10500
10501      /**
10502      * @cfg {String} method
10503      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
10504      */
10505     method : 'POST',
10506     /**
10507      * @cfg {String} url
10508      * The URL to use for form actions if one isn't supplied in the action options.
10509      */
10510     /**
10511      * @cfg {Boolean} fileUpload
10512      * Set to true if this form is a file upload.
10513      */
10514
10515     /**
10516      * @cfg {Object} baseParams
10517      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
10518      */
10519
10520     /**
10521      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
10522      */
10523     timeout: 30,
10524     /**
10525      * @cfg {Sting} align (left|right) for navbar forms
10526      */
10527     align : 'left',
10528
10529     // private
10530     activeAction : null,
10531
10532     /**
10533      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
10534      * element by passing it or its id or mask the form itself by passing in true.
10535      * @type Mixed
10536      */
10537     waitMsgTarget : false,
10538
10539     loadMask : true,
10540     
10541     /**
10542      * @cfg {Boolean} errorMask (true|false) default false
10543      */
10544     errorMask : false,
10545     
10546     /**
10547      * @cfg {Number} maskOffset Default 100
10548      */
10549     maskOffset : 100,
10550     
10551     /**
10552      * @cfg {Boolean} maskBody
10553      */
10554     maskBody : false,
10555
10556     getAutoCreate : function(){
10557
10558         var cfg = {
10559             tag: 'form',
10560             method : this.method || 'POST',
10561             id : this.id || Roo.id(),
10562             cls : ''
10563         };
10564         if (this.parent().xtype.match(/^Nav/)) {
10565             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
10566
10567         }
10568
10569         if (this.labelAlign == 'left' ) {
10570             cfg.cls += ' form-horizontal';
10571         }
10572
10573
10574         return cfg;
10575     },
10576     initEvents : function()
10577     {
10578         this.el.on('submit', this.onSubmit, this);
10579         // this was added as random key presses on the form where triggering form submit.
10580         this.el.on('keypress', function(e) {
10581             if (e.getCharCode() != 13) {
10582                 return true;
10583             }
10584             // we might need to allow it for textareas.. and some other items.
10585             // check e.getTarget().
10586
10587             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
10588                 return true;
10589             }
10590
10591             Roo.log("keypress blocked");
10592
10593             e.preventDefault();
10594             return false;
10595         });
10596         
10597     },
10598     // private
10599     onSubmit : function(e){
10600         e.stopEvent();
10601     },
10602
10603      /**
10604      * Returns true if client-side validation on the form is successful.
10605      * @return Boolean
10606      */
10607     isValid : function(){
10608         var items = this.getItems();
10609         var valid = true;
10610         var target = false;
10611         
10612         items.each(function(f){
10613             
10614             if(f.validate()){
10615                 return;
10616             }
10617             
10618             Roo.log('invalid field: ' + f.name);
10619             
10620             valid = false;
10621
10622             if(!target && f.el.isVisible(true)){
10623                 target = f;
10624             }
10625            
10626         });
10627         
10628         if(this.errorMask && !valid){
10629             Roo.bootstrap.Form.popover.mask(this, target);
10630         }
10631         
10632         return valid;
10633     },
10634     
10635     /**
10636      * Returns true if any fields in this form have changed since their original load.
10637      * @return Boolean
10638      */
10639     isDirty : function(){
10640         var dirty = false;
10641         var items = this.getItems();
10642         items.each(function(f){
10643            if(f.isDirty()){
10644                dirty = true;
10645                return false;
10646            }
10647            return true;
10648         });
10649         return dirty;
10650     },
10651      /**
10652      * Performs a predefined action (submit or load) or custom actions you define on this form.
10653      * @param {String} actionName The name of the action type
10654      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10655      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10656      * accept other config options):
10657      * <pre>
10658 Property          Type             Description
10659 ----------------  ---------------  ----------------------------------------------------------------------------------
10660 url               String           The url for the action (defaults to the form's url)
10661 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10662 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10663 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10664                                    validate the form on the client (defaults to false)
10665      * </pre>
10666      * @return {BasicForm} this
10667      */
10668     doAction : function(action, options){
10669         if(typeof action == 'string'){
10670             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10671         }
10672         if(this.fireEvent('beforeaction', this, action) !== false){
10673             this.beforeAction(action);
10674             action.run.defer(100, action);
10675         }
10676         return this;
10677     },
10678
10679     // private
10680     beforeAction : function(action){
10681         var o = action.options;
10682         
10683         if(this.loadMask){
10684             
10685             if(this.maskBody){
10686                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10687             } else {
10688                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10689             }
10690         }
10691         // not really supported yet.. ??
10692
10693         //if(this.waitMsgTarget === true){
10694         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10695         //}else if(this.waitMsgTarget){
10696         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10697         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10698         //}else {
10699         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10700        // }
10701
10702     },
10703
10704     // private
10705     afterAction : function(action, success){
10706         this.activeAction = null;
10707         var o = action.options;
10708
10709         if(this.loadMask){
10710             
10711             if(this.maskBody){
10712                 Roo.get(document.body).unmask();
10713             } else {
10714                 this.el.unmask();
10715             }
10716         }
10717         
10718         //if(this.waitMsgTarget === true){
10719 //            this.el.unmask();
10720         //}else if(this.waitMsgTarget){
10721         //    this.waitMsgTarget.unmask();
10722         //}else{
10723         //    Roo.MessageBox.updateProgress(1);
10724         //    Roo.MessageBox.hide();
10725        // }
10726         //
10727         if(success){
10728             if(o.reset){
10729                 this.reset();
10730             }
10731             Roo.callback(o.success, o.scope, [this, action]);
10732             this.fireEvent('actioncomplete', this, action);
10733
10734         }else{
10735
10736             // failure condition..
10737             // we have a scenario where updates need confirming.
10738             // eg. if a locking scenario exists..
10739             // we look for { errors : { needs_confirm : true }} in the response.
10740             if (
10741                 (typeof(action.result) != 'undefined')  &&
10742                 (typeof(action.result.errors) != 'undefined')  &&
10743                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10744            ){
10745                 var _t = this;
10746                 Roo.log("not supported yet");
10747                  /*
10748
10749                 Roo.MessageBox.confirm(
10750                     "Change requires confirmation",
10751                     action.result.errorMsg,
10752                     function(r) {
10753                         if (r != 'yes') {
10754                             return;
10755                         }
10756                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10757                     }
10758
10759                 );
10760                 */
10761
10762
10763                 return;
10764             }
10765
10766             Roo.callback(o.failure, o.scope, [this, action]);
10767             // show an error message if no failed handler is set..
10768             if (!this.hasListener('actionfailed')) {
10769                 Roo.log("need to add dialog support");
10770                 /*
10771                 Roo.MessageBox.alert("Error",
10772                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10773                         action.result.errorMsg :
10774                         "Saving Failed, please check your entries or try again"
10775                 );
10776                 */
10777             }
10778
10779             this.fireEvent('actionfailed', this, action);
10780         }
10781
10782     },
10783     /**
10784      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10785      * @param {String} id The value to search for
10786      * @return Field
10787      */
10788     findField : function(id){
10789         var items = this.getItems();
10790         var field = items.get(id);
10791         if(!field){
10792              items.each(function(f){
10793                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10794                     field = f;
10795                     return false;
10796                 }
10797                 return true;
10798             });
10799         }
10800         return field || null;
10801     },
10802      /**
10803      * Mark fields in this form invalid in bulk.
10804      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10805      * @return {BasicForm} this
10806      */
10807     markInvalid : function(errors){
10808         if(errors instanceof Array){
10809             for(var i = 0, len = errors.length; i < len; i++){
10810                 var fieldError = errors[i];
10811                 var f = this.findField(fieldError.id);
10812                 if(f){
10813                     f.markInvalid(fieldError.msg);
10814                 }
10815             }
10816         }else{
10817             var field, id;
10818             for(id in errors){
10819                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10820                     field.markInvalid(errors[id]);
10821                 }
10822             }
10823         }
10824         //Roo.each(this.childForms || [], function (f) {
10825         //    f.markInvalid(errors);
10826         //});
10827
10828         return this;
10829     },
10830
10831     /**
10832      * Set values for fields in this form in bulk.
10833      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10834      * @return {BasicForm} this
10835      */
10836     setValues : function(values){
10837         if(values instanceof Array){ // array of objects
10838             for(var i = 0, len = values.length; i < len; i++){
10839                 var v = values[i];
10840                 var f = this.findField(v.id);
10841                 if(f){
10842                     f.setValue(v.value);
10843                     if(this.trackResetOnLoad){
10844                         f.originalValue = f.getValue();
10845                     }
10846                 }
10847             }
10848         }else{ // object hash
10849             var field, id;
10850             for(id in values){
10851                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10852
10853                     if (field.setFromData &&
10854                         field.valueField &&
10855                         field.displayField &&
10856                         // combos' with local stores can
10857                         // be queried via setValue()
10858                         // to set their value..
10859                         (field.store && !field.store.isLocal)
10860                         ) {
10861                         // it's a combo
10862                         var sd = { };
10863                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10864                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10865                         field.setFromData(sd);
10866
10867                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10868                         
10869                         field.setFromData(values);
10870                         
10871                     } else {
10872                         field.setValue(values[id]);
10873                     }
10874
10875
10876                     if(this.trackResetOnLoad){
10877                         field.originalValue = field.getValue();
10878                     }
10879                 }
10880             }
10881         }
10882
10883         //Roo.each(this.childForms || [], function (f) {
10884         //    f.setValues(values);
10885         //});
10886
10887         return this;
10888     },
10889
10890     /**
10891      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10892      * they are returned as an array.
10893      * @param {Boolean} asString
10894      * @return {Object}
10895      */
10896     getValues : function(asString){
10897         //if (this.childForms) {
10898             // copy values from the child forms
10899         //    Roo.each(this.childForms, function (f) {
10900         //        this.setValues(f.getValues());
10901         //    }, this);
10902         //}
10903
10904
10905
10906         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10907         if(asString === true){
10908             return fs;
10909         }
10910         return Roo.urlDecode(fs);
10911     },
10912
10913     /**
10914      * Returns the fields in this form as an object with key/value pairs.
10915      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10916      * @return {Object}
10917      */
10918     getFieldValues : function(with_hidden)
10919     {
10920         var items = this.getItems();
10921         var ret = {};
10922         items.each(function(f){
10923             
10924             if (!f.getName()) {
10925                 return;
10926             }
10927             
10928             var v = f.getValue();
10929             
10930             if (f.inputType =='radio') {
10931                 if (typeof(ret[f.getName()]) == 'undefined') {
10932                     ret[f.getName()] = ''; // empty..
10933                 }
10934
10935                 if (!f.el.dom.checked) {
10936                     return;
10937
10938                 }
10939                 v = f.el.dom.value;
10940
10941             }
10942             
10943             if(f.xtype == 'MoneyField'){
10944                 ret[f.currencyName] = f.getCurrency();
10945             }
10946
10947             // not sure if this supported any more..
10948             if ((typeof(v) == 'object') && f.getRawValue) {
10949                 v = f.getRawValue() ; // dates..
10950             }
10951             // combo boxes where name != hiddenName...
10952             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10953                 ret[f.name] = f.getRawValue();
10954             }
10955             ret[f.getName()] = v;
10956         });
10957
10958         return ret;
10959     },
10960
10961     /**
10962      * Clears all invalid messages in this form.
10963      * @return {BasicForm} this
10964      */
10965     clearInvalid : function(){
10966         var items = this.getItems();
10967
10968         items.each(function(f){
10969            f.clearInvalid();
10970         });
10971
10972         return this;
10973     },
10974
10975     /**
10976      * Resets this form.
10977      * @return {BasicForm} this
10978      */
10979     reset : function(){
10980         var items = this.getItems();
10981         items.each(function(f){
10982             f.reset();
10983         });
10984
10985         Roo.each(this.childForms || [], function (f) {
10986             f.reset();
10987         });
10988
10989
10990         return this;
10991     },
10992     
10993     getItems : function()
10994     {
10995         var r=new Roo.util.MixedCollection(false, function(o){
10996             return o.id || (o.id = Roo.id());
10997         });
10998         var iter = function(el) {
10999             if (el.inputEl) {
11000                 r.add(el);
11001             }
11002             if (!el.items) {
11003                 return;
11004             }
11005             Roo.each(el.items,function(e) {
11006                 iter(e);
11007             });
11008         };
11009
11010         iter(this);
11011         return r;
11012     },
11013     
11014     hideFields : function(items)
11015     {
11016         Roo.each(items, function(i){
11017             
11018             var f = this.findField(i);
11019             
11020             if(!f){
11021                 return;
11022             }
11023             
11024             f.hide();
11025             
11026         }, this);
11027     },
11028     
11029     showFields : function(items)
11030     {
11031         Roo.each(items, function(i){
11032             
11033             var f = this.findField(i);
11034             
11035             if(!f){
11036                 return;
11037             }
11038             
11039             f.show();
11040             
11041         }, this);
11042     }
11043
11044 });
11045
11046 Roo.apply(Roo.bootstrap.Form, {
11047     
11048     popover : {
11049         
11050         padding : 5,
11051         
11052         isApplied : false,
11053         
11054         isMasked : false,
11055         
11056         form : false,
11057         
11058         target : false,
11059         
11060         toolTip : false,
11061         
11062         intervalID : false,
11063         
11064         maskEl : false,
11065         
11066         apply : function()
11067         {
11068             if(this.isApplied){
11069                 return;
11070             }
11071             
11072             this.maskEl = {
11073                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
11074                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
11075                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
11076                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
11077             };
11078             
11079             this.maskEl.top.enableDisplayMode("block");
11080             this.maskEl.left.enableDisplayMode("block");
11081             this.maskEl.bottom.enableDisplayMode("block");
11082             this.maskEl.right.enableDisplayMode("block");
11083             
11084             this.toolTip = new Roo.bootstrap.Tooltip({
11085                 cls : 'roo-form-error-popover',
11086                 alignment : {
11087                     'left' : ['r-l', [-2,0], 'right'],
11088                     'right' : ['l-r', [2,0], 'left'],
11089                     'bottom' : ['tl-bl', [0,2], 'top'],
11090                     'top' : [ 'bl-tl', [0,-2], 'bottom']
11091                 }
11092             });
11093             
11094             this.toolTip.render(Roo.get(document.body));
11095
11096             this.toolTip.el.enableDisplayMode("block");
11097             
11098             Roo.get(document.body).on('click', function(){
11099                 this.unmask();
11100             }, this);
11101             
11102             Roo.get(document.body).on('touchstart', function(){
11103                 this.unmask();
11104             }, this);
11105             
11106             this.isApplied = true
11107         },
11108         
11109         mask : function(form, target)
11110         {
11111             this.form = form;
11112             
11113             this.target = target;
11114             
11115             if(!this.form.errorMask || !target.el){
11116                 return;
11117             }
11118             
11119             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
11120             
11121             Roo.log(scrollable);
11122             
11123             var ot = this.target.el.calcOffsetsTo(scrollable);
11124             
11125             var scrollTo = ot[1] - this.form.maskOffset;
11126             
11127             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
11128             
11129             scrollable.scrollTo('top', scrollTo);
11130             
11131             var box = this.target.el.getBox();
11132             Roo.log(box);
11133             var zIndex = Roo.bootstrap.Modal.zIndex++;
11134
11135             
11136             this.maskEl.top.setStyle('position', 'absolute');
11137             this.maskEl.top.setStyle('z-index', zIndex);
11138             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
11139             this.maskEl.top.setLeft(0);
11140             this.maskEl.top.setTop(0);
11141             this.maskEl.top.show();
11142             
11143             this.maskEl.left.setStyle('position', 'absolute');
11144             this.maskEl.left.setStyle('z-index', zIndex);
11145             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
11146             this.maskEl.left.setLeft(0);
11147             this.maskEl.left.setTop(box.y - this.padding);
11148             this.maskEl.left.show();
11149
11150             this.maskEl.bottom.setStyle('position', 'absolute');
11151             this.maskEl.bottom.setStyle('z-index', zIndex);
11152             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
11153             this.maskEl.bottom.setLeft(0);
11154             this.maskEl.bottom.setTop(box.bottom + this.padding);
11155             this.maskEl.bottom.show();
11156
11157             this.maskEl.right.setStyle('position', 'absolute');
11158             this.maskEl.right.setStyle('z-index', zIndex);
11159             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
11160             this.maskEl.right.setLeft(box.right + this.padding);
11161             this.maskEl.right.setTop(box.y - this.padding);
11162             this.maskEl.right.show();
11163
11164             this.toolTip.bindEl = this.target.el;
11165
11166             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
11167
11168             var tip = this.target.blankText;
11169
11170             if(this.target.getValue() !== '' ) {
11171                 
11172                 if (this.target.invalidText.length) {
11173                     tip = this.target.invalidText;
11174                 } else if (this.target.regexText.length){
11175                     tip = this.target.regexText;
11176                 }
11177             }
11178
11179             this.toolTip.show(tip);
11180
11181             this.intervalID = window.setInterval(function() {
11182                 Roo.bootstrap.Form.popover.unmask();
11183             }, 10000);
11184
11185             window.onwheel = function(){ return false;};
11186             
11187             (function(){ this.isMasked = true; }).defer(500, this);
11188             
11189         },
11190         
11191         unmask : function()
11192         {
11193             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
11194                 return;
11195             }
11196             
11197             this.maskEl.top.setStyle('position', 'absolute');
11198             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
11199             this.maskEl.top.hide();
11200
11201             this.maskEl.left.setStyle('position', 'absolute');
11202             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
11203             this.maskEl.left.hide();
11204
11205             this.maskEl.bottom.setStyle('position', 'absolute');
11206             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
11207             this.maskEl.bottom.hide();
11208
11209             this.maskEl.right.setStyle('position', 'absolute');
11210             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
11211             this.maskEl.right.hide();
11212             
11213             this.toolTip.hide();
11214             
11215             this.toolTip.el.hide();
11216             
11217             window.onwheel = function(){ return true;};
11218             
11219             if(this.intervalID){
11220                 window.clearInterval(this.intervalID);
11221                 this.intervalID = false;
11222             }
11223             
11224             this.isMasked = false;
11225             
11226         }
11227         
11228     }
11229     
11230 });
11231
11232 /*
11233  * Based on:
11234  * Ext JS Library 1.1.1
11235  * Copyright(c) 2006-2007, Ext JS, LLC.
11236  *
11237  * Originally Released Under LGPL - original licence link has changed is not relivant.
11238  *
11239  * Fork - LGPL
11240  * <script type="text/javascript">
11241  */
11242 /**
11243  * @class Roo.form.VTypes
11244  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
11245  * @singleton
11246  */
11247 Roo.form.VTypes = function(){
11248     // closure these in so they are only created once.
11249     var alpha = /^[a-zA-Z_]+$/;
11250     var alphanum = /^[a-zA-Z0-9_]+$/;
11251     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
11252     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
11253
11254     // All these messages and functions are configurable
11255     return {
11256         /**
11257          * The function used to validate email addresses
11258          * @param {String} value The email address
11259          */
11260         'email' : function(v){
11261             return email.test(v);
11262         },
11263         /**
11264          * The error text to display when the email validation function returns false
11265          * @type String
11266          */
11267         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
11268         /**
11269          * The keystroke filter mask to be applied on email input
11270          * @type RegExp
11271          */
11272         'emailMask' : /[a-z0-9_\.\-@]/i,
11273
11274         /**
11275          * The function used to validate URLs
11276          * @param {String} value The URL
11277          */
11278         'url' : function(v){
11279             return url.test(v);
11280         },
11281         /**
11282          * The error text to display when the url validation function returns false
11283          * @type String
11284          */
11285         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
11286         
11287         /**
11288          * The function used to validate alpha values
11289          * @param {String} value The value
11290          */
11291         'alpha' : function(v){
11292             return alpha.test(v);
11293         },
11294         /**
11295          * The error text to display when the alpha validation function returns false
11296          * @type String
11297          */
11298         'alphaText' : 'This field should only contain letters and _',
11299         /**
11300          * The keystroke filter mask to be applied on alpha input
11301          * @type RegExp
11302          */
11303         'alphaMask' : /[a-z_]/i,
11304
11305         /**
11306          * The function used to validate alphanumeric values
11307          * @param {String} value The value
11308          */
11309         'alphanum' : function(v){
11310             return alphanum.test(v);
11311         },
11312         /**
11313          * The error text to display when the alphanumeric validation function returns false
11314          * @type String
11315          */
11316         'alphanumText' : 'This field should only contain letters, numbers and _',
11317         /**
11318          * The keystroke filter mask to be applied on alphanumeric input
11319          * @type RegExp
11320          */
11321         'alphanumMask' : /[a-z0-9_]/i
11322     };
11323 }();/*
11324  * - LGPL
11325  *
11326  * Input
11327  * 
11328  */
11329
11330 /**
11331  * @class Roo.bootstrap.Input
11332  * @extends Roo.bootstrap.Component
11333  * Bootstrap Input class
11334  * @cfg {Boolean} disabled is it disabled
11335  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
11336  * @cfg {String} name name of the input
11337  * @cfg {string} fieldLabel - the label associated
11338  * @cfg {string} placeholder - placeholder to put in text.
11339  * @cfg {string}  before - input group add on before
11340  * @cfg {string} after - input group add on after
11341  * @cfg {string} size - (lg|sm) or leave empty..
11342  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
11343  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
11344  * @cfg {Number} md colspan out of 12 for computer-sized screens
11345  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
11346  * @cfg {string} value default value of the input
11347  * @cfg {Number} labelWidth set the width of label 
11348  * @cfg {Number} labellg set the width of label (1-12)
11349  * @cfg {Number} labelmd set the width of label (1-12)
11350  * @cfg {Number} labelsm set the width of label (1-12)
11351  * @cfg {Number} labelxs set the width of label (1-12)
11352  * @cfg {String} labelAlign (top|left)
11353  * @cfg {Boolean} readOnly Specifies that the field should be read-only
11354  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
11355  * @cfg {String} indicatorpos (left|right) default left
11356  * @cfg {String} capture (user|camera) use for file input only. (default empty)
11357  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
11358  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
11359
11360  * @cfg {String} align (left|center|right) Default left
11361  * @cfg {Boolean} forceFeedback (true|false) Default false
11362  * 
11363  * @constructor
11364  * Create a new Input
11365  * @param {Object} config The config object
11366  */
11367
11368 Roo.bootstrap.Input = function(config){
11369     
11370     Roo.bootstrap.Input.superclass.constructor.call(this, config);
11371     
11372     this.addEvents({
11373         /**
11374          * @event focus
11375          * Fires when this field receives input focus.
11376          * @param {Roo.form.Field} this
11377          */
11378         focus : true,
11379         /**
11380          * @event blur
11381          * Fires when this field loses input focus.
11382          * @param {Roo.form.Field} this
11383          */
11384         blur : true,
11385         /**
11386          * @event specialkey
11387          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
11388          * {@link Roo.EventObject#getKey} to determine which key was pressed.
11389          * @param {Roo.form.Field} this
11390          * @param {Roo.EventObject} e The event object
11391          */
11392         specialkey : true,
11393         /**
11394          * @event change
11395          * Fires just before the field blurs if the field value has changed.
11396          * @param {Roo.form.Field} this
11397          * @param {Mixed} newValue The new value
11398          * @param {Mixed} oldValue The original value
11399          */
11400         change : true,
11401         /**
11402          * @event invalid
11403          * Fires after the field has been marked as invalid.
11404          * @param {Roo.form.Field} this
11405          * @param {String} msg The validation message
11406          */
11407         invalid : true,
11408         /**
11409          * @event valid
11410          * Fires after the field has been validated with no errors.
11411          * @param {Roo.form.Field} this
11412          */
11413         valid : true,
11414          /**
11415          * @event keyup
11416          * Fires after the key up
11417          * @param {Roo.form.Field} this
11418          * @param {Roo.EventObject}  e The event Object
11419          */
11420         keyup : true,
11421         /**
11422          * @event paste
11423          * Fires after the user pastes into input
11424          * @param {Roo.form.Field} this
11425          * @param {Roo.EventObject}  e The event Object
11426          */
11427         paste : true
11428     });
11429 };
11430
11431 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
11432      /**
11433      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
11434       automatic validation (defaults to "keyup").
11435      */
11436     validationEvent : "keyup",
11437      /**
11438      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
11439      */
11440     validateOnBlur : true,
11441     /**
11442      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
11443      */
11444     validationDelay : 250,
11445      /**
11446      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
11447      */
11448     focusClass : "x-form-focus",  // not needed???
11449     
11450        
11451     /**
11452      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11453      */
11454     invalidClass : "has-warning",
11455     
11456     /**
11457      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
11458      */
11459     validClass : "has-success",
11460     
11461     /**
11462      * @cfg {Boolean} hasFeedback (true|false) default true
11463      */
11464     hasFeedback : true,
11465     
11466     /**
11467      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11468      */
11469     invalidFeedbackClass : "glyphicon-warning-sign",
11470     
11471     /**
11472      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
11473      */
11474     validFeedbackClass : "glyphicon-ok",
11475     
11476     /**
11477      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
11478      */
11479     selectOnFocus : false,
11480     
11481      /**
11482      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
11483      */
11484     maskRe : null,
11485        /**
11486      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
11487      */
11488     vtype : null,
11489     
11490       /**
11491      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
11492      */
11493     disableKeyFilter : false,
11494     
11495        /**
11496      * @cfg {Boolean} disabled True to disable the field (defaults to false).
11497      */
11498     disabled : false,
11499      /**
11500      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
11501      */
11502     allowBlank : true,
11503     /**
11504      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
11505      */
11506     blankText : "Please complete this mandatory field",
11507     
11508      /**
11509      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
11510      */
11511     minLength : 0,
11512     /**
11513      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
11514      */
11515     maxLength : Number.MAX_VALUE,
11516     /**
11517      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
11518      */
11519     minLengthText : "The minimum length for this field is {0}",
11520     /**
11521      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
11522      */
11523     maxLengthText : "The maximum length for this field is {0}",
11524   
11525     
11526     /**
11527      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
11528      * If available, this function will be called only after the basic validators all return true, and will be passed the
11529      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
11530      */
11531     validator : null,
11532     /**
11533      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
11534      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
11535      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
11536      */
11537     regex : null,
11538     /**
11539      * @cfg {String} regexText -- Depricated - use Invalid Text
11540      */
11541     regexText : "",
11542     
11543     /**
11544      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
11545      */
11546     invalidText : "",
11547     
11548     
11549     
11550     autocomplete: false,
11551     
11552     
11553     fieldLabel : '',
11554     inputType : 'text',
11555     
11556     name : false,
11557     placeholder: false,
11558     before : false,
11559     after : false,
11560     size : false,
11561     hasFocus : false,
11562     preventMark: false,
11563     isFormField : true,
11564     value : '',
11565     labelWidth : 2,
11566     labelAlign : false,
11567     readOnly : false,
11568     align : false,
11569     formatedValue : false,
11570     forceFeedback : false,
11571     
11572     indicatorpos : 'left',
11573     
11574     labellg : 0,
11575     labelmd : 0,
11576     labelsm : 0,
11577     labelxs : 0,
11578     
11579     capture : '',
11580     accept : '',
11581     
11582     parentLabelAlign : function()
11583     {
11584         var parent = this;
11585         while (parent.parent()) {
11586             parent = parent.parent();
11587             if (typeof(parent.labelAlign) !='undefined') {
11588                 return parent.labelAlign;
11589             }
11590         }
11591         return 'left';
11592         
11593     },
11594     
11595     getAutoCreate : function()
11596     {
11597         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11598         
11599         var id = Roo.id();
11600         
11601         var cfg = {};
11602         
11603         if(this.inputType != 'hidden'){
11604             cfg.cls = 'form-group' //input-group
11605         }
11606         
11607         var input =  {
11608             tag: 'input',
11609             id : id,
11610             type : this.inputType,
11611             value : this.value,
11612             cls : 'form-control',
11613             placeholder : this.placeholder || '',
11614             autocomplete : this.autocomplete || 'new-password'
11615         };
11616         if (this.inputType == 'file') {
11617             input.style = 'overflow:hidden'; // why not in CSS?
11618         }
11619         
11620         if(this.capture.length){
11621             input.capture = this.capture;
11622         }
11623         
11624         if(this.accept.length){
11625             input.accept = this.accept + "/*";
11626         }
11627         
11628         if(this.align){
11629             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11630         }
11631         
11632         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11633             input.maxLength = this.maxLength;
11634         }
11635         
11636         if (this.disabled) {
11637             input.disabled=true;
11638         }
11639         
11640         if (this.readOnly) {
11641             input.readonly=true;
11642         }
11643         
11644         if (this.name) {
11645             input.name = this.name;
11646         }
11647         
11648         if (this.size) {
11649             input.cls += ' input-' + this.size;
11650         }
11651         
11652         var settings=this;
11653         ['xs','sm','md','lg'].map(function(size){
11654             if (settings[size]) {
11655                 cfg.cls += ' col-' + size + '-' + settings[size];
11656             }
11657         });
11658         
11659         var inputblock = input;
11660         
11661         var feedback = {
11662             tag: 'span',
11663             cls: 'glyphicon form-control-feedback'
11664         };
11665             
11666         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11667             
11668             inputblock = {
11669                 cls : 'has-feedback',
11670                 cn :  [
11671                     input,
11672                     feedback
11673                 ] 
11674             };  
11675         }
11676         
11677         if (this.before || this.after) {
11678             
11679             inputblock = {
11680                 cls : 'input-group',
11681                 cn :  [] 
11682             };
11683             
11684             if (this.before && typeof(this.before) == 'string') {
11685                 
11686                 inputblock.cn.push({
11687                     tag :'span',
11688                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11689                     html : this.before
11690                 });
11691             }
11692             if (this.before && typeof(this.before) == 'object') {
11693                 this.before = Roo.factory(this.before);
11694                 
11695                 inputblock.cn.push({
11696                     tag :'span',
11697                     cls : 'roo-input-before input-group-prepend   input-group-' +
11698                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11699                 });
11700             }
11701             
11702             inputblock.cn.push(input);
11703             
11704             if (this.after && typeof(this.after) == 'string') {
11705                 inputblock.cn.push({
11706                     tag :'span',
11707                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11708                     html : this.after
11709                 });
11710             }
11711             if (this.after && typeof(this.after) == 'object') {
11712                 this.after = Roo.factory(this.after);
11713                 
11714                 inputblock.cn.push({
11715                     tag :'span',
11716                     cls : 'roo-input-after input-group-append  input-group-' +
11717                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11718                 });
11719             }
11720             
11721             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11722                 inputblock.cls += ' has-feedback';
11723                 inputblock.cn.push(feedback);
11724             }
11725         };
11726         var indicator = {
11727             tag : 'i',
11728             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11729             tooltip : 'This field is required'
11730         };
11731         if (this.allowBlank ) {
11732             indicator.style = this.allowBlank ? ' display:none' : '';
11733         }
11734         if (align ==='left' && this.fieldLabel.length) {
11735             
11736             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11737             
11738             cfg.cn = [
11739                 indicator,
11740                 {
11741                     tag: 'label',
11742                     'for' :  id,
11743                     cls : 'control-label col-form-label',
11744                     html : this.fieldLabel
11745
11746                 },
11747                 {
11748                     cls : "", 
11749                     cn: [
11750                         inputblock
11751                     ]
11752                 }
11753             ];
11754             
11755             var labelCfg = cfg.cn[1];
11756             var contentCfg = cfg.cn[2];
11757             
11758             if(this.indicatorpos == 'right'){
11759                 cfg.cn = [
11760                     {
11761                         tag: 'label',
11762                         'for' :  id,
11763                         cls : 'control-label col-form-label',
11764                         cn : [
11765                             {
11766                                 tag : 'span',
11767                                 html : this.fieldLabel
11768                             },
11769                             indicator
11770                         ]
11771                     },
11772                     {
11773                         cls : "",
11774                         cn: [
11775                             inputblock
11776                         ]
11777                     }
11778
11779                 ];
11780                 
11781                 labelCfg = cfg.cn[0];
11782                 contentCfg = cfg.cn[1];
11783             
11784             }
11785             
11786             if(this.labelWidth > 12){
11787                 labelCfg.style = "width: " + this.labelWidth + 'px';
11788             }
11789             
11790             if(this.labelWidth < 13 && this.labelmd == 0){
11791                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11792             }
11793             
11794             if(this.labellg > 0){
11795                 labelCfg.cls += ' col-lg-' + this.labellg;
11796                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11797             }
11798             
11799             if(this.labelmd > 0){
11800                 labelCfg.cls += ' col-md-' + this.labelmd;
11801                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11802             }
11803             
11804             if(this.labelsm > 0){
11805                 labelCfg.cls += ' col-sm-' + this.labelsm;
11806                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11807             }
11808             
11809             if(this.labelxs > 0){
11810                 labelCfg.cls += ' col-xs-' + this.labelxs;
11811                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11812             }
11813             
11814             
11815         } else if ( this.fieldLabel.length) {
11816                 
11817             
11818             
11819             cfg.cn = [
11820                 {
11821                     tag : 'i',
11822                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11823                     tooltip : 'This field is required',
11824                     style : this.allowBlank ? ' display:none' : '' 
11825                 },
11826                 {
11827                     tag: 'label',
11828                    //cls : 'input-group-addon',
11829                     html : this.fieldLabel
11830
11831                 },
11832
11833                inputblock
11834
11835            ];
11836            
11837            if(this.indicatorpos == 'right'){
11838        
11839                 cfg.cn = [
11840                     {
11841                         tag: 'label',
11842                        //cls : 'input-group-addon',
11843                         html : this.fieldLabel
11844
11845                     },
11846                     {
11847                         tag : 'i',
11848                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11849                         tooltip : 'This field is required',
11850                         style : this.allowBlank ? ' display:none' : '' 
11851                     },
11852
11853                    inputblock
11854
11855                ];
11856
11857             }
11858
11859         } else {
11860             
11861             cfg.cn = [
11862
11863                     inputblock
11864
11865             ];
11866                 
11867                 
11868         };
11869         
11870         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11871            cfg.cls += ' navbar-form';
11872         }
11873         
11874         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11875             // on BS4 we do this only if not form 
11876             cfg.cls += ' navbar-form';
11877             cfg.tag = 'li';
11878         }
11879         
11880         return cfg;
11881         
11882     },
11883     /**
11884      * return the real input element.
11885      */
11886     inputEl: function ()
11887     {
11888         return this.el.select('input.form-control',true).first();
11889     },
11890     
11891     tooltipEl : function()
11892     {
11893         return this.inputEl();
11894     },
11895     
11896     indicatorEl : function()
11897     {
11898         if (Roo.bootstrap.version == 4) {
11899             return false; // not enabled in v4 yet.
11900         }
11901         
11902         var indicator = this.el.select('i.roo-required-indicator',true).first();
11903         
11904         if(!indicator){
11905             return false;
11906         }
11907         
11908         return indicator;
11909         
11910     },
11911     
11912     setDisabled : function(v)
11913     {
11914         var i  = this.inputEl().dom;
11915         if (!v) {
11916             i.removeAttribute('disabled');
11917             return;
11918             
11919         }
11920         i.setAttribute('disabled','true');
11921     },
11922     initEvents : function()
11923     {
11924           
11925         this.inputEl().on("keydown" , this.fireKey,  this);
11926         this.inputEl().on("focus", this.onFocus,  this);
11927         this.inputEl().on("blur", this.onBlur,  this);
11928         
11929         this.inputEl().relayEvent('keyup', this);
11930         this.inputEl().relayEvent('paste', this);
11931         
11932         this.indicator = this.indicatorEl();
11933         
11934         if(this.indicator){
11935             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11936         }
11937  
11938         // reference to original value for reset
11939         this.originalValue = this.getValue();
11940         //Roo.form.TextField.superclass.initEvents.call(this);
11941         if(this.validationEvent == 'keyup'){
11942             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11943             this.inputEl().on('keyup', this.filterValidation, this);
11944         }
11945         else if(this.validationEvent !== false){
11946             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11947         }
11948         
11949         if(this.selectOnFocus){
11950             this.on("focus", this.preFocus, this);
11951             
11952         }
11953         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11954             this.inputEl().on("keypress", this.filterKeys, this);
11955         } else {
11956             this.inputEl().relayEvent('keypress', this);
11957         }
11958        /* if(this.grow){
11959             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11960             this.el.on("click", this.autoSize,  this);
11961         }
11962         */
11963         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11964             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11965         }
11966         
11967         if (typeof(this.before) == 'object') {
11968             this.before.render(this.el.select('.roo-input-before',true).first());
11969         }
11970         if (typeof(this.after) == 'object') {
11971             this.after.render(this.el.select('.roo-input-after',true).first());
11972         }
11973         
11974         this.inputEl().on('change', this.onChange, this);
11975         
11976     },
11977     filterValidation : function(e){
11978         if(!e.isNavKeyPress()){
11979             this.validationTask.delay(this.validationDelay);
11980         }
11981     },
11982      /**
11983      * Validates the field value
11984      * @return {Boolean} True if the value is valid, else false
11985      */
11986     validate : function(){
11987         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11988         if(this.disabled || this.validateValue(this.getRawValue())){
11989             this.markValid();
11990             return true;
11991         }
11992         
11993         this.markInvalid();
11994         return false;
11995     },
11996     
11997     
11998     /**
11999      * Validates a value according to the field's validation rules and marks the field as invalid
12000      * if the validation fails
12001      * @param {Mixed} value The value to validate
12002      * @return {Boolean} True if the value is valid, else false
12003      */
12004     validateValue : function(value)
12005     {
12006         if(this.getVisibilityEl().hasClass('hidden')){
12007             return true;
12008         }
12009         
12010         if(value.length < 1)  { // if it's blank
12011             if(this.allowBlank){
12012                 return true;
12013             }
12014             return false;
12015         }
12016         
12017         if(value.length < this.minLength){
12018             return false;
12019         }
12020         if(value.length > this.maxLength){
12021             return false;
12022         }
12023         if(this.vtype){
12024             var vt = Roo.form.VTypes;
12025             if(!vt[this.vtype](value, this)){
12026                 return false;
12027             }
12028         }
12029         if(typeof this.validator == "function"){
12030             var msg = this.validator(value);
12031             if(msg !== true){
12032                 return false;
12033             }
12034             if (typeof(msg) == 'string') {
12035                 this.invalidText = msg;
12036             }
12037         }
12038         
12039         if(this.regex && !this.regex.test(value)){
12040             return false;
12041         }
12042         
12043         return true;
12044     },
12045     
12046      // private
12047     fireKey : function(e){
12048         //Roo.log('field ' + e.getKey());
12049         if(e.isNavKeyPress()){
12050             this.fireEvent("specialkey", this, e);
12051         }
12052     },
12053     focus : function (selectText){
12054         if(this.rendered){
12055             this.inputEl().focus();
12056             if(selectText === true){
12057                 this.inputEl().dom.select();
12058             }
12059         }
12060         return this;
12061     } ,
12062     
12063     onFocus : function(){
12064         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12065            // this.el.addClass(this.focusClass);
12066         }
12067         if(!this.hasFocus){
12068             this.hasFocus = true;
12069             this.startValue = this.getValue();
12070             this.fireEvent("focus", this);
12071         }
12072     },
12073     
12074     beforeBlur : Roo.emptyFn,
12075
12076     
12077     // private
12078     onBlur : function(){
12079         this.beforeBlur();
12080         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
12081             //this.el.removeClass(this.focusClass);
12082         }
12083         this.hasFocus = false;
12084         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
12085             this.validate();
12086         }
12087         var v = this.getValue();
12088         if(String(v) !== String(this.startValue)){
12089             this.fireEvent('change', this, v, this.startValue);
12090         }
12091         this.fireEvent("blur", this);
12092     },
12093     
12094     onChange : function(e)
12095     {
12096         var v = this.getValue();
12097         if(String(v) !== String(this.startValue)){
12098             this.fireEvent('change', this, v, this.startValue);
12099         }
12100         
12101     },
12102     
12103     /**
12104      * Resets the current field value to the originally loaded value and clears any validation messages
12105      */
12106     reset : function(){
12107         this.setValue(this.originalValue);
12108         this.validate();
12109     },
12110      /**
12111      * Returns the name of the field
12112      * @return {Mixed} name The name field
12113      */
12114     getName: function(){
12115         return this.name;
12116     },
12117      /**
12118      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
12119      * @return {Mixed} value The field value
12120      */
12121     getValue : function(){
12122         
12123         var v = this.inputEl().getValue();
12124         
12125         return v;
12126     },
12127     /**
12128      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
12129      * @return {Mixed} value The field value
12130      */
12131     getRawValue : function(){
12132         var v = this.inputEl().getValue();
12133         
12134         return v;
12135     },
12136     
12137     /**
12138      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
12139      * @param {Mixed} value The value to set
12140      */
12141     setRawValue : function(v){
12142         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12143     },
12144     
12145     selectText : function(start, end){
12146         var v = this.getRawValue();
12147         if(v.length > 0){
12148             start = start === undefined ? 0 : start;
12149             end = end === undefined ? v.length : end;
12150             var d = this.inputEl().dom;
12151             if(d.setSelectionRange){
12152                 d.setSelectionRange(start, end);
12153             }else if(d.createTextRange){
12154                 var range = d.createTextRange();
12155                 range.moveStart("character", start);
12156                 range.moveEnd("character", v.length-end);
12157                 range.select();
12158             }
12159         }
12160     },
12161     
12162     /**
12163      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
12164      * @param {Mixed} value The value to set
12165      */
12166     setValue : function(v){
12167         this.value = v;
12168         if(this.rendered){
12169             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
12170             this.validate();
12171         }
12172     },
12173     
12174     /*
12175     processValue : function(value){
12176         if(this.stripCharsRe){
12177             var newValue = value.replace(this.stripCharsRe, '');
12178             if(newValue !== value){
12179                 this.setRawValue(newValue);
12180                 return newValue;
12181             }
12182         }
12183         return value;
12184     },
12185   */
12186     preFocus : function(){
12187         
12188         if(this.selectOnFocus){
12189             this.inputEl().dom.select();
12190         }
12191     },
12192     filterKeys : function(e){
12193         var k = e.getKey();
12194         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
12195             return;
12196         }
12197         var c = e.getCharCode(), cc = String.fromCharCode(c);
12198         if(Roo.isIE && (e.isSpecialKey() || !cc)){
12199             return;
12200         }
12201         if(!this.maskRe.test(cc)){
12202             e.stopEvent();
12203         }
12204     },
12205      /**
12206      * Clear any invalid styles/messages for this field
12207      */
12208     clearInvalid : function(){
12209         
12210         if(!this.el || this.preventMark){ // not rendered
12211             return;
12212         }
12213         
12214         
12215         this.el.removeClass([this.invalidClass, 'is-invalid']);
12216         
12217         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12218             
12219             var feedback = this.el.select('.form-control-feedback', true).first();
12220             
12221             if(feedback){
12222                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12223             }
12224             
12225         }
12226         
12227         if(this.indicator){
12228             this.indicator.removeClass('visible');
12229             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12230         }
12231         
12232         this.fireEvent('valid', this);
12233     },
12234     
12235      /**
12236      * Mark this field as valid
12237      */
12238     markValid : function()
12239     {
12240         if(!this.el  || this.preventMark){ // not rendered...
12241             return;
12242         }
12243         
12244         this.el.removeClass([this.invalidClass, this.validClass]);
12245         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12246
12247         var feedback = this.el.select('.form-control-feedback', true).first();
12248             
12249         if(feedback){
12250             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12251         }
12252         
12253         if(this.indicator){
12254             this.indicator.removeClass('visible');
12255             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12256         }
12257         
12258         if(this.disabled){
12259             return;
12260         }
12261         
12262            
12263         if(this.allowBlank && !this.getRawValue().length){
12264             return;
12265         }
12266         if (Roo.bootstrap.version == 3) {
12267             this.el.addClass(this.validClass);
12268         } else {
12269             this.inputEl().addClass('is-valid');
12270         }
12271
12272         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12273             
12274             var feedback = this.el.select('.form-control-feedback', true).first();
12275             
12276             if(feedback){
12277                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12278                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12279             }
12280             
12281         }
12282         
12283         this.fireEvent('valid', this);
12284     },
12285     
12286      /**
12287      * Mark this field as invalid
12288      * @param {String} msg The validation message
12289      */
12290     markInvalid : function(msg)
12291     {
12292         if(!this.el  || this.preventMark){ // not rendered
12293             return;
12294         }
12295         
12296         this.el.removeClass([this.invalidClass, this.validClass]);
12297         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12298         
12299         var feedback = this.el.select('.form-control-feedback', true).first();
12300             
12301         if(feedback){
12302             this.el.select('.form-control-feedback', true).first().removeClass(
12303                     [this.invalidFeedbackClass, this.validFeedbackClass]);
12304         }
12305
12306         if(this.disabled){
12307             return;
12308         }
12309         
12310         if(this.allowBlank && !this.getRawValue().length){
12311             return;
12312         }
12313         
12314         if(this.indicator){
12315             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
12316             this.indicator.addClass('visible');
12317         }
12318         if (Roo.bootstrap.version == 3) {
12319             this.el.addClass(this.invalidClass);
12320         } else {
12321             this.inputEl().addClass('is-invalid');
12322         }
12323         
12324         
12325         
12326         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12327             
12328             var feedback = this.el.select('.form-control-feedback', true).first();
12329             
12330             if(feedback){
12331                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12332                 
12333                 if(this.getValue().length || this.forceFeedback){
12334                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12335                 }
12336                 
12337             }
12338             
12339         }
12340         
12341         this.fireEvent('invalid', this, msg);
12342     },
12343     // private
12344     SafariOnKeyDown : function(event)
12345     {
12346         // this is a workaround for a password hang bug on chrome/ webkit.
12347         if (this.inputEl().dom.type != 'password') {
12348             return;
12349         }
12350         
12351         var isSelectAll = false;
12352         
12353         if(this.inputEl().dom.selectionEnd > 0){
12354             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
12355         }
12356         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
12357             event.preventDefault();
12358             this.setValue('');
12359             return;
12360         }
12361         
12362         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
12363             
12364             event.preventDefault();
12365             // this is very hacky as keydown always get's upper case.
12366             //
12367             var cc = String.fromCharCode(event.getCharCode());
12368             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
12369             
12370         }
12371     },
12372     adjustWidth : function(tag, w){
12373         tag = tag.toLowerCase();
12374         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
12375             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
12376                 if(tag == 'input'){
12377                     return w + 2;
12378                 }
12379                 if(tag == 'textarea'){
12380                     return w-2;
12381                 }
12382             }else if(Roo.isOpera){
12383                 if(tag == 'input'){
12384                     return w + 2;
12385                 }
12386                 if(tag == 'textarea'){
12387                     return w-2;
12388                 }
12389             }
12390         }
12391         return w;
12392     },
12393     
12394     setFieldLabel : function(v)
12395     {
12396         if(!this.rendered){
12397             return;
12398         }
12399         
12400         if(this.indicatorEl()){
12401             var ar = this.el.select('label > span',true);
12402             
12403             if (ar.elements.length) {
12404                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12405                 this.fieldLabel = v;
12406                 return;
12407             }
12408             
12409             var br = this.el.select('label',true);
12410             
12411             if(br.elements.length) {
12412                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12413                 this.fieldLabel = v;
12414                 return;
12415             }
12416             
12417             Roo.log('Cannot Found any of label > span || label in input');
12418             return;
12419         }
12420         
12421         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
12422         this.fieldLabel = v;
12423         
12424         
12425     }
12426 });
12427
12428  
12429 /*
12430  * - LGPL
12431  *
12432  * Input
12433  * 
12434  */
12435
12436 /**
12437  * @class Roo.bootstrap.TextArea
12438  * @extends Roo.bootstrap.Input
12439  * Bootstrap TextArea class
12440  * @cfg {Number} cols Specifies the visible width of a text area
12441  * @cfg {Number} rows Specifies the visible number of lines in a text area
12442  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
12443  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
12444  * @cfg {string} html text
12445  * 
12446  * @constructor
12447  * Create a new TextArea
12448  * @param {Object} config The config object
12449  */
12450
12451 Roo.bootstrap.TextArea = function(config){
12452     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
12453    
12454 };
12455
12456 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
12457      
12458     cols : false,
12459     rows : 5,
12460     readOnly : false,
12461     warp : 'soft',
12462     resize : false,
12463     value: false,
12464     html: false,
12465     
12466     getAutoCreate : function(){
12467         
12468         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
12469         
12470         var id = Roo.id();
12471         
12472         var cfg = {};
12473         
12474         if(this.inputType != 'hidden'){
12475             cfg.cls = 'form-group' //input-group
12476         }
12477         
12478         var input =  {
12479             tag: 'textarea',
12480             id : id,
12481             warp : this.warp,
12482             rows : this.rows,
12483             value : this.value || '',
12484             html: this.html || '',
12485             cls : 'form-control',
12486             placeholder : this.placeholder || '' 
12487             
12488         };
12489         
12490         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
12491             input.maxLength = this.maxLength;
12492         }
12493         
12494         if(this.resize){
12495             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
12496         }
12497         
12498         if(this.cols){
12499             input.cols = this.cols;
12500         }
12501         
12502         if (this.readOnly) {
12503             input.readonly = true;
12504         }
12505         
12506         if (this.name) {
12507             input.name = this.name;
12508         }
12509         
12510         if (this.size) {
12511             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
12512         }
12513         
12514         var settings=this;
12515         ['xs','sm','md','lg'].map(function(size){
12516             if (settings[size]) {
12517                 cfg.cls += ' col-' + size + '-' + settings[size];
12518             }
12519         });
12520         
12521         var inputblock = input;
12522         
12523         if(this.hasFeedback && !this.allowBlank){
12524             
12525             var feedback = {
12526                 tag: 'span',
12527                 cls: 'glyphicon form-control-feedback'
12528             };
12529
12530             inputblock = {
12531                 cls : 'has-feedback',
12532                 cn :  [
12533                     input,
12534                     feedback
12535                 ] 
12536             };  
12537         }
12538         
12539         
12540         if (this.before || this.after) {
12541             
12542             inputblock = {
12543                 cls : 'input-group',
12544                 cn :  [] 
12545             };
12546             if (this.before) {
12547                 inputblock.cn.push({
12548                     tag :'span',
12549                     cls : 'input-group-addon',
12550                     html : this.before
12551                 });
12552             }
12553             
12554             inputblock.cn.push(input);
12555             
12556             if(this.hasFeedback && !this.allowBlank){
12557                 inputblock.cls += ' has-feedback';
12558                 inputblock.cn.push(feedback);
12559             }
12560             
12561             if (this.after) {
12562                 inputblock.cn.push({
12563                     tag :'span',
12564                     cls : 'input-group-addon',
12565                     html : this.after
12566                 });
12567             }
12568             
12569         }
12570         
12571         if (align ==='left' && this.fieldLabel.length) {
12572             cfg.cn = [
12573                 {
12574                     tag: 'label',
12575                     'for' :  id,
12576                     cls : 'control-label',
12577                     html : this.fieldLabel
12578                 },
12579                 {
12580                     cls : "",
12581                     cn: [
12582                         inputblock
12583                     ]
12584                 }
12585
12586             ];
12587             
12588             if(this.labelWidth > 12){
12589                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
12590             }
12591
12592             if(this.labelWidth < 13 && this.labelmd == 0){
12593                 this.labelmd = this.labelWidth;
12594             }
12595
12596             if(this.labellg > 0){
12597                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
12598                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
12599             }
12600
12601             if(this.labelmd > 0){
12602                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12603                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12604             }
12605
12606             if(this.labelsm > 0){
12607                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12608                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12609             }
12610
12611             if(this.labelxs > 0){
12612                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12613                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12614             }
12615             
12616         } else if ( this.fieldLabel.length) {
12617             cfg.cn = [
12618
12619                {
12620                    tag: 'label',
12621                    //cls : 'input-group-addon',
12622                    html : this.fieldLabel
12623
12624                },
12625
12626                inputblock
12627
12628            ];
12629
12630         } else {
12631
12632             cfg.cn = [
12633
12634                 inputblock
12635
12636             ];
12637                 
12638         }
12639         
12640         if (this.disabled) {
12641             input.disabled=true;
12642         }
12643         
12644         return cfg;
12645         
12646     },
12647     /**
12648      * return the real textarea element.
12649      */
12650     inputEl: function ()
12651     {
12652         return this.el.select('textarea.form-control',true).first();
12653     },
12654     
12655     /**
12656      * Clear any invalid styles/messages for this field
12657      */
12658     clearInvalid : function()
12659     {
12660         
12661         if(!this.el || this.preventMark){ // not rendered
12662             return;
12663         }
12664         
12665         var label = this.el.select('label', true).first();
12666         var icon = this.el.select('i.fa-star', true).first();
12667         
12668         if(label && icon){
12669             icon.remove();
12670         }
12671         this.el.removeClass( this.validClass);
12672         this.inputEl().removeClass('is-invalid');
12673          
12674         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12675             
12676             var feedback = this.el.select('.form-control-feedback', true).first();
12677             
12678             if(feedback){
12679                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12680             }
12681             
12682         }
12683         
12684         this.fireEvent('valid', this);
12685     },
12686     
12687      /**
12688      * Mark this field as valid
12689      */
12690     markValid : function()
12691     {
12692         if(!this.el  || this.preventMark){ // not rendered
12693             return;
12694         }
12695         
12696         this.el.removeClass([this.invalidClass, this.validClass]);
12697         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12698         
12699         var feedback = this.el.select('.form-control-feedback', true).first();
12700             
12701         if(feedback){
12702             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12703         }
12704
12705         if(this.disabled || this.allowBlank){
12706             return;
12707         }
12708         
12709         var label = this.el.select('label', true).first();
12710         var icon = this.el.select('i.fa-star', true).first();
12711         
12712         if(label && icon){
12713             icon.remove();
12714         }
12715         if (Roo.bootstrap.version == 3) {
12716             this.el.addClass(this.validClass);
12717         } else {
12718             this.inputEl().addClass('is-valid');
12719         }
12720         
12721         
12722         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12723             
12724             var feedback = this.el.select('.form-control-feedback', true).first();
12725             
12726             if(feedback){
12727                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12728                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12729             }
12730             
12731         }
12732         
12733         this.fireEvent('valid', this);
12734     },
12735     
12736      /**
12737      * Mark this field as invalid
12738      * @param {String} msg The validation message
12739      */
12740     markInvalid : function(msg)
12741     {
12742         if(!this.el  || this.preventMark){ // not rendered
12743             return;
12744         }
12745         
12746         this.el.removeClass([this.invalidClass, this.validClass]);
12747         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12748         
12749         var feedback = this.el.select('.form-control-feedback', true).first();
12750             
12751         if(feedback){
12752             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12753         }
12754
12755         if(this.disabled || this.allowBlank){
12756             return;
12757         }
12758         
12759         var label = this.el.select('label', true).first();
12760         var icon = this.el.select('i.fa-star', true).first();
12761         
12762         if(!this.getValue().length && label && !icon){
12763             this.el.createChild({
12764                 tag : 'i',
12765                 cls : 'text-danger fa fa-lg fa-star',
12766                 tooltip : 'This field is required',
12767                 style : 'margin-right:5px;'
12768             }, label, true);
12769         }
12770         
12771         if (Roo.bootstrap.version == 3) {
12772             this.el.addClass(this.invalidClass);
12773         } else {
12774             this.inputEl().addClass('is-invalid');
12775         }
12776         
12777         // fixme ... this may be depricated need to test..
12778         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12779             
12780             var feedback = this.el.select('.form-control-feedback', true).first();
12781             
12782             if(feedback){
12783                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12784                 
12785                 if(this.getValue().length || this.forceFeedback){
12786                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12787                 }
12788                 
12789             }
12790             
12791         }
12792         
12793         this.fireEvent('invalid', this, msg);
12794     }
12795 });
12796
12797  
12798 /*
12799  * - LGPL
12800  *
12801  * trigger field - base class for combo..
12802  * 
12803  */
12804  
12805 /**
12806  * @class Roo.bootstrap.TriggerField
12807  * @extends Roo.bootstrap.Input
12808  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12809  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12810  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12811  * for which you can provide a custom implementation.  For example:
12812  * <pre><code>
12813 var trigger = new Roo.bootstrap.TriggerField();
12814 trigger.onTriggerClick = myTriggerFn;
12815 trigger.applyTo('my-field');
12816 </code></pre>
12817  *
12818  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12819  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12820  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12821  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12822  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12823
12824  * @constructor
12825  * Create a new TriggerField.
12826  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12827  * to the base TextField)
12828  */
12829 Roo.bootstrap.TriggerField = function(config){
12830     this.mimicing = false;
12831     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12832 };
12833
12834 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12835     /**
12836      * @cfg {String} triggerClass A CSS class to apply to the trigger
12837      */
12838      /**
12839      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12840      */
12841     hideTrigger:false,
12842
12843     /**
12844      * @cfg {Boolean} removable (true|false) special filter default false
12845      */
12846     removable : false,
12847     
12848     /** @cfg {Boolean} grow @hide */
12849     /** @cfg {Number} growMin @hide */
12850     /** @cfg {Number} growMax @hide */
12851
12852     /**
12853      * @hide 
12854      * @method
12855      */
12856     autoSize: Roo.emptyFn,
12857     // private
12858     monitorTab : true,
12859     // private
12860     deferHeight : true,
12861
12862     
12863     actionMode : 'wrap',
12864     
12865     caret : false,
12866     
12867     
12868     getAutoCreate : function(){
12869        
12870         var align = this.labelAlign || this.parentLabelAlign();
12871         
12872         var id = Roo.id();
12873         
12874         var cfg = {
12875             cls: 'form-group' //input-group
12876         };
12877         
12878         
12879         var input =  {
12880             tag: 'input',
12881             id : id,
12882             type : this.inputType,
12883             cls : 'form-control',
12884             autocomplete: 'new-password',
12885             placeholder : this.placeholder || '' 
12886             
12887         };
12888         if (this.name) {
12889             input.name = this.name;
12890         }
12891         if (this.size) {
12892             input.cls += ' input-' + this.size;
12893         }
12894         
12895         if (this.disabled) {
12896             input.disabled=true;
12897         }
12898         
12899         var inputblock = input;
12900         
12901         if(this.hasFeedback && !this.allowBlank){
12902             
12903             var feedback = {
12904                 tag: 'span',
12905                 cls: 'glyphicon form-control-feedback'
12906             };
12907             
12908             if(this.removable && !this.editable  ){
12909                 inputblock = {
12910                     cls : 'has-feedback',
12911                     cn :  [
12912                         inputblock,
12913                         {
12914                             tag: 'button',
12915                             html : 'x',
12916                             cls : 'roo-combo-removable-btn close'
12917                         },
12918                         feedback
12919                     ] 
12920                 };
12921             } else {
12922                 inputblock = {
12923                     cls : 'has-feedback',
12924                     cn :  [
12925                         inputblock,
12926                         feedback
12927                     ] 
12928                 };
12929             }
12930
12931         } else {
12932             if(this.removable && !this.editable ){
12933                 inputblock = {
12934                     cls : 'roo-removable',
12935                     cn :  [
12936                         inputblock,
12937                         {
12938                             tag: 'button',
12939                             html : 'x',
12940                             cls : 'roo-combo-removable-btn close'
12941                         }
12942                     ] 
12943                 };
12944             }
12945         }
12946         
12947         if (this.before || this.after) {
12948             
12949             inputblock = {
12950                 cls : 'input-group',
12951                 cn :  [] 
12952             };
12953             if (this.before) {
12954                 inputblock.cn.push({
12955                     tag :'span',
12956                     cls : 'input-group-addon input-group-prepend input-group-text',
12957                     html : this.before
12958                 });
12959             }
12960             
12961             inputblock.cn.push(input);
12962             
12963             if(this.hasFeedback && !this.allowBlank){
12964                 inputblock.cls += ' has-feedback';
12965                 inputblock.cn.push(feedback);
12966             }
12967             
12968             if (this.after) {
12969                 inputblock.cn.push({
12970                     tag :'span',
12971                     cls : 'input-group-addon input-group-append input-group-text',
12972                     html : this.after
12973                 });
12974             }
12975             
12976         };
12977         
12978       
12979         
12980         var ibwrap = inputblock;
12981         
12982         if(this.multiple){
12983             ibwrap = {
12984                 tag: 'ul',
12985                 cls: 'roo-select2-choices',
12986                 cn:[
12987                     {
12988                         tag: 'li',
12989                         cls: 'roo-select2-search-field',
12990                         cn: [
12991
12992                             inputblock
12993                         ]
12994                     }
12995                 ]
12996             };
12997                 
12998         }
12999         
13000         var combobox = {
13001             cls: 'roo-select2-container input-group',
13002             cn: [
13003                  {
13004                     tag: 'input',
13005                     type : 'hidden',
13006                     cls: 'form-hidden-field'
13007                 },
13008                 ibwrap
13009             ]
13010         };
13011         
13012         if(!this.multiple && this.showToggleBtn){
13013             
13014             var caret = {
13015                         tag: 'span',
13016                         cls: 'caret'
13017              };
13018             if (this.caret != false) {
13019                 caret = {
13020                      tag: 'i',
13021                      cls: 'fa fa-' + this.caret
13022                 };
13023                 
13024             }
13025             
13026             combobox.cn.push({
13027                 tag :'span',
13028                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
13029                 cn : [
13030                     Roo.bootstrap.version == 3 ? caret : '',
13031                     {
13032                         tag: 'span',
13033                         cls: 'combobox-clear',
13034                         cn  : [
13035                             {
13036                                 tag : 'i',
13037                                 cls: 'icon-remove'
13038                             }
13039                         ]
13040                     }
13041                 ]
13042
13043             })
13044         }
13045         
13046         if(this.multiple){
13047             combobox.cls += ' roo-select2-container-multi';
13048         }
13049          var indicator = {
13050             tag : 'i',
13051             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
13052             tooltip : 'This field is required'
13053         };
13054         if (Roo.bootstrap.version == 4) {
13055             indicator = {
13056                 tag : 'i',
13057                 style : 'display:none'
13058             };
13059         }
13060         
13061         
13062         if (align ==='left' && this.fieldLabel.length) {
13063             
13064             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
13065
13066             cfg.cn = [
13067                 indicator,
13068                 {
13069                     tag: 'label',
13070                     'for' :  id,
13071                     cls : 'control-label',
13072                     html : this.fieldLabel
13073
13074                 },
13075                 {
13076                     cls : "", 
13077                     cn: [
13078                         combobox
13079                     ]
13080                 }
13081
13082             ];
13083             
13084             var labelCfg = cfg.cn[1];
13085             var contentCfg = cfg.cn[2];
13086             
13087             if(this.indicatorpos == 'right'){
13088                 cfg.cn = [
13089                     {
13090                         tag: 'label',
13091                         'for' :  id,
13092                         cls : 'control-label',
13093                         cn : [
13094                             {
13095                                 tag : 'span',
13096                                 html : this.fieldLabel
13097                             },
13098                             indicator
13099                         ]
13100                     },
13101                     {
13102                         cls : "", 
13103                         cn: [
13104                             combobox
13105                         ]
13106                     }
13107
13108                 ];
13109                 
13110                 labelCfg = cfg.cn[0];
13111                 contentCfg = cfg.cn[1];
13112             }
13113             
13114             if(this.labelWidth > 12){
13115                 labelCfg.style = "width: " + this.labelWidth + 'px';
13116             }
13117             
13118             if(this.labelWidth < 13 && this.labelmd == 0){
13119                 this.labelmd = this.labelWidth;
13120             }
13121             
13122             if(this.labellg > 0){
13123                 labelCfg.cls += ' col-lg-' + this.labellg;
13124                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
13125             }
13126             
13127             if(this.labelmd > 0){
13128                 labelCfg.cls += ' col-md-' + this.labelmd;
13129                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
13130             }
13131             
13132             if(this.labelsm > 0){
13133                 labelCfg.cls += ' col-sm-' + this.labelsm;
13134                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
13135             }
13136             
13137             if(this.labelxs > 0){
13138                 labelCfg.cls += ' col-xs-' + this.labelxs;
13139                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
13140             }
13141             
13142         } else if ( this.fieldLabel.length) {
13143 //                Roo.log(" label");
13144             cfg.cn = [
13145                 indicator,
13146                {
13147                    tag: 'label',
13148                    //cls : 'input-group-addon',
13149                    html : this.fieldLabel
13150
13151                },
13152
13153                combobox
13154
13155             ];
13156             
13157             if(this.indicatorpos == 'right'){
13158                 
13159                 cfg.cn = [
13160                     {
13161                        tag: 'label',
13162                        cn : [
13163                            {
13164                                tag : 'span',
13165                                html : this.fieldLabel
13166                            },
13167                            indicator
13168                        ]
13169
13170                     },
13171                     combobox
13172
13173                 ];
13174
13175             }
13176
13177         } else {
13178             
13179 //                Roo.log(" no label && no align");
13180                 cfg = combobox
13181                      
13182                 
13183         }
13184         
13185         var settings=this;
13186         ['xs','sm','md','lg'].map(function(size){
13187             if (settings[size]) {
13188                 cfg.cls += ' col-' + size + '-' + settings[size];
13189             }
13190         });
13191         
13192         return cfg;
13193         
13194     },
13195     
13196     
13197     
13198     // private
13199     onResize : function(w, h){
13200 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
13201 //        if(typeof w == 'number'){
13202 //            var x = w - this.trigger.getWidth();
13203 //            this.inputEl().setWidth(this.adjustWidth('input', x));
13204 //            this.trigger.setStyle('left', x+'px');
13205 //        }
13206     },
13207
13208     // private
13209     adjustSize : Roo.BoxComponent.prototype.adjustSize,
13210
13211     // private
13212     getResizeEl : function(){
13213         return this.inputEl();
13214     },
13215
13216     // private
13217     getPositionEl : function(){
13218         return this.inputEl();
13219     },
13220
13221     // private
13222     alignErrorIcon : function(){
13223         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
13224     },
13225
13226     // private
13227     initEvents : function(){
13228         
13229         this.createList();
13230         
13231         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
13232         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
13233         if(!this.multiple && this.showToggleBtn){
13234             this.trigger = this.el.select('span.dropdown-toggle',true).first();
13235             if(this.hideTrigger){
13236                 this.trigger.setDisplayed(false);
13237             }
13238             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
13239         }
13240         
13241         if(this.multiple){
13242             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
13243         }
13244         
13245         if(this.removable && !this.editable && !this.tickable){
13246             var close = this.closeTriggerEl();
13247             
13248             if(close){
13249                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
13250                 close.on('click', this.removeBtnClick, this, close);
13251             }
13252         }
13253         
13254         //this.trigger.addClassOnOver('x-form-trigger-over');
13255         //this.trigger.addClassOnClick('x-form-trigger-click');
13256         
13257         //if(!this.width){
13258         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
13259         //}
13260     },
13261     
13262     closeTriggerEl : function()
13263     {
13264         var close = this.el.select('.roo-combo-removable-btn', true).first();
13265         return close ? close : false;
13266     },
13267     
13268     removeBtnClick : function(e, h, el)
13269     {
13270         e.preventDefault();
13271         
13272         if(this.fireEvent("remove", this) !== false){
13273             this.reset();
13274             this.fireEvent("afterremove", this)
13275         }
13276     },
13277     
13278     createList : function()
13279     {
13280         this.list = Roo.get(document.body).createChild({
13281             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
13282             cls: 'typeahead typeahead-long dropdown-menu shadow',
13283             style: 'display:none'
13284         });
13285         
13286         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
13287         
13288     },
13289
13290     // private
13291     initTrigger : function(){
13292        
13293     },
13294
13295     // private
13296     onDestroy : function(){
13297         if(this.trigger){
13298             this.trigger.removeAllListeners();
13299           //  this.trigger.remove();
13300         }
13301         //if(this.wrap){
13302         //    this.wrap.remove();
13303         //}
13304         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
13305     },
13306
13307     // private
13308     onFocus : function(){
13309         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
13310         /*
13311         if(!this.mimicing){
13312             this.wrap.addClass('x-trigger-wrap-focus');
13313             this.mimicing = true;
13314             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
13315             if(this.monitorTab){
13316                 this.el.on("keydown", this.checkTab, this);
13317             }
13318         }
13319         */
13320     },
13321
13322     // private
13323     checkTab : function(e){
13324         if(e.getKey() == e.TAB){
13325             this.triggerBlur();
13326         }
13327     },
13328
13329     // private
13330     onBlur : function(){
13331         // do nothing
13332     },
13333
13334     // private
13335     mimicBlur : function(e, t){
13336         /*
13337         if(!this.wrap.contains(t) && this.validateBlur()){
13338             this.triggerBlur();
13339         }
13340         */
13341     },
13342
13343     // private
13344     triggerBlur : function(){
13345         this.mimicing = false;
13346         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
13347         if(this.monitorTab){
13348             this.el.un("keydown", this.checkTab, this);
13349         }
13350         //this.wrap.removeClass('x-trigger-wrap-focus');
13351         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
13352     },
13353
13354     // private
13355     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
13356     validateBlur : function(e, t){
13357         return true;
13358     },
13359
13360     // private
13361     onDisable : function(){
13362         this.inputEl().dom.disabled = true;
13363         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
13364         //if(this.wrap){
13365         //    this.wrap.addClass('x-item-disabled');
13366         //}
13367     },
13368
13369     // private
13370     onEnable : function(){
13371         this.inputEl().dom.disabled = false;
13372         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
13373         //if(this.wrap){
13374         //    this.el.removeClass('x-item-disabled');
13375         //}
13376     },
13377
13378     // private
13379     onShow : function(){
13380         var ae = this.getActionEl();
13381         
13382         if(ae){
13383             ae.dom.style.display = '';
13384             ae.dom.style.visibility = 'visible';
13385         }
13386     },
13387
13388     // private
13389     
13390     onHide : function(){
13391         var ae = this.getActionEl();
13392         ae.dom.style.display = 'none';
13393     },
13394
13395     /**
13396      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
13397      * by an implementing function.
13398      * @method
13399      * @param {EventObject} e
13400      */
13401     onTriggerClick : Roo.emptyFn
13402 });
13403  
13404 /*
13405 * Licence: LGPL
13406 */
13407
13408 /**
13409  * @class Roo.bootstrap.CardUploader
13410  * @extends Roo.bootstrap.Button
13411  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
13412  * @cfg {Number} errorTimeout default 3000
13413  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
13414  * @cfg {Array}  html The button text.
13415
13416  *
13417  * @constructor
13418  * Create a new CardUploader
13419  * @param {Object} config The config object
13420  */
13421
13422 Roo.bootstrap.CardUploader = function(config){
13423     
13424  
13425     
13426     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
13427     
13428     
13429     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
13430         return r.data.id
13431      });
13432     
13433      this.addEvents({
13434          // raw events
13435         /**
13436          * @event preview
13437          * When a image is clicked on - and needs to display a slideshow or similar..
13438          * @param {Roo.bootstrap.Card} this
13439          * @param {Object} The image information data 
13440          *
13441          */
13442         'preview' : true,
13443          /**
13444          * @event download
13445          * When a the download link is clicked
13446          * @param {Roo.bootstrap.Card} this
13447          * @param {Object} The image information data  contains 
13448          */
13449         'download' : true
13450         
13451     });
13452 };
13453  
13454 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
13455     
13456      
13457     errorTimeout : 3000,
13458      
13459     images : false,
13460    
13461     fileCollection : false,
13462     allowBlank : true,
13463     
13464     getAutoCreate : function()
13465     {
13466         
13467         var cfg =  {
13468             cls :'form-group' ,
13469             cn : [
13470                
13471                 {
13472                     tag: 'label',
13473                    //cls : 'input-group-addon',
13474                     html : this.fieldLabel
13475
13476                 },
13477
13478                 {
13479                     tag: 'input',
13480                     type : 'hidden',
13481                     name : this.name,
13482                     value : this.value,
13483                     cls : 'd-none  form-control'
13484                 },
13485                 
13486                 {
13487                     tag: 'input',
13488                     multiple : 'multiple',
13489                     type : 'file',
13490                     cls : 'd-none  roo-card-upload-selector'
13491                 },
13492                 
13493                 {
13494                     cls : 'roo-card-uploader-button-container w-100 mb-2'
13495                 },
13496                 {
13497                     cls : 'card-columns roo-card-uploader-container'
13498                 }
13499
13500             ]
13501         };
13502            
13503          
13504         return cfg;
13505     },
13506     
13507     getChildContainer : function() /// what children are added to.
13508     {
13509         return this.containerEl;
13510     },
13511    
13512     getButtonContainer : function() /// what children are added to.
13513     {
13514         return this.el.select(".roo-card-uploader-button-container").first();
13515     },
13516    
13517     initEvents : function()
13518     {
13519         
13520         Roo.bootstrap.Input.prototype.initEvents.call(this);
13521         
13522         var t = this;
13523         this.addxtype({
13524             xns: Roo.bootstrap,
13525
13526             xtype : 'Button',
13527             container_method : 'getButtonContainer' ,            
13528             html :  this.html, // fix changable?
13529             cls : 'w-100 ',
13530             listeners : {
13531                 'click' : function(btn, e) {
13532                     t.onClick(e);
13533                 }
13534             }
13535         });
13536         
13537         
13538         
13539         
13540         this.urlAPI = (window.createObjectURL && window) || 
13541                                 (window.URL && URL.revokeObjectURL && URL) || 
13542                                 (window.webkitURL && webkitURL);
13543                         
13544          
13545          
13546          
13547         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
13548         
13549         this.selectorEl.on('change', this.onFileSelected, this);
13550         if (this.images) {
13551             var t = this;
13552             this.images.forEach(function(img) {
13553                 t.addCard(img)
13554             });
13555             this.images = false;
13556         }
13557         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
13558          
13559        
13560     },
13561     
13562    
13563     onClick : function(e)
13564     {
13565         e.preventDefault();
13566          
13567         this.selectorEl.dom.click();
13568          
13569     },
13570     
13571     onFileSelected : function(e)
13572     {
13573         e.preventDefault();
13574         
13575         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
13576             return;
13577         }
13578         
13579         Roo.each(this.selectorEl.dom.files, function(file){    
13580             this.addFile(file);
13581         }, this);
13582          
13583     },
13584     
13585       
13586     
13587       
13588     
13589     addFile : function(file)
13590     {
13591            
13592         if(typeof(file) === 'string'){
13593             throw "Add file by name?"; // should not happen
13594             return;
13595         }
13596         
13597         if(!file || !this.urlAPI){
13598             return;
13599         }
13600         
13601         // file;
13602         // file.type;
13603         
13604         var _this = this;
13605         
13606         
13607         var url = _this.urlAPI.createObjectURL( file);
13608            
13609         this.addCard({
13610             id : Roo.bootstrap.CardUploader.ID--,
13611             is_uploaded : false,
13612             src : url,
13613             srcfile : file,
13614             title : file.name,
13615             mimetype : file.type,
13616             preview : false,
13617             is_deleted : 0
13618         });
13619         
13620     },
13621     
13622     /**
13623      * addCard - add an Attachment to the uploader
13624      * @param data - the data about the image to upload
13625      *
13626      * {
13627           id : 123
13628           title : "Title of file",
13629           is_uploaded : false,
13630           src : "http://.....",
13631           srcfile : { the File upload object },
13632           mimetype : file.type,
13633           preview : false,
13634           is_deleted : 0
13635           .. any other data...
13636         }
13637      *
13638      * 
13639     */
13640     
13641     addCard : function (data)
13642     {
13643         // hidden input element?
13644         // if the file is not an image...
13645         //then we need to use something other that and header_image
13646         var t = this;
13647         //   remove.....
13648         var footer = [
13649             {
13650                 xns : Roo.bootstrap,
13651                 xtype : 'CardFooter',
13652                  items: [
13653                     {
13654                         xns : Roo.bootstrap,
13655                         xtype : 'Element',
13656                         cls : 'd-flex',
13657                         items : [
13658                             
13659                             {
13660                                 xns : Roo.bootstrap,
13661                                 xtype : 'Button',
13662                                 html : String.format("<small>{0}</small>", data.title),
13663                                 cls : 'col-10 text-left',
13664                                 size: 'sm',
13665                                 weight: 'link',
13666                                 fa : 'download',
13667                                 listeners : {
13668                                     click : function() {
13669                                      
13670                                         t.fireEvent( "download", t, data );
13671                                     }
13672                                 }
13673                             },
13674                           
13675                             {
13676                                 xns : Roo.bootstrap,
13677                                 xtype : 'Button',
13678                                 style: 'max-height: 28px; ',
13679                                 size : 'sm',
13680                                 weight: 'danger',
13681                                 cls : 'col-2',
13682                                 fa : 'times',
13683                                 listeners : {
13684                                     click : function() {
13685                                         t.removeCard(data.id)
13686                                     }
13687                                 }
13688                             }
13689                         ]
13690                     }
13691                     
13692                 ] 
13693             }
13694             
13695         ];
13696         
13697         var cn = this.addxtype(
13698             {
13699                  
13700                 xns : Roo.bootstrap,
13701                 xtype : 'Card',
13702                 closeable : true,
13703                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13704                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13705                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13706                 data : data,
13707                 html : false,
13708                  
13709                 items : footer,
13710                 initEvents : function() {
13711                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13712                     var card = this;
13713                     this.imgEl = this.el.select('.card-img-top').first();
13714                     if (this.imgEl) {
13715                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13716                         this.imgEl.set({ 'pointer' : 'cursor' });
13717                                   
13718                     }
13719                     this.getCardFooter().addClass('p-1');
13720                     
13721                   
13722                 }
13723                 
13724             }
13725         );
13726         // dont' really need ot update items.
13727         // this.items.push(cn);
13728         this.fileCollection.add(cn);
13729         
13730         if (!data.srcfile) {
13731             this.updateInput();
13732             return;
13733         }
13734             
13735         var _t = this;
13736         var reader = new FileReader();
13737         reader.addEventListener("load", function() {  
13738             data.srcdata =  reader.result;
13739             _t.updateInput();
13740         });
13741         reader.readAsDataURL(data.srcfile);
13742         
13743         
13744         
13745     },
13746     removeCard : function(id)
13747     {
13748         
13749         var card  = this.fileCollection.get(id);
13750         card.data.is_deleted = 1;
13751         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13752         //this.fileCollection.remove(card);
13753         //this.items = this.items.filter(function(e) { return e != card });
13754         // dont' really need ot update items.
13755         card.el.dom.parentNode.removeChild(card.el.dom);
13756         this.updateInput();
13757
13758         
13759     },
13760     reset: function()
13761     {
13762         this.fileCollection.each(function(card) {
13763             if (card.el.dom && card.el.dom.parentNode) {
13764                 card.el.dom.parentNode.removeChild(card.el.dom);
13765             }
13766         });
13767         this.fileCollection.clear();
13768         this.updateInput();
13769     },
13770     
13771     updateInput : function()
13772     {
13773          var data = [];
13774         this.fileCollection.each(function(e) {
13775             data.push(e.data);
13776             
13777         });
13778         this.inputEl().dom.value = JSON.stringify(data);
13779         
13780         
13781         
13782     }
13783     
13784     
13785 });
13786
13787
13788 Roo.bootstrap.CardUploader.ID = -1;/*
13789  * Based on:
13790  * Ext JS Library 1.1.1
13791  * Copyright(c) 2006-2007, Ext JS, LLC.
13792  *
13793  * Originally Released Under LGPL - original licence link has changed is not relivant.
13794  *
13795  * Fork - LGPL
13796  * <script type="text/javascript">
13797  */
13798
13799
13800 /**
13801  * @class Roo.data.SortTypes
13802  * @singleton
13803  * Defines the default sorting (casting?) comparison functions used when sorting data.
13804  */
13805 Roo.data.SortTypes = {
13806     /**
13807      * Default sort that does nothing
13808      * @param {Mixed} s The value being converted
13809      * @return {Mixed} The comparison value
13810      */
13811     none : function(s){
13812         return s;
13813     },
13814     
13815     /**
13816      * The regular expression used to strip tags
13817      * @type {RegExp}
13818      * @property
13819      */
13820     stripTagsRE : /<\/?[^>]+>/gi,
13821     
13822     /**
13823      * Strips all HTML tags to sort on text only
13824      * @param {Mixed} s The value being converted
13825      * @return {String} The comparison value
13826      */
13827     asText : function(s){
13828         return String(s).replace(this.stripTagsRE, "");
13829     },
13830     
13831     /**
13832      * Strips all HTML tags to sort on text only - Case insensitive
13833      * @param {Mixed} s The value being converted
13834      * @return {String} The comparison value
13835      */
13836     asUCText : function(s){
13837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13838     },
13839     
13840     /**
13841      * Case insensitive string
13842      * @param {Mixed} s The value being converted
13843      * @return {String} The comparison value
13844      */
13845     asUCString : function(s) {
13846         return String(s).toUpperCase();
13847     },
13848     
13849     /**
13850      * Date sorting
13851      * @param {Mixed} s The value being converted
13852      * @return {Number} The comparison value
13853      */
13854     asDate : function(s) {
13855         if(!s){
13856             return 0;
13857         }
13858         if(s instanceof Date){
13859             return s.getTime();
13860         }
13861         return Date.parse(String(s));
13862     },
13863     
13864     /**
13865      * Float sorting
13866      * @param {Mixed} s The value being converted
13867      * @return {Float} The comparison value
13868      */
13869     asFloat : function(s) {
13870         var val = parseFloat(String(s).replace(/,/g, ""));
13871         if(isNaN(val)) {
13872             val = 0;
13873         }
13874         return val;
13875     },
13876     
13877     /**
13878      * Integer sorting
13879      * @param {Mixed} s The value being converted
13880      * @return {Number} The comparison value
13881      */
13882     asInt : function(s) {
13883         var val = parseInt(String(s).replace(/,/g, ""));
13884         if(isNaN(val)) {
13885             val = 0;
13886         }
13887         return val;
13888     }
13889 };/*
13890  * Based on:
13891  * Ext JS Library 1.1.1
13892  * Copyright(c) 2006-2007, Ext JS, LLC.
13893  *
13894  * Originally Released Under LGPL - original licence link has changed is not relivant.
13895  *
13896  * Fork - LGPL
13897  * <script type="text/javascript">
13898  */
13899
13900 /**
13901 * @class Roo.data.Record
13902  * Instances of this class encapsulate both record <em>definition</em> information, and record
13903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13904  * to access Records cached in an {@link Roo.data.Store} object.<br>
13905  * <p>
13906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13908  * objects.<br>
13909  * <p>
13910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13911  * @constructor
13912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13913  * {@link #create}. The parameters are the same.
13914  * @param {Array} data An associative Array of data values keyed by the field name.
13915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13917  * not specified an integer id is generated.
13918  */
13919 Roo.data.Record = function(data, id){
13920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13921     this.data = data;
13922 };
13923
13924 /**
13925  * Generate a constructor for a specific record layout.
13926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13928  * Each field definition object may contain the following properties: <ul>
13929  * <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,
13930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13936  * this may be omitted.</p></li>
13937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13938  * <ul><li>auto (Default, implies no conversion)</li>
13939  * <li>string</li>
13940  * <li>int</li>
13941  * <li>float</li>
13942  * <li>boolean</li>
13943  * <li>date</li></ul></p></li>
13944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13947  * by the Reader into an object that will be stored in the Record. It is passed the
13948  * following parameters:<ul>
13949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13950  * </ul></p></li>
13951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13952  * </ul>
13953  * <br>usage:<br><pre><code>
13954 var TopicRecord = Roo.data.Record.create(
13955     {name: 'title', mapping: 'topic_title'},
13956     {name: 'author', mapping: 'username'},
13957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13959     {name: 'lastPoster', mapping: 'user2'},
13960     {name: 'excerpt', mapping: 'post_text'}
13961 );
13962
13963 var myNewRecord = new TopicRecord({
13964     title: 'Do my job please',
13965     author: 'noobie',
13966     totalPosts: 1,
13967     lastPost: new Date(),
13968     lastPoster: 'Animal',
13969     excerpt: 'No way dude!'
13970 });
13971 myStore.add(myNewRecord);
13972 </code></pre>
13973  * @method create
13974  * @static
13975  */
13976 Roo.data.Record.create = function(o){
13977     var f = function(){
13978         f.superclass.constructor.apply(this, arguments);
13979     };
13980     Roo.extend(f, Roo.data.Record);
13981     var p = f.prototype;
13982     p.fields = new Roo.util.MixedCollection(false, function(field){
13983         return field.name;
13984     });
13985     for(var i = 0, len = o.length; i < len; i++){
13986         p.fields.add(new Roo.data.Field(o[i]));
13987     }
13988     f.getField = function(name){
13989         return p.fields.get(name);  
13990     };
13991     return f;
13992 };
13993
13994 Roo.data.Record.AUTO_ID = 1000;
13995 Roo.data.Record.EDIT = 'edit';
13996 Roo.data.Record.REJECT = 'reject';
13997 Roo.data.Record.COMMIT = 'commit';
13998
13999 Roo.data.Record.prototype = {
14000     /**
14001      * Readonly flag - true if this record has been modified.
14002      * @type Boolean
14003      */
14004     dirty : false,
14005     editing : false,
14006     error: null,
14007     modified: null,
14008
14009     // private
14010     join : function(store){
14011         this.store = store;
14012     },
14013
14014     /**
14015      * Set the named field to the specified value.
14016      * @param {String} name The name of the field to set.
14017      * @param {Object} value The value to set the field to.
14018      */
14019     set : function(name, value){
14020         if(this.data[name] == value){
14021             return;
14022         }
14023         this.dirty = true;
14024         if(!this.modified){
14025             this.modified = {};
14026         }
14027         if(typeof this.modified[name] == 'undefined'){
14028             this.modified[name] = this.data[name];
14029         }
14030         this.data[name] = value;
14031         if(!this.editing && this.store){
14032             this.store.afterEdit(this);
14033         }       
14034     },
14035
14036     /**
14037      * Get the value of the named field.
14038      * @param {String} name The name of the field to get the value of.
14039      * @return {Object} The value of the field.
14040      */
14041     get : function(name){
14042         return this.data[name]; 
14043     },
14044
14045     // private
14046     beginEdit : function(){
14047         this.editing = true;
14048         this.modified = {}; 
14049     },
14050
14051     // private
14052     cancelEdit : function(){
14053         this.editing = false;
14054         delete this.modified;
14055     },
14056
14057     // private
14058     endEdit : function(){
14059         this.editing = false;
14060         if(this.dirty && this.store){
14061             this.store.afterEdit(this);
14062         }
14063     },
14064
14065     /**
14066      * Usually called by the {@link Roo.data.Store} which owns the Record.
14067      * Rejects all changes made to the Record since either creation, or the last commit operation.
14068      * Modified fields are reverted to their original values.
14069      * <p>
14070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14071      * of reject operations.
14072      */
14073     reject : function(){
14074         var m = this.modified;
14075         for(var n in m){
14076             if(typeof m[n] != "function"){
14077                 this.data[n] = m[n];
14078             }
14079         }
14080         this.dirty = false;
14081         delete this.modified;
14082         this.editing = false;
14083         if(this.store){
14084             this.store.afterReject(this);
14085         }
14086     },
14087
14088     /**
14089      * Usually called by the {@link Roo.data.Store} which owns the Record.
14090      * Commits all changes made to the Record since either creation, or the last commit operation.
14091      * <p>
14092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
14093      * of commit operations.
14094      */
14095     commit : function(){
14096         this.dirty = false;
14097         delete this.modified;
14098         this.editing = false;
14099         if(this.store){
14100             this.store.afterCommit(this);
14101         }
14102     },
14103
14104     // private
14105     hasError : function(){
14106         return this.error != null;
14107     },
14108
14109     // private
14110     clearError : function(){
14111         this.error = null;
14112     },
14113
14114     /**
14115      * Creates a copy of this record.
14116      * @param {String} id (optional) A new record id if you don't want to use this record's id
14117      * @return {Record}
14118      */
14119     copy : function(newId) {
14120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
14121     }
14122 };/*
14123  * Based on:
14124  * Ext JS Library 1.1.1
14125  * Copyright(c) 2006-2007, Ext JS, LLC.
14126  *
14127  * Originally Released Under LGPL - original licence link has changed is not relivant.
14128  *
14129  * Fork - LGPL
14130  * <script type="text/javascript">
14131  */
14132
14133
14134
14135 /**
14136  * @class Roo.data.Store
14137  * @extends Roo.util.Observable
14138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
14139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
14140  * <p>
14141  * 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
14142  * has no knowledge of the format of the data returned by the Proxy.<br>
14143  * <p>
14144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
14145  * instances from the data object. These records are cached and made available through accessor functions.
14146  * @constructor
14147  * Creates a new Store.
14148  * @param {Object} config A config object containing the objects needed for the Store to access data,
14149  * and read the data into Records.
14150  */
14151 Roo.data.Store = function(config){
14152     this.data = new Roo.util.MixedCollection(false);
14153     this.data.getKey = function(o){
14154         return o.id;
14155     };
14156     this.baseParams = {};
14157     // private
14158     this.paramNames = {
14159         "start" : "start",
14160         "limit" : "limit",
14161         "sort" : "sort",
14162         "dir" : "dir",
14163         "multisort" : "_multisort"
14164     };
14165
14166     if(config && config.data){
14167         this.inlineData = config.data;
14168         delete config.data;
14169     }
14170
14171     Roo.apply(this, config);
14172     
14173     if(this.reader){ // reader passed
14174         this.reader = Roo.factory(this.reader, Roo.data);
14175         this.reader.xmodule = this.xmodule || false;
14176         if(!this.recordType){
14177             this.recordType = this.reader.recordType;
14178         }
14179         if(this.reader.onMetaChange){
14180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
14181         }
14182     }
14183
14184     if(this.recordType){
14185         this.fields = this.recordType.prototype.fields;
14186     }
14187     this.modified = [];
14188
14189     this.addEvents({
14190         /**
14191          * @event datachanged
14192          * Fires when the data cache has changed, and a widget which is using this Store
14193          * as a Record cache should refresh its view.
14194          * @param {Store} this
14195          */
14196         datachanged : true,
14197         /**
14198          * @event metachange
14199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
14200          * @param {Store} this
14201          * @param {Object} meta The JSON metadata
14202          */
14203         metachange : true,
14204         /**
14205          * @event add
14206          * Fires when Records have been added to the Store
14207          * @param {Store} this
14208          * @param {Roo.data.Record[]} records The array of Records added
14209          * @param {Number} index The index at which the record(s) were added
14210          */
14211         add : true,
14212         /**
14213          * @event remove
14214          * Fires when a Record has been removed from the Store
14215          * @param {Store} this
14216          * @param {Roo.data.Record} record The Record that was removed
14217          * @param {Number} index The index at which the record was removed
14218          */
14219         remove : true,
14220         /**
14221          * @event update
14222          * Fires when a Record has been updated
14223          * @param {Store} this
14224          * @param {Roo.data.Record} record The Record that was updated
14225          * @param {String} operation The update operation being performed.  Value may be one of:
14226          * <pre><code>
14227  Roo.data.Record.EDIT
14228  Roo.data.Record.REJECT
14229  Roo.data.Record.COMMIT
14230          * </code></pre>
14231          */
14232         update : true,
14233         /**
14234          * @event clear
14235          * Fires when the data cache has been cleared.
14236          * @param {Store} this
14237          */
14238         clear : true,
14239         /**
14240          * @event beforeload
14241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
14242          * the load action will be canceled.
14243          * @param {Store} this
14244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14245          */
14246         beforeload : true,
14247         /**
14248          * @event beforeloadadd
14249          * Fires after a new set of Records has been loaded.
14250          * @param {Store} this
14251          * @param {Roo.data.Record[]} records The Records that were loaded
14252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14253          */
14254         beforeloadadd : true,
14255         /**
14256          * @event load
14257          * Fires after a new set of Records has been loaded, before they are added to the store.
14258          * @param {Store} this
14259          * @param {Roo.data.Record[]} records The Records that were loaded
14260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
14261          * @params {Object} return from reader
14262          */
14263         load : true,
14264         /**
14265          * @event loadexception
14266          * Fires if an exception occurs in the Proxy during loading.
14267          * Called with the signature of the Proxy's "loadexception" event.
14268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
14269          * 
14270          * @param {Proxy} 
14271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
14272          * @param {Object} load options 
14273          * @param {Object} jsonData from your request (normally this contains the Exception)
14274          */
14275         loadexception : true
14276     });
14277     
14278     if(this.proxy){
14279         this.proxy = Roo.factory(this.proxy, Roo.data);
14280         this.proxy.xmodule = this.xmodule || false;
14281         this.relayEvents(this.proxy,  ["loadexception"]);
14282     }
14283     this.sortToggle = {};
14284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
14285
14286     Roo.data.Store.superclass.constructor.call(this);
14287
14288     if(this.inlineData){
14289         this.loadData(this.inlineData);
14290         delete this.inlineData;
14291     }
14292 };
14293
14294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
14295      /**
14296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
14297     * without a remote query - used by combo/forms at present.
14298     */
14299     
14300     /**
14301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
14302     */
14303     /**
14304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
14305     */
14306     /**
14307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
14308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
14309     */
14310     /**
14311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
14312     * on any HTTP request
14313     */
14314     /**
14315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
14316     */
14317     /**
14318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
14319     */
14320     multiSort: false,
14321     /**
14322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
14323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
14324     */
14325     remoteSort : false,
14326
14327     /**
14328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
14329      * loaded or when a record is removed. (defaults to false).
14330     */
14331     pruneModifiedRecords : false,
14332
14333     // private
14334     lastOptions : null,
14335
14336     /**
14337      * Add Records to the Store and fires the add event.
14338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14339      */
14340     add : function(records){
14341         records = [].concat(records);
14342         for(var i = 0, len = records.length; i < len; i++){
14343             records[i].join(this);
14344         }
14345         var index = this.data.length;
14346         this.data.addAll(records);
14347         this.fireEvent("add", this, records, index);
14348     },
14349
14350     /**
14351      * Remove a Record from the Store and fires the remove event.
14352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
14353      */
14354     remove : function(record){
14355         var index = this.data.indexOf(record);
14356         this.data.removeAt(index);
14357  
14358         if(this.pruneModifiedRecords){
14359             this.modified.remove(record);
14360         }
14361         this.fireEvent("remove", this, record, index);
14362     },
14363
14364     /**
14365      * Remove all Records from the Store and fires the clear event.
14366      */
14367     removeAll : function(){
14368         this.data.clear();
14369         if(this.pruneModifiedRecords){
14370             this.modified = [];
14371         }
14372         this.fireEvent("clear", this);
14373     },
14374
14375     /**
14376      * Inserts Records to the Store at the given index and fires the add event.
14377      * @param {Number} index The start index at which to insert the passed Records.
14378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
14379      */
14380     insert : function(index, records){
14381         records = [].concat(records);
14382         for(var i = 0, len = records.length; i < len; i++){
14383             this.data.insert(index, records[i]);
14384             records[i].join(this);
14385         }
14386         this.fireEvent("add", this, records, index);
14387     },
14388
14389     /**
14390      * Get the index within the cache of the passed Record.
14391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
14392      * @return {Number} The index of the passed Record. Returns -1 if not found.
14393      */
14394     indexOf : function(record){
14395         return this.data.indexOf(record);
14396     },
14397
14398     /**
14399      * Get the index within the cache of the Record with the passed id.
14400      * @param {String} id The id of the Record to find.
14401      * @return {Number} The index of the Record. Returns -1 if not found.
14402      */
14403     indexOfId : function(id){
14404         return this.data.indexOfKey(id);
14405     },
14406
14407     /**
14408      * Get the Record with the specified id.
14409      * @param {String} id The id of the Record to find.
14410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
14411      */
14412     getById : function(id){
14413         return this.data.key(id);
14414     },
14415
14416     /**
14417      * Get the Record at the specified index.
14418      * @param {Number} index The index of the Record to find.
14419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
14420      */
14421     getAt : function(index){
14422         return this.data.itemAt(index);
14423     },
14424
14425     /**
14426      * Returns a range of Records between specified indices.
14427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
14428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
14429      * @return {Roo.data.Record[]} An array of Records
14430      */
14431     getRange : function(start, end){
14432         return this.data.getRange(start, end);
14433     },
14434
14435     // private
14436     storeOptions : function(o){
14437         o = Roo.apply({}, o);
14438         delete o.callback;
14439         delete o.scope;
14440         this.lastOptions = o;
14441     },
14442
14443     /**
14444      * Loads the Record cache from the configured Proxy using the configured Reader.
14445      * <p>
14446      * If using remote paging, then the first load call must specify the <em>start</em>
14447      * and <em>limit</em> properties in the options.params property to establish the initial
14448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
14449      * <p>
14450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
14451      * and this call will return before the new data has been loaded. Perform any post-processing
14452      * in a callback function, or in a "load" event handler.</strong>
14453      * <p>
14454      * @param {Object} options An object containing properties which control loading options:<ul>
14455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
14456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
14457      * passed the following arguments:<ul>
14458      * <li>r : Roo.data.Record[]</li>
14459      * <li>options: Options object from the load call</li>
14460      * <li>success: Boolean success indicator</li></ul></li>
14461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
14462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
14463      * </ul>
14464      */
14465     load : function(options){
14466         options = options || {};
14467         if(this.fireEvent("beforeload", this, options) !== false){
14468             this.storeOptions(options);
14469             var p = Roo.apply(options.params || {}, this.baseParams);
14470             // if meta was not loaded from remote source.. try requesting it.
14471             if (!this.reader.metaFromRemote) {
14472                 p._requestMeta = 1;
14473             }
14474             if(this.sortInfo && this.remoteSort){
14475                 var pn = this.paramNames;
14476                 p[pn["sort"]] = this.sortInfo.field;
14477                 p[pn["dir"]] = this.sortInfo.direction;
14478             }
14479             if (this.multiSort) {
14480                 var pn = this.paramNames;
14481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
14482             }
14483             
14484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
14485         }
14486     },
14487
14488     /**
14489      * Reloads the Record cache from the configured Proxy using the configured Reader and
14490      * the options from the last load operation performed.
14491      * @param {Object} options (optional) An object containing properties which may override the options
14492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
14493      * the most recently used options are reused).
14494      */
14495     reload : function(options){
14496         this.load(Roo.applyIf(options||{}, this.lastOptions));
14497     },
14498
14499     // private
14500     // Called as a callback by the Reader during a load operation.
14501     loadRecords : function(o, options, success){
14502         if(!o || success === false){
14503             if(success !== false){
14504                 this.fireEvent("load", this, [], options, o);
14505             }
14506             if(options.callback){
14507                 options.callback.call(options.scope || this, [], options, false);
14508             }
14509             return;
14510         }
14511         // if data returned failure - throw an exception.
14512         if (o.success === false) {
14513             // show a message if no listener is registered.
14514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
14515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
14516             }
14517             // loadmask wil be hooked into this..
14518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
14519             return;
14520         }
14521         var r = o.records, t = o.totalRecords || r.length;
14522         
14523         this.fireEvent("beforeloadadd", this, r, options, o);
14524         
14525         if(!options || options.add !== true){
14526             if(this.pruneModifiedRecords){
14527                 this.modified = [];
14528             }
14529             for(var i = 0, len = r.length; i < len; i++){
14530                 r[i].join(this);
14531             }
14532             if(this.snapshot){
14533                 this.data = this.snapshot;
14534                 delete this.snapshot;
14535             }
14536             this.data.clear();
14537             this.data.addAll(r);
14538             this.totalLength = t;
14539             this.applySort();
14540             this.fireEvent("datachanged", this);
14541         }else{
14542             this.totalLength = Math.max(t, this.data.length+r.length);
14543             this.add(r);
14544         }
14545         
14546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
14547                 
14548             var e = new Roo.data.Record({});
14549
14550             e.set(this.parent.displayField, this.parent.emptyTitle);
14551             e.set(this.parent.valueField, '');
14552
14553             this.insert(0, e);
14554         }
14555             
14556         this.fireEvent("load", this, r, options, o);
14557         if(options.callback){
14558             options.callback.call(options.scope || this, r, options, true);
14559         }
14560     },
14561
14562
14563     /**
14564      * Loads data from a passed data block. A Reader which understands the format of the data
14565      * must have been configured in the constructor.
14566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
14567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
14568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
14569      */
14570     loadData : function(o, append){
14571         var r = this.reader.readRecords(o);
14572         this.loadRecords(r, {add: append}, true);
14573     },
14574     
14575      /**
14576      * using 'cn' the nested child reader read the child array into it's child stores.
14577      * @param {Object} rec The record with a 'children array
14578      */
14579     loadDataFromChildren : function(rec)
14580     {
14581         this.loadData(this.reader.toLoadData(rec));
14582     },
14583     
14584
14585     /**
14586      * Gets the number of cached records.
14587      * <p>
14588      * <em>If using paging, this may not be the total size of the dataset. If the data object
14589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
14590      * the data set size</em>
14591      */
14592     getCount : function(){
14593         return this.data.length || 0;
14594     },
14595
14596     /**
14597      * Gets the total number of records in the dataset as returned by the server.
14598      * <p>
14599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
14600      * the dataset size</em>
14601      */
14602     getTotalCount : function(){
14603         return this.totalLength || 0;
14604     },
14605
14606     /**
14607      * Returns the sort state of the Store as an object with two properties:
14608      * <pre><code>
14609  field {String} The name of the field by which the Records are sorted
14610  direction {String} The sort order, "ASC" or "DESC"
14611      * </code></pre>
14612      */
14613     getSortState : function(){
14614         return this.sortInfo;
14615     },
14616
14617     // private
14618     applySort : function(){
14619         if(this.sortInfo && !this.remoteSort){
14620             var s = this.sortInfo, f = s.field;
14621             var st = this.fields.get(f).sortType;
14622             var fn = function(r1, r2){
14623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14625             };
14626             this.data.sort(s.direction, fn);
14627             if(this.snapshot && this.snapshot != this.data){
14628                 this.snapshot.sort(s.direction, fn);
14629             }
14630         }
14631     },
14632
14633     /**
14634      * Sets the default sort column and order to be used by the next load operation.
14635      * @param {String} fieldName The name of the field to sort by.
14636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14637      */
14638     setDefaultSort : function(field, dir){
14639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14640     },
14641
14642     /**
14643      * Sort the Records.
14644      * If remote sorting is used, the sort is performed on the server, and the cache is
14645      * reloaded. If local sorting is used, the cache is sorted internally.
14646      * @param {String} fieldName The name of the field to sort by.
14647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14648      */
14649     sort : function(fieldName, dir){
14650         var f = this.fields.get(fieldName);
14651         if(!dir){
14652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14653             
14654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14656             }else{
14657                 dir = f.sortDir;
14658             }
14659         }
14660         this.sortToggle[f.name] = dir;
14661         this.sortInfo = {field: f.name, direction: dir};
14662         if(!this.remoteSort){
14663             this.applySort();
14664             this.fireEvent("datachanged", this);
14665         }else{
14666             this.load(this.lastOptions);
14667         }
14668     },
14669
14670     /**
14671      * Calls the specified function for each of the Records in the cache.
14672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14673      * Returning <em>false</em> aborts and exits the iteration.
14674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14675      */
14676     each : function(fn, scope){
14677         this.data.each(fn, scope);
14678     },
14679
14680     /**
14681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14682      * (e.g., during paging).
14683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14684      */
14685     getModifiedRecords : function(){
14686         return this.modified;
14687     },
14688
14689     // private
14690     createFilterFn : function(property, value, anyMatch){
14691         if(!value.exec){ // not a regex
14692             value = String(value);
14693             if(value.length == 0){
14694                 return false;
14695             }
14696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14697         }
14698         return function(r){
14699             return value.test(r.data[property]);
14700         };
14701     },
14702
14703     /**
14704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14705      * @param {String} property A field on your records
14706      * @param {Number} start The record index to start at (defaults to 0)
14707      * @param {Number} end The last record index to include (defaults to length - 1)
14708      * @return {Number} The sum
14709      */
14710     sum : function(property, start, end){
14711         var rs = this.data.items, v = 0;
14712         start = start || 0;
14713         end = (end || end === 0) ? end : rs.length-1;
14714
14715         for(var i = start; i <= end; i++){
14716             v += (rs[i].data[property] || 0);
14717         }
14718         return v;
14719     },
14720
14721     /**
14722      * Filter the records by a specified property.
14723      * @param {String} field A field on your records
14724      * @param {String/RegExp} value Either a string that the field
14725      * should start with or a RegExp to test against the field
14726      * @param {Boolean} anyMatch True to match any part not just the beginning
14727      */
14728     filter : function(property, value, anyMatch){
14729         var fn = this.createFilterFn(property, value, anyMatch);
14730         return fn ? this.filterBy(fn) : this.clearFilter();
14731     },
14732
14733     /**
14734      * Filter by a function. The specified function will be called with each
14735      * record in this data source. If the function returns true the record is included,
14736      * otherwise it is filtered.
14737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14738      * @param {Object} scope (optional) The scope of the function (defaults to this)
14739      */
14740     filterBy : function(fn, scope){
14741         this.snapshot = this.snapshot || this.data;
14742         this.data = this.queryBy(fn, scope||this);
14743         this.fireEvent("datachanged", this);
14744     },
14745
14746     /**
14747      * Query the records by a specified property.
14748      * @param {String} field A field on your records
14749      * @param {String/RegExp} value Either a string that the field
14750      * should start with or a RegExp to test against the field
14751      * @param {Boolean} anyMatch True to match any part not just the beginning
14752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14753      */
14754     query : function(property, value, anyMatch){
14755         var fn = this.createFilterFn(property, value, anyMatch);
14756         return fn ? this.queryBy(fn) : this.data.clone();
14757     },
14758
14759     /**
14760      * Query by a function. The specified function will be called with each
14761      * record in this data source. If the function returns true the record is included
14762      * in the results.
14763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14764      * @param {Object} scope (optional) The scope of the function (defaults to this)
14765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14766      **/
14767     queryBy : function(fn, scope){
14768         var data = this.snapshot || this.data;
14769         return data.filterBy(fn, scope||this);
14770     },
14771
14772     /**
14773      * Collects unique values for a particular dataIndex from this store.
14774      * @param {String} dataIndex The property to collect
14775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14777      * @return {Array} An array of the unique values
14778      **/
14779     collect : function(dataIndex, allowNull, bypassFilter){
14780         var d = (bypassFilter === true && this.snapshot) ?
14781                 this.snapshot.items : this.data.items;
14782         var v, sv, r = [], l = {};
14783         for(var i = 0, len = d.length; i < len; i++){
14784             v = d[i].data[dataIndex];
14785             sv = String(v);
14786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14787                 l[sv] = true;
14788                 r[r.length] = v;
14789             }
14790         }
14791         return r;
14792     },
14793
14794     /**
14795      * Revert to a view of the Record cache with no filtering applied.
14796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14797      */
14798     clearFilter : function(suppressEvent){
14799         if(this.snapshot && this.snapshot != this.data){
14800             this.data = this.snapshot;
14801             delete this.snapshot;
14802             if(suppressEvent !== true){
14803                 this.fireEvent("datachanged", this);
14804             }
14805         }
14806     },
14807
14808     // private
14809     afterEdit : function(record){
14810         if(this.modified.indexOf(record) == -1){
14811             this.modified.push(record);
14812         }
14813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14814     },
14815     
14816     // private
14817     afterReject : function(record){
14818         this.modified.remove(record);
14819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14820     },
14821
14822     // private
14823     afterCommit : function(record){
14824         this.modified.remove(record);
14825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14826     },
14827
14828     /**
14829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14831      */
14832     commitChanges : function(){
14833         var m = this.modified.slice(0);
14834         this.modified = [];
14835         for(var i = 0, len = m.length; i < len; i++){
14836             m[i].commit();
14837         }
14838     },
14839
14840     /**
14841      * Cancel outstanding changes on all changed records.
14842      */
14843     rejectChanges : function(){
14844         var m = this.modified.slice(0);
14845         this.modified = [];
14846         for(var i = 0, len = m.length; i < len; i++){
14847             m[i].reject();
14848         }
14849     },
14850
14851     onMetaChange : function(meta, rtype, o){
14852         this.recordType = rtype;
14853         this.fields = rtype.prototype.fields;
14854         delete this.snapshot;
14855         this.sortInfo = meta.sortInfo || this.sortInfo;
14856         this.modified = [];
14857         this.fireEvent('metachange', this, this.reader.meta);
14858     },
14859     
14860     moveIndex : function(data, type)
14861     {
14862         var index = this.indexOf(data);
14863         
14864         var newIndex = index + type;
14865         
14866         this.remove(data);
14867         
14868         this.insert(newIndex, data);
14869         
14870     }
14871 });/*
14872  * Based on:
14873  * Ext JS Library 1.1.1
14874  * Copyright(c) 2006-2007, Ext JS, LLC.
14875  *
14876  * Originally Released Under LGPL - original licence link has changed is not relivant.
14877  *
14878  * Fork - LGPL
14879  * <script type="text/javascript">
14880  */
14881
14882 /**
14883  * @class Roo.data.SimpleStore
14884  * @extends Roo.data.Store
14885  * Small helper class to make creating Stores from Array data easier.
14886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14887  * @cfg {Array} fields An array of field definition objects, or field name strings.
14888  * @cfg {Object} an existing reader (eg. copied from another store)
14889  * @cfg {Array} data The multi-dimensional array of data
14890  * @constructor
14891  * @param {Object} config
14892  */
14893 Roo.data.SimpleStore = function(config)
14894 {
14895     Roo.data.SimpleStore.superclass.constructor.call(this, {
14896         isLocal : true,
14897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14898                 id: config.id
14899             },
14900             Roo.data.Record.create(config.fields)
14901         ),
14902         proxy : new Roo.data.MemoryProxy(config.data)
14903     });
14904     this.load();
14905 };
14906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14907  * Based on:
14908  * Ext JS Library 1.1.1
14909  * Copyright(c) 2006-2007, Ext JS, LLC.
14910  *
14911  * Originally Released Under LGPL - original licence link has changed is not relivant.
14912  *
14913  * Fork - LGPL
14914  * <script type="text/javascript">
14915  */
14916
14917 /**
14918 /**
14919  * @extends Roo.data.Store
14920  * @class Roo.data.JsonStore
14921  * Small helper class to make creating Stores for JSON data easier. <br/>
14922 <pre><code>
14923 var store = new Roo.data.JsonStore({
14924     url: 'get-images.php',
14925     root: 'images',
14926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14927 });
14928 </code></pre>
14929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14930  * JsonReader and HttpProxy (unless inline data is provided).</b>
14931  * @cfg {Array} fields An array of field definition objects, or field name strings.
14932  * @constructor
14933  * @param {Object} config
14934  */
14935 Roo.data.JsonStore = function(c){
14936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14938         reader: new Roo.data.JsonReader(c, c.fields)
14939     }));
14940 };
14941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14942  * Based on:
14943  * Ext JS Library 1.1.1
14944  * Copyright(c) 2006-2007, Ext JS, LLC.
14945  *
14946  * Originally Released Under LGPL - original licence link has changed is not relivant.
14947  *
14948  * Fork - LGPL
14949  * <script type="text/javascript">
14950  */
14951
14952  
14953 Roo.data.Field = function(config){
14954     if(typeof config == "string"){
14955         config = {name: config};
14956     }
14957     Roo.apply(this, config);
14958     
14959     if(!this.type){
14960         this.type = "auto";
14961     }
14962     
14963     var st = Roo.data.SortTypes;
14964     // named sortTypes are supported, here we look them up
14965     if(typeof this.sortType == "string"){
14966         this.sortType = st[this.sortType];
14967     }
14968     
14969     // set default sortType for strings and dates
14970     if(!this.sortType){
14971         switch(this.type){
14972             case "string":
14973                 this.sortType = st.asUCString;
14974                 break;
14975             case "date":
14976                 this.sortType = st.asDate;
14977                 break;
14978             default:
14979                 this.sortType = st.none;
14980         }
14981     }
14982
14983     // define once
14984     var stripRe = /[\$,%]/g;
14985
14986     // prebuilt conversion function for this field, instead of
14987     // switching every time we're reading a value
14988     if(!this.convert){
14989         var cv, dateFormat = this.dateFormat;
14990         switch(this.type){
14991             case "":
14992             case "auto":
14993             case undefined:
14994                 cv = function(v){ return v; };
14995                 break;
14996             case "string":
14997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14998                 break;
14999             case "int":
15000                 cv = function(v){
15001                     return v !== undefined && v !== null && v !== '' ?
15002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
15003                     };
15004                 break;
15005             case "float":
15006                 cv = function(v){
15007                     return v !== undefined && v !== null && v !== '' ?
15008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
15009                     };
15010                 break;
15011             case "bool":
15012             case "boolean":
15013                 cv = function(v){ return v === true || v === "true" || v == 1; };
15014                 break;
15015             case "date":
15016                 cv = function(v){
15017                     if(!v){
15018                         return '';
15019                     }
15020                     if(v instanceof Date){
15021                         return v;
15022                     }
15023                     if(dateFormat){
15024                         if(dateFormat == "timestamp"){
15025                             return new Date(v*1000);
15026                         }
15027                         return Date.parseDate(v, dateFormat);
15028                     }
15029                     var parsed = Date.parse(v);
15030                     return parsed ? new Date(parsed) : null;
15031                 };
15032              break;
15033             
15034         }
15035         this.convert = cv;
15036     }
15037 };
15038
15039 Roo.data.Field.prototype = {
15040     dateFormat: null,
15041     defaultValue: "",
15042     mapping: null,
15043     sortType : null,
15044     sortDir : "ASC"
15045 };/*
15046  * Based on:
15047  * Ext JS Library 1.1.1
15048  * Copyright(c) 2006-2007, Ext JS, LLC.
15049  *
15050  * Originally Released Under LGPL - original licence link has changed is not relivant.
15051  *
15052  * Fork - LGPL
15053  * <script type="text/javascript">
15054  */
15055  
15056 // Base class for reading structured data from a data source.  This class is intended to be
15057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
15058
15059 /**
15060  * @class Roo.data.DataReader
15061  * Base class for reading structured data from a data source.  This class is intended to be
15062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
15063  */
15064
15065 Roo.data.DataReader = function(meta, recordType){
15066     
15067     this.meta = meta;
15068     
15069     this.recordType = recordType instanceof Array ? 
15070         Roo.data.Record.create(recordType) : recordType;
15071 };
15072
15073 Roo.data.DataReader.prototype = {
15074     
15075     
15076     readerType : 'Data',
15077      /**
15078      * Create an empty record
15079      * @param {Object} data (optional) - overlay some values
15080      * @return {Roo.data.Record} record created.
15081      */
15082     newRow :  function(d) {
15083         var da =  {};
15084         this.recordType.prototype.fields.each(function(c) {
15085             switch( c.type) {
15086                 case 'int' : da[c.name] = 0; break;
15087                 case 'date' : da[c.name] = new Date(); break;
15088                 case 'float' : da[c.name] = 0.0; break;
15089                 case 'boolean' : da[c.name] = false; break;
15090                 default : da[c.name] = ""; break;
15091             }
15092             
15093         });
15094         return new this.recordType(Roo.apply(da, d));
15095     }
15096     
15097     
15098 };/*
15099  * Based on:
15100  * Ext JS Library 1.1.1
15101  * Copyright(c) 2006-2007, Ext JS, LLC.
15102  *
15103  * Originally Released Under LGPL - original licence link has changed is not relivant.
15104  *
15105  * Fork - LGPL
15106  * <script type="text/javascript">
15107  */
15108
15109 /**
15110  * @class Roo.data.DataProxy
15111  * @extends Roo.data.Observable
15112  * This class is an abstract base class for implementations which provide retrieval of
15113  * unformatted data objects.<br>
15114  * <p>
15115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
15116  * (of the appropriate type which knows how to parse the data object) to provide a block of
15117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
15118  * <p>
15119  * Custom implementations must implement the load method as described in
15120  * {@link Roo.data.HttpProxy#load}.
15121  */
15122 Roo.data.DataProxy = function(){
15123     this.addEvents({
15124         /**
15125          * @event beforeload
15126          * Fires before a network request is made to retrieve a data object.
15127          * @param {Object} This DataProxy object.
15128          * @param {Object} params The params parameter to the load function.
15129          */
15130         beforeload : true,
15131         /**
15132          * @event load
15133          * Fires before the load method's callback is called.
15134          * @param {Object} This DataProxy object.
15135          * @param {Object} o The data object.
15136          * @param {Object} arg The callback argument object passed to the load function.
15137          */
15138         load : true,
15139         /**
15140          * @event loadexception
15141          * Fires if an Exception occurs during data retrieval.
15142          * @param {Object} This DataProxy object.
15143          * @param {Object} o The data object.
15144          * @param {Object} arg The callback argument object passed to the load function.
15145          * @param {Object} e The Exception.
15146          */
15147         loadexception : true
15148     });
15149     Roo.data.DataProxy.superclass.constructor.call(this);
15150 };
15151
15152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
15153
15154     /**
15155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
15156      */
15157 /*
15158  * Based on:
15159  * Ext JS Library 1.1.1
15160  * Copyright(c) 2006-2007, Ext JS, LLC.
15161  *
15162  * Originally Released Under LGPL - original licence link has changed is not relivant.
15163  *
15164  * Fork - LGPL
15165  * <script type="text/javascript">
15166  */
15167 /**
15168  * @class Roo.data.MemoryProxy
15169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
15170  * to the Reader when its load method is called.
15171  * @constructor
15172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
15173  */
15174 Roo.data.MemoryProxy = function(data){
15175     if (data.data) {
15176         data = data.data;
15177     }
15178     Roo.data.MemoryProxy.superclass.constructor.call(this);
15179     this.data = data;
15180 };
15181
15182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
15183     
15184     /**
15185      * Load data from the requested source (in this case an in-memory
15186      * data object passed to the constructor), read the data object into
15187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15188      * process that block using the passed callback.
15189      * @param {Object} params This parameter is not used by the MemoryProxy class.
15190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15191      * object into a block of Roo.data.Records.
15192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
15193      * The function must be passed <ul>
15194      * <li>The Record block object</li>
15195      * <li>The "arg" argument from the load function</li>
15196      * <li>A boolean success indicator</li>
15197      * </ul>
15198      * @param {Object} scope The scope in which to call the callback
15199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15200      */
15201     load : function(params, reader, callback, scope, arg){
15202         params = params || {};
15203         var result;
15204         try {
15205             result = reader.readRecords(params.data ? params.data :this.data);
15206         }catch(e){
15207             this.fireEvent("loadexception", this, arg, null, e);
15208             callback.call(scope, null, arg, false);
15209             return;
15210         }
15211         callback.call(scope, result, arg, true);
15212     },
15213     
15214     // private
15215     update : function(params, records){
15216         
15217     }
15218 });/*
15219  * Based on:
15220  * Ext JS Library 1.1.1
15221  * Copyright(c) 2006-2007, Ext JS, LLC.
15222  *
15223  * Originally Released Under LGPL - original licence link has changed is not relivant.
15224  *
15225  * Fork - LGPL
15226  * <script type="text/javascript">
15227  */
15228 /**
15229  * @class Roo.data.HttpProxy
15230  * @extends Roo.data.DataProxy
15231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
15232  * configured to reference a certain URL.<br><br>
15233  * <p>
15234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
15235  * from which the running page was served.<br><br>
15236  * <p>
15237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
15238  * <p>
15239  * Be aware that to enable the browser to parse an XML document, the server must set
15240  * the Content-Type header in the HTTP response to "text/xml".
15241  * @constructor
15242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
15243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
15244  * will be used to make the request.
15245  */
15246 Roo.data.HttpProxy = function(conn){
15247     Roo.data.HttpProxy.superclass.constructor.call(this);
15248     // is conn a conn config or a real conn?
15249     this.conn = conn;
15250     this.useAjax = !conn || !conn.events;
15251   
15252 };
15253
15254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
15255     // thse are take from connection...
15256     
15257     /**
15258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
15259      */
15260     /**
15261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
15262      * extra parameters to each request made by this object. (defaults to undefined)
15263      */
15264     /**
15265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
15266      *  to each request made by this object. (defaults to undefined)
15267      */
15268     /**
15269      * @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)
15270      */
15271     /**
15272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
15273      */
15274      /**
15275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
15276      * @type Boolean
15277      */
15278   
15279
15280     /**
15281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
15282      * @type Boolean
15283      */
15284     /**
15285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
15286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
15287      * a finer-grained basis than the DataProxy events.
15288      */
15289     getConnection : function(){
15290         return this.useAjax ? Roo.Ajax : this.conn;
15291     },
15292
15293     /**
15294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
15295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
15296      * process that block using the passed callback.
15297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15298      * for the request to the remote server.
15299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15300      * object into a block of Roo.data.Records.
15301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15302      * The function must be passed <ul>
15303      * <li>The Record block object</li>
15304      * <li>The "arg" argument from the load function</li>
15305      * <li>A boolean success indicator</li>
15306      * </ul>
15307      * @param {Object} scope The scope in which to call the callback
15308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15309      */
15310     load : function(params, reader, callback, scope, arg){
15311         if(this.fireEvent("beforeload", this, params) !== false){
15312             var  o = {
15313                 params : params || {},
15314                 request: {
15315                     callback : callback,
15316                     scope : scope,
15317                     arg : arg
15318                 },
15319                 reader: reader,
15320                 callback : this.loadResponse,
15321                 scope: this
15322             };
15323             if(this.useAjax){
15324                 Roo.applyIf(o, this.conn);
15325                 if(this.activeRequest){
15326                     Roo.Ajax.abort(this.activeRequest);
15327                 }
15328                 this.activeRequest = Roo.Ajax.request(o);
15329             }else{
15330                 this.conn.request(o);
15331             }
15332         }else{
15333             callback.call(scope||this, null, arg, false);
15334         }
15335     },
15336
15337     // private
15338     loadResponse : function(o, success, response){
15339         delete this.activeRequest;
15340         if(!success){
15341             this.fireEvent("loadexception", this, o, response);
15342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15343             return;
15344         }
15345         var result;
15346         try {
15347             result = o.reader.read(response);
15348         }catch(e){
15349             this.fireEvent("loadexception", this, o, response, e);
15350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
15351             return;
15352         }
15353         
15354         this.fireEvent("load", this, o, o.request.arg);
15355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
15356     },
15357
15358     // private
15359     update : function(dataSet){
15360
15361     },
15362
15363     // private
15364     updateResponse : function(dataSet){
15365
15366     }
15367 });/*
15368  * Based on:
15369  * Ext JS Library 1.1.1
15370  * Copyright(c) 2006-2007, Ext JS, LLC.
15371  *
15372  * Originally Released Under LGPL - original licence link has changed is not relivant.
15373  *
15374  * Fork - LGPL
15375  * <script type="text/javascript">
15376  */
15377
15378 /**
15379  * @class Roo.data.ScriptTagProxy
15380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
15381  * other than the originating domain of the running page.<br><br>
15382  * <p>
15383  * <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
15384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
15385  * <p>
15386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
15387  * source code that is used as the source inside a &lt;script> tag.<br><br>
15388  * <p>
15389  * In order for the browser to process the returned data, the server must wrap the data object
15390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
15391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
15392  * depending on whether the callback name was passed:
15393  * <p>
15394  * <pre><code>
15395 boolean scriptTag = false;
15396 String cb = request.getParameter("callback");
15397 if (cb != null) {
15398     scriptTag = true;
15399     response.setContentType("text/javascript");
15400 } else {
15401     response.setContentType("application/x-json");
15402 }
15403 Writer out = response.getWriter();
15404 if (scriptTag) {
15405     out.write(cb + "(");
15406 }
15407 out.print(dataBlock.toJsonString());
15408 if (scriptTag) {
15409     out.write(");");
15410 }
15411 </pre></code>
15412  *
15413  * @constructor
15414  * @param {Object} config A configuration object.
15415  */
15416 Roo.data.ScriptTagProxy = function(config){
15417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
15418     Roo.apply(this, config);
15419     this.head = document.getElementsByTagName("head")[0];
15420 };
15421
15422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
15423
15424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
15425     /**
15426      * @cfg {String} url The URL from which to request the data object.
15427      */
15428     /**
15429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
15430      */
15431     timeout : 30000,
15432     /**
15433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
15434      * the server the name of the callback function set up by the load call to process the returned data object.
15435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
15436      * javascript output which calls this named function passing the data object as its only parameter.
15437      */
15438     callbackParam : "callback",
15439     /**
15440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
15441      * name to the request.
15442      */
15443     nocache : true,
15444
15445     /**
15446      * Load data from the configured URL, read the data object into
15447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
15448      * process that block using the passed callback.
15449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
15450      * for the request to the remote server.
15451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
15452      * object into a block of Roo.data.Records.
15453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
15454      * The function must be passed <ul>
15455      * <li>The Record block object</li>
15456      * <li>The "arg" argument from the load function</li>
15457      * <li>A boolean success indicator</li>
15458      * </ul>
15459      * @param {Object} scope The scope in which to call the callback
15460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
15461      */
15462     load : function(params, reader, callback, scope, arg){
15463         if(this.fireEvent("beforeload", this, params) !== false){
15464
15465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
15466
15467             var url = this.url;
15468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
15469             if(this.nocache){
15470                 url += "&_dc=" + (new Date().getTime());
15471             }
15472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
15473             var trans = {
15474                 id : transId,
15475                 cb : "stcCallback"+transId,
15476                 scriptId : "stcScript"+transId,
15477                 params : params,
15478                 arg : arg,
15479                 url : url,
15480                 callback : callback,
15481                 scope : scope,
15482                 reader : reader
15483             };
15484             var conn = this;
15485
15486             window[trans.cb] = function(o){
15487                 conn.handleResponse(o, trans);
15488             };
15489
15490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
15491
15492             if(this.autoAbort !== false){
15493                 this.abort();
15494             }
15495
15496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
15497
15498             var script = document.createElement("script");
15499             script.setAttribute("src", url);
15500             script.setAttribute("type", "text/javascript");
15501             script.setAttribute("id", trans.scriptId);
15502             this.head.appendChild(script);
15503
15504             this.trans = trans;
15505         }else{
15506             callback.call(scope||this, null, arg, false);
15507         }
15508     },
15509
15510     // private
15511     isLoading : function(){
15512         return this.trans ? true : false;
15513     },
15514
15515     /**
15516      * Abort the current server request.
15517      */
15518     abort : function(){
15519         if(this.isLoading()){
15520             this.destroyTrans(this.trans);
15521         }
15522     },
15523
15524     // private
15525     destroyTrans : function(trans, isLoaded){
15526         this.head.removeChild(document.getElementById(trans.scriptId));
15527         clearTimeout(trans.timeoutId);
15528         if(isLoaded){
15529             window[trans.cb] = undefined;
15530             try{
15531                 delete window[trans.cb];
15532             }catch(e){}
15533         }else{
15534             // if hasn't been loaded, wait for load to remove it to prevent script error
15535             window[trans.cb] = function(){
15536                 window[trans.cb] = undefined;
15537                 try{
15538                     delete window[trans.cb];
15539                 }catch(e){}
15540             };
15541         }
15542     },
15543
15544     // private
15545     handleResponse : function(o, trans){
15546         this.trans = false;
15547         this.destroyTrans(trans, true);
15548         var result;
15549         try {
15550             result = trans.reader.readRecords(o);
15551         }catch(e){
15552             this.fireEvent("loadexception", this, o, trans.arg, e);
15553             trans.callback.call(trans.scope||window, null, trans.arg, false);
15554             return;
15555         }
15556         this.fireEvent("load", this, o, trans.arg);
15557         trans.callback.call(trans.scope||window, result, trans.arg, true);
15558     },
15559
15560     // private
15561     handleFailure : function(trans){
15562         this.trans = false;
15563         this.destroyTrans(trans, false);
15564         this.fireEvent("loadexception", this, null, trans.arg);
15565         trans.callback.call(trans.scope||window, null, trans.arg, false);
15566     }
15567 });/*
15568  * Based on:
15569  * Ext JS Library 1.1.1
15570  * Copyright(c) 2006-2007, Ext JS, LLC.
15571  *
15572  * Originally Released Under LGPL - original licence link has changed is not relivant.
15573  *
15574  * Fork - LGPL
15575  * <script type="text/javascript">
15576  */
15577
15578 /**
15579  * @class Roo.data.JsonReader
15580  * @extends Roo.data.DataReader
15581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
15582  * based on mappings in a provided Roo.data.Record constructor.
15583  * 
15584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
15585  * in the reply previously. 
15586  * 
15587  * <p>
15588  * Example code:
15589  * <pre><code>
15590 var RecordDef = Roo.data.Record.create([
15591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
15592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
15593 ]);
15594 var myReader = new Roo.data.JsonReader({
15595     totalProperty: "results",    // The property which contains the total dataset size (optional)
15596     root: "rows",                // The property which contains an Array of row objects
15597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
15598 }, RecordDef);
15599 </code></pre>
15600  * <p>
15601  * This would consume a JSON file like this:
15602  * <pre><code>
15603 { 'results': 2, 'rows': [
15604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15606 }
15607 </code></pre>
15608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15610  * paged from the remote server.
15611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15612  * @cfg {String} root name of the property which contains the Array of row objects.
15613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15614  * @cfg {Array} fields Array of field definition objects
15615  * @constructor
15616  * Create a new JsonReader
15617  * @param {Object} meta Metadata configuration options
15618  * @param {Object} recordType Either an Array of field definition objects,
15619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15620  */
15621 Roo.data.JsonReader = function(meta, recordType){
15622     
15623     meta = meta || {};
15624     // set some defaults:
15625     Roo.applyIf(meta, {
15626         totalProperty: 'total',
15627         successProperty : 'success',
15628         root : 'data',
15629         id : 'id'
15630     });
15631     
15632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15633 };
15634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15635     
15636     readerType : 'Json',
15637     
15638     /**
15639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15640      * Used by Store query builder to append _requestMeta to params.
15641      * 
15642      */
15643     metaFromRemote : false,
15644     /**
15645      * This method is only used by a DataProxy which has retrieved data from a remote server.
15646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15647      * @return {Object} data A data block which is used by an Roo.data.Store object as
15648      * a cache of Roo.data.Records.
15649      */
15650     read : function(response){
15651         var json = response.responseText;
15652        
15653         var o = /* eval:var:o */ eval("("+json+")");
15654         if(!o) {
15655             throw {message: "JsonReader.read: Json object not found"};
15656         }
15657         
15658         if(o.metaData){
15659             
15660             delete this.ef;
15661             this.metaFromRemote = true;
15662             this.meta = o.metaData;
15663             this.recordType = Roo.data.Record.create(o.metaData.fields);
15664             this.onMetaChange(this.meta, this.recordType, o);
15665         }
15666         return this.readRecords(o);
15667     },
15668
15669     // private function a store will implement
15670     onMetaChange : function(meta, recordType, o){
15671
15672     },
15673
15674     /**
15675          * @ignore
15676          */
15677     simpleAccess: function(obj, subsc) {
15678         return obj[subsc];
15679     },
15680
15681         /**
15682          * @ignore
15683          */
15684     getJsonAccessor: function(){
15685         var re = /[\[\.]/;
15686         return function(expr) {
15687             try {
15688                 return(re.test(expr))
15689                     ? new Function("obj", "return obj." + expr)
15690                     : function(obj){
15691                         return obj[expr];
15692                     };
15693             } catch(e){}
15694             return Roo.emptyFn;
15695         };
15696     }(),
15697
15698     /**
15699      * Create a data block containing Roo.data.Records from an XML document.
15700      * @param {Object} o An object which contains an Array of row objects in the property specified
15701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15702      * which contains the total size of the dataset.
15703      * @return {Object} data A data block which is used by an Roo.data.Store object as
15704      * a cache of Roo.data.Records.
15705      */
15706     readRecords : function(o){
15707         /**
15708          * After any data loads, the raw JSON data is available for further custom processing.
15709          * @type Object
15710          */
15711         this.o = o;
15712         var s = this.meta, Record = this.recordType,
15713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15714
15715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15716         if (!this.ef) {
15717             if(s.totalProperty) {
15718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15719                 }
15720                 if(s.successProperty) {
15721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15722                 }
15723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15724                 if (s.id) {
15725                         var g = this.getJsonAccessor(s.id);
15726                         this.getId = function(rec) {
15727                                 var r = g(rec);  
15728                                 return (r === undefined || r === "") ? null : r;
15729                         };
15730                 } else {
15731                         this.getId = function(){return null;};
15732                 }
15733             this.ef = [];
15734             for(var jj = 0; jj < fl; jj++){
15735                 f = fi[jj];
15736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15737                 this.ef[jj] = this.getJsonAccessor(map);
15738             }
15739         }
15740
15741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15742         if(s.totalProperty){
15743             var vt = parseInt(this.getTotal(o), 10);
15744             if(!isNaN(vt)){
15745                 totalRecords = vt;
15746             }
15747         }
15748         if(s.successProperty){
15749             var vs = this.getSuccess(o);
15750             if(vs === false || vs === 'false'){
15751                 success = false;
15752             }
15753         }
15754         var records = [];
15755         for(var i = 0; i < c; i++){
15756                 var n = root[i];
15757             var values = {};
15758             var id = this.getId(n);
15759             for(var j = 0; j < fl; j++){
15760                 f = fi[j];
15761             var v = this.ef[j](n);
15762             if (!f.convert) {
15763                 Roo.log('missing convert for ' + f.name);
15764                 Roo.log(f);
15765                 continue;
15766             }
15767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15768             }
15769             var record = new Record(values, id);
15770             record.json = n;
15771             records[i] = record;
15772         }
15773         return {
15774             raw : o,
15775             success : success,
15776             records : records,
15777             totalRecords : totalRecords
15778         };
15779     },
15780     // used when loading children.. @see loadDataFromChildren
15781     toLoadData: function(rec)
15782     {
15783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15785         return { data : data, total : data.length };
15786         
15787     }
15788 });/*
15789  * Based on:
15790  * Ext JS Library 1.1.1
15791  * Copyright(c) 2006-2007, Ext JS, LLC.
15792  *
15793  * Originally Released Under LGPL - original licence link has changed is not relivant.
15794  *
15795  * Fork - LGPL
15796  * <script type="text/javascript">
15797  */
15798
15799 /**
15800  * @class Roo.data.ArrayReader
15801  * @extends Roo.data.DataReader
15802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15803  * Each element of that Array represents a row of data fields. The
15804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15806  * <p>
15807  * Example code:.
15808  * <pre><code>
15809 var RecordDef = Roo.data.Record.create([
15810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15812 ]);
15813 var myReader = new Roo.data.ArrayReader({
15814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15815 }, RecordDef);
15816 </code></pre>
15817  * <p>
15818  * This would consume an Array like this:
15819  * <pre><code>
15820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15821   </code></pre>
15822  
15823  * @constructor
15824  * Create a new JsonReader
15825  * @param {Object} meta Metadata configuration options.
15826  * @param {Object|Array} recordType Either an Array of field definition objects
15827  * 
15828  * @cfg {Array} fields Array of field definition objects
15829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15830  * as specified to {@link Roo.data.Record#create},
15831  * or an {@link Roo.data.Record} object
15832  *
15833  * 
15834  * created using {@link Roo.data.Record#create}.
15835  */
15836 Roo.data.ArrayReader = function(meta, recordType)
15837 {    
15838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15839 };
15840
15841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15842     
15843       /**
15844      * Create a data block containing Roo.data.Records from an XML document.
15845      * @param {Object} o An Array of row objects which represents the dataset.
15846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15847      * a cache of Roo.data.Records.
15848      */
15849     readRecords : function(o)
15850     {
15851         var sid = this.meta ? this.meta.id : null;
15852         var recordType = this.recordType, fields = recordType.prototype.fields;
15853         var records = [];
15854         var root = o;
15855         for(var i = 0; i < root.length; i++){
15856             var n = root[i];
15857             var values = {};
15858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15859             for(var j = 0, jlen = fields.length; j < jlen; j++){
15860                 var f = fields.items[j];
15861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15863                 v = f.convert(v);
15864                 values[f.name] = v;
15865             }
15866             var record = new recordType(values, id);
15867             record.json = n;
15868             records[records.length] = record;
15869         }
15870         return {
15871             records : records,
15872             totalRecords : records.length
15873         };
15874     },
15875     // used when loading children.. @see loadDataFromChildren
15876     toLoadData: function(rec)
15877     {
15878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15880         
15881     }
15882     
15883     
15884 });/*
15885  * - LGPL
15886  * * 
15887  */
15888
15889 /**
15890  * @class Roo.bootstrap.ComboBox
15891  * @extends Roo.bootstrap.TriggerField
15892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15893  * @cfg {Boolean} append (true|false) default false
15894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15899  * @cfg {Boolean} animate default true
15900  * @cfg {Boolean} emptyResultText only for touch device
15901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15902  * @cfg {String} emptyTitle default ''
15903  * @cfg {Number} width fixed with? experimental
15904  * @constructor
15905  * Create a new ComboBox.
15906  * @param {Object} config Configuration options
15907  */
15908 Roo.bootstrap.ComboBox = function(config){
15909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15910     this.addEvents({
15911         /**
15912          * @event expand
15913          * Fires when the dropdown list is expanded
15914         * @param {Roo.bootstrap.ComboBox} combo This combo box
15915         */
15916         'expand' : true,
15917         /**
15918          * @event collapse
15919          * Fires when the dropdown list is collapsed
15920         * @param {Roo.bootstrap.ComboBox} combo This combo box
15921         */
15922         'collapse' : true,
15923         /**
15924          * @event beforeselect
15925          * Fires before a list item is selected. Return false to cancel the selection.
15926         * @param {Roo.bootstrap.ComboBox} combo This combo box
15927         * @param {Roo.data.Record} record The data record returned from the underlying store
15928         * @param {Number} index The index of the selected item in the dropdown list
15929         */
15930         'beforeselect' : true,
15931         /**
15932          * @event select
15933          * Fires when a list item is selected
15934         * @param {Roo.bootstrap.ComboBox} combo This combo box
15935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15936         * @param {Number} index The index of the selected item in the dropdown list
15937         */
15938         'select' : true,
15939         /**
15940          * @event beforequery
15941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15942          * The event object passed has these properties:
15943         * @param {Roo.bootstrap.ComboBox} combo This combo box
15944         * @param {String} query The query
15945         * @param {Boolean} forceAll true to force "all" query
15946         * @param {Boolean} cancel true to cancel the query
15947         * @param {Object} e The query event object
15948         */
15949         'beforequery': true,
15950          /**
15951          * @event add
15952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15953         * @param {Roo.bootstrap.ComboBox} combo This combo box
15954         */
15955         'add' : true,
15956         /**
15957          * @event edit
15958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15959         * @param {Roo.bootstrap.ComboBox} combo This combo box
15960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15961         */
15962         'edit' : true,
15963         /**
15964          * @event remove
15965          * Fires when the remove value from the combobox array
15966         * @param {Roo.bootstrap.ComboBox} combo This combo box
15967         */
15968         'remove' : true,
15969         /**
15970          * @event afterremove
15971          * Fires when the remove value from the combobox array
15972         * @param {Roo.bootstrap.ComboBox} combo This combo box
15973         */
15974         'afterremove' : true,
15975         /**
15976          * @event specialfilter
15977          * Fires when specialfilter
15978             * @param {Roo.bootstrap.ComboBox} combo This combo box
15979             */
15980         'specialfilter' : true,
15981         /**
15982          * @event tick
15983          * Fires when tick the element
15984             * @param {Roo.bootstrap.ComboBox} combo This combo box
15985             */
15986         'tick' : true,
15987         /**
15988          * @event touchviewdisplay
15989          * Fires when touch view require special display (default is using displayField)
15990             * @param {Roo.bootstrap.ComboBox} combo This combo box
15991             * @param {Object} cfg set html .
15992             */
15993         'touchviewdisplay' : true
15994         
15995     });
15996     
15997     this.item = [];
15998     this.tickItems = [];
15999     
16000     this.selectedIndex = -1;
16001     if(this.mode == 'local'){
16002         if(config.queryDelay === undefined){
16003             this.queryDelay = 10;
16004         }
16005         if(config.minChars === undefined){
16006             this.minChars = 0;
16007         }
16008     }
16009 };
16010
16011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
16012      
16013     /**
16014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
16015      * rendering into an Roo.Editor, defaults to false)
16016      */
16017     /**
16018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
16019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
16020      */
16021     /**
16022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
16023      */
16024     /**
16025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
16026      * the dropdown list (defaults to undefined, with no header element)
16027      */
16028
16029      /**
16030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
16031      */
16032      
16033      /**
16034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
16035      */
16036     listWidth: undefined,
16037     /**
16038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
16039      * mode = 'remote' or 'text' if mode = 'local')
16040      */
16041     displayField: undefined,
16042     
16043     /**
16044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
16045      * mode = 'remote' or 'value' if mode = 'local'). 
16046      * Note: use of a valueField requires the user make a selection
16047      * in order for a value to be mapped.
16048      */
16049     valueField: undefined,
16050     /**
16051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
16052      */
16053     modalTitle : '',
16054     
16055     /**
16056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
16057      * field's data value (defaults to the underlying DOM element's name)
16058      */
16059     hiddenName: undefined,
16060     /**
16061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
16062      */
16063     listClass: '',
16064     /**
16065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
16066      */
16067     selectedClass: 'active',
16068     
16069     /**
16070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
16071      */
16072     shadow:'sides',
16073     /**
16074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
16075      * anchor positions (defaults to 'tl-bl')
16076      */
16077     listAlign: 'tl-bl?',
16078     /**
16079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
16080      */
16081     maxHeight: 300,
16082     /**
16083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
16084      * query specified by the allQuery config option (defaults to 'query')
16085      */
16086     triggerAction: 'query',
16087     /**
16088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
16089      * (defaults to 4, does not apply if editable = false)
16090      */
16091     minChars : 4,
16092     /**
16093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
16094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
16095      */
16096     typeAhead: false,
16097     /**
16098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
16099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
16100      */
16101     queryDelay: 500,
16102     /**
16103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
16104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
16105      */
16106     pageSize: 0,
16107     /**
16108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
16109      * when editable = true (defaults to false)
16110      */
16111     selectOnFocus:false,
16112     /**
16113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
16114      */
16115     queryParam: 'query',
16116     /**
16117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
16118      * when mode = 'remote' (defaults to 'Loading...')
16119      */
16120     loadingText: 'Loading...',
16121     /**
16122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
16123      */
16124     resizable: false,
16125     /**
16126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
16127      */
16128     handleHeight : 8,
16129     /**
16130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
16131      * traditional select (defaults to true)
16132      */
16133     editable: true,
16134     /**
16135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
16136      */
16137     allQuery: '',
16138     /**
16139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
16140      */
16141     mode: 'remote',
16142     /**
16143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
16144      * listWidth has a higher value)
16145      */
16146     minListWidth : 70,
16147     /**
16148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
16149      * allow the user to set arbitrary text into the field (defaults to false)
16150      */
16151     forceSelection:false,
16152     /**
16153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
16154      * if typeAhead = true (defaults to 250)
16155      */
16156     typeAheadDelay : 250,
16157     /**
16158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
16159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
16160      */
16161     valueNotFoundText : undefined,
16162     /**
16163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
16164      */
16165     blockFocus : false,
16166     
16167     /**
16168      * @cfg {Boolean} disableClear Disable showing of clear button.
16169      */
16170     disableClear : false,
16171     /**
16172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
16173      */
16174     alwaysQuery : false,
16175     
16176     /**
16177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
16178      */
16179     multiple : false,
16180     
16181     /**
16182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
16183      */
16184     invalidClass : "has-warning",
16185     
16186     /**
16187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
16188      */
16189     validClass : "has-success",
16190     
16191     /**
16192      * @cfg {Boolean} specialFilter (true|false) special filter default false
16193      */
16194     specialFilter : false,
16195     
16196     /**
16197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
16198      */
16199     mobileTouchView : true,
16200     
16201     /**
16202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
16203      */
16204     useNativeIOS : false,
16205     
16206     /**
16207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
16208      */
16209     mobile_restrict_height : false,
16210     
16211     ios_options : false,
16212     
16213     //private
16214     addicon : false,
16215     editicon: false,
16216     
16217     page: 0,
16218     hasQuery: false,
16219     append: false,
16220     loadNext: false,
16221     autoFocus : true,
16222     tickable : false,
16223     btnPosition : 'right',
16224     triggerList : true,
16225     showToggleBtn : true,
16226     animate : true,
16227     emptyResultText: 'Empty',
16228     triggerText : 'Select',
16229     emptyTitle : '',
16230     width : false,
16231     
16232     // element that contains real text value.. (when hidden is used..)
16233     
16234     getAutoCreate : function()
16235     {   
16236         var cfg = false;
16237         //render
16238         /*
16239          * Render classic select for iso
16240          */
16241         
16242         if(Roo.isIOS && this.useNativeIOS){
16243             cfg = this.getAutoCreateNativeIOS();
16244             return cfg;
16245         }
16246         
16247         /*
16248          * Touch Devices
16249          */
16250         
16251         if(Roo.isTouch && this.mobileTouchView){
16252             cfg = this.getAutoCreateTouchView();
16253             return cfg;;
16254         }
16255         
16256         /*
16257          *  Normal ComboBox
16258          */
16259         if(!this.tickable){
16260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
16261             return cfg;
16262         }
16263         
16264         /*
16265          *  ComboBox with tickable selections
16266          */
16267              
16268         var align = this.labelAlign || this.parentLabelAlign();
16269         
16270         cfg = {
16271             cls : 'form-group roo-combobox-tickable' //input-group
16272         };
16273         
16274         var btn_text_select = '';
16275         var btn_text_done = '';
16276         var btn_text_cancel = '';
16277         
16278         if (this.btn_text_show) {
16279             btn_text_select = 'Select';
16280             btn_text_done = 'Done';
16281             btn_text_cancel = 'Cancel'; 
16282         }
16283         
16284         var buttons = {
16285             tag : 'div',
16286             cls : 'tickable-buttons',
16287             cn : [
16288                 {
16289                     tag : 'button',
16290                     type : 'button',
16291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
16292                     //html : this.triggerText
16293                     html: btn_text_select
16294                 },
16295                 {
16296                     tag : 'button',
16297                     type : 'button',
16298                     name : 'ok',
16299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
16300                     //html : 'Done'
16301                     html: btn_text_done
16302                 },
16303                 {
16304                     tag : 'button',
16305                     type : 'button',
16306                     name : 'cancel',
16307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
16308                     //html : 'Cancel'
16309                     html: btn_text_cancel
16310                 }
16311             ]
16312         };
16313         
16314         if(this.editable){
16315             buttons.cn.unshift({
16316                 tag: 'input',
16317                 cls: 'roo-select2-search-field-input'
16318             });
16319         }
16320         
16321         var _this = this;
16322         
16323         Roo.each(buttons.cn, function(c){
16324             if (_this.size) {
16325                 c.cls += ' btn-' + _this.size;
16326             }
16327
16328             if (_this.disabled) {
16329                 c.disabled = true;
16330             }
16331         });
16332         
16333         var box = {
16334             tag: 'div',
16335             style : 'display: contents',
16336             cn: [
16337                 {
16338                     tag: 'input',
16339                     type : 'hidden',
16340                     cls: 'form-hidden-field'
16341                 },
16342                 {
16343                     tag: 'ul',
16344                     cls: 'roo-select2-choices',
16345                     cn:[
16346                         {
16347                             tag: 'li',
16348                             cls: 'roo-select2-search-field',
16349                             cn: [
16350                                 buttons
16351                             ]
16352                         }
16353                     ]
16354                 }
16355             ]
16356         };
16357         
16358         var combobox = {
16359             cls: 'roo-select2-container input-group roo-select2-container-multi',
16360             cn: [
16361                 
16362                 box
16363 //                {
16364 //                    tag: 'ul',
16365 //                    cls: 'typeahead typeahead-long dropdown-menu',
16366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
16367 //                }
16368             ]
16369         };
16370         
16371         if(this.hasFeedback && !this.allowBlank){
16372             
16373             var feedback = {
16374                 tag: 'span',
16375                 cls: 'glyphicon form-control-feedback'
16376             };
16377
16378             combobox.cn.push(feedback);
16379         }
16380         
16381         
16382         
16383         var indicator = {
16384             tag : 'i',
16385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
16386             tooltip : 'This field is required'
16387         };
16388         if (Roo.bootstrap.version == 4) {
16389             indicator = {
16390                 tag : 'i',
16391                 style : 'display:none'
16392             };
16393         }
16394         if (align ==='left' && this.fieldLabel.length) {
16395             
16396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
16397             
16398             cfg.cn = [
16399                 indicator,
16400                 {
16401                     tag: 'label',
16402                     'for' :  id,
16403                     cls : 'control-label col-form-label',
16404                     html : this.fieldLabel
16405
16406                 },
16407                 {
16408                     cls : "", 
16409                     cn: [
16410                         combobox
16411                     ]
16412                 }
16413
16414             ];
16415             
16416             var labelCfg = cfg.cn[1];
16417             var contentCfg = cfg.cn[2];
16418             
16419
16420             if(this.indicatorpos == 'right'){
16421                 
16422                 cfg.cn = [
16423                     {
16424                         tag: 'label',
16425                         'for' :  id,
16426                         cls : 'control-label col-form-label',
16427                         cn : [
16428                             {
16429                                 tag : 'span',
16430                                 html : this.fieldLabel
16431                             },
16432                             indicator
16433                         ]
16434                     },
16435                     {
16436                         cls : "",
16437                         cn: [
16438                             combobox
16439                         ]
16440                     }
16441
16442                 ];
16443                 
16444                 
16445                 
16446                 labelCfg = cfg.cn[0];
16447                 contentCfg = cfg.cn[1];
16448             
16449             }
16450             
16451             if(this.labelWidth > 12){
16452                 labelCfg.style = "width: " + this.labelWidth + 'px';
16453             }
16454             if(this.width * 1 > 0){
16455                 contentCfg.style = "width: " + this.width + 'px';
16456             }
16457             if(this.labelWidth < 13 && this.labelmd == 0){
16458                 this.labelmd = this.labelWidth;
16459             }
16460             
16461             if(this.labellg > 0){
16462                 labelCfg.cls += ' col-lg-' + this.labellg;
16463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
16464             }
16465             
16466             if(this.labelmd > 0){
16467                 labelCfg.cls += ' col-md-' + this.labelmd;
16468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
16469             }
16470             
16471             if(this.labelsm > 0){
16472                 labelCfg.cls += ' col-sm-' + this.labelsm;
16473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
16474             }
16475             
16476             if(this.labelxs > 0){
16477                 labelCfg.cls += ' col-xs-' + this.labelxs;
16478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
16479             }
16480                 
16481                 
16482         } else if ( this.fieldLabel.length) {
16483 //                Roo.log(" label");
16484                  cfg.cn = [
16485                    indicator,
16486                     {
16487                         tag: 'label',
16488                         //cls : 'input-group-addon',
16489                         html : this.fieldLabel
16490                     },
16491                     combobox
16492                 ];
16493                 
16494                 if(this.indicatorpos == 'right'){
16495                     cfg.cn = [
16496                         {
16497                             tag: 'label',
16498                             //cls : 'input-group-addon',
16499                             html : this.fieldLabel
16500                         },
16501                         indicator,
16502                         combobox
16503                     ];
16504                     
16505                 }
16506
16507         } else {
16508             
16509 //                Roo.log(" no label && no align");
16510                 cfg = combobox
16511                      
16512                 
16513         }
16514          
16515         var settings=this;
16516         ['xs','sm','md','lg'].map(function(size){
16517             if (settings[size]) {
16518                 cfg.cls += ' col-' + size + '-' + settings[size];
16519             }
16520         });
16521         
16522         return cfg;
16523         
16524     },
16525     
16526     _initEventsCalled : false,
16527     
16528     // private
16529     initEvents: function()
16530     {   
16531         if (this._initEventsCalled) { // as we call render... prevent looping...
16532             return;
16533         }
16534         this._initEventsCalled = true;
16535         
16536         if (!this.store) {
16537             throw "can not find store for combo";
16538         }
16539         
16540         this.indicator = this.indicatorEl();
16541         
16542         this.store = Roo.factory(this.store, Roo.data);
16543         this.store.parent = this;
16544         
16545         // if we are building from html. then this element is so complex, that we can not really
16546         // use the rendered HTML.
16547         // so we have to trash and replace the previous code.
16548         if (Roo.XComponent.build_from_html) {
16549             // remove this element....
16550             var e = this.el.dom, k=0;
16551             while (e ) { e = e.previousSibling;  ++k;}
16552
16553             this.el.remove();
16554             
16555             this.el=false;
16556             this.rendered = false;
16557             
16558             this.render(this.parent().getChildContainer(true), k);
16559         }
16560         
16561         if(Roo.isIOS && this.useNativeIOS){
16562             this.initIOSView();
16563             return;
16564         }
16565         
16566         /*
16567          * Touch Devices
16568          */
16569         
16570         if(Roo.isTouch && this.mobileTouchView){
16571             this.initTouchView();
16572             return;
16573         }
16574         
16575         if(this.tickable){
16576             this.initTickableEvents();
16577             return;
16578         }
16579         
16580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
16581         
16582         if(this.hiddenName){
16583             
16584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16585             
16586             this.hiddenField.dom.value =
16587                 this.hiddenValue !== undefined ? this.hiddenValue :
16588                 this.value !== undefined ? this.value : '';
16589
16590             // prevent input submission
16591             this.el.dom.removeAttribute('name');
16592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16593              
16594              
16595         }
16596         //if(Roo.isGecko){
16597         //    this.el.dom.setAttribute('autocomplete', 'off');
16598         //}
16599         
16600         var cls = 'x-combo-list';
16601         
16602         //this.list = new Roo.Layer({
16603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16604         //});
16605         
16606         var _this = this;
16607         
16608         (function(){
16609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16610             _this.list.setWidth(lw);
16611         }).defer(100);
16612         
16613         this.list.on('mouseover', this.onViewOver, this);
16614         this.list.on('mousemove', this.onViewMove, this);
16615         this.list.on('scroll', this.onViewScroll, this);
16616         
16617         /*
16618         this.list.swallowEvent('mousewheel');
16619         this.assetHeight = 0;
16620
16621         if(this.title){
16622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16623             this.assetHeight += this.header.getHeight();
16624         }
16625
16626         this.innerList = this.list.createChild({cls:cls+'-inner'});
16627         this.innerList.on('mouseover', this.onViewOver, this);
16628         this.innerList.on('mousemove', this.onViewMove, this);
16629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16630         
16631         if(this.allowBlank && !this.pageSize && !this.disableClear){
16632             this.footer = this.list.createChild({cls:cls+'-ft'});
16633             this.pageTb = new Roo.Toolbar(this.footer);
16634            
16635         }
16636         if(this.pageSize){
16637             this.footer = this.list.createChild({cls:cls+'-ft'});
16638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16639                     {pageSize: this.pageSize});
16640             
16641         }
16642         
16643         if (this.pageTb && this.allowBlank && !this.disableClear) {
16644             var _this = this;
16645             this.pageTb.add(new Roo.Toolbar.Fill(), {
16646                 cls: 'x-btn-icon x-btn-clear',
16647                 text: '&#160;',
16648                 handler: function()
16649                 {
16650                     _this.collapse();
16651                     _this.clearValue();
16652                     _this.onSelect(false, -1);
16653                 }
16654             });
16655         }
16656         if (this.footer) {
16657             this.assetHeight += this.footer.getHeight();
16658         }
16659         */
16660             
16661         if(!this.tpl){
16662             this.tpl = Roo.bootstrap.version == 4 ?
16663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16665         }
16666
16667         this.view = new Roo.View(this.list, this.tpl, {
16668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16669         });
16670         //this.view.wrapEl.setDisplayed(false);
16671         this.view.on('click', this.onViewClick, this);
16672         
16673         
16674         this.store.on('beforeload', this.onBeforeLoad, this);
16675         this.store.on('load', this.onLoad, this);
16676         this.store.on('loadexception', this.onLoadException, this);
16677         /*
16678         if(this.resizable){
16679             this.resizer = new Roo.Resizable(this.list,  {
16680                pinned:true, handles:'se'
16681             });
16682             this.resizer.on('resize', function(r, w, h){
16683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16684                 this.listWidth = w;
16685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16686                 this.restrictHeight();
16687             }, this);
16688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16689         }
16690         */
16691         if(!this.editable){
16692             this.editable = true;
16693             this.setEditable(false);
16694         }
16695         
16696         /*
16697         
16698         if (typeof(this.events.add.listeners) != 'undefined') {
16699             
16700             this.addicon = this.wrap.createChild(
16701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16702        
16703             this.addicon.on('click', function(e) {
16704                 this.fireEvent('add', this);
16705             }, this);
16706         }
16707         if (typeof(this.events.edit.listeners) != 'undefined') {
16708             
16709             this.editicon = this.wrap.createChild(
16710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16711             if (this.addicon) {
16712                 this.editicon.setStyle('margin-left', '40px');
16713             }
16714             this.editicon.on('click', function(e) {
16715                 
16716                 // we fire even  if inothing is selected..
16717                 this.fireEvent('edit', this, this.lastData );
16718                 
16719             }, this);
16720         }
16721         */
16722         
16723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16724             "up" : function(e){
16725                 this.inKeyMode = true;
16726                 this.selectPrev();
16727             },
16728
16729             "down" : function(e){
16730                 if(!this.isExpanded()){
16731                     this.onTriggerClick();
16732                 }else{
16733                     this.inKeyMode = true;
16734                     this.selectNext();
16735                 }
16736             },
16737
16738             "enter" : function(e){
16739 //                this.onViewClick();
16740                 //return true;
16741                 this.collapse();
16742                 
16743                 if(this.fireEvent("specialkey", this, e)){
16744                     this.onViewClick(false);
16745                 }
16746                 
16747                 return true;
16748             },
16749
16750             "esc" : function(e){
16751                 this.collapse();
16752             },
16753
16754             "tab" : function(e){
16755                 this.collapse();
16756                 
16757                 if(this.fireEvent("specialkey", this, e)){
16758                     this.onViewClick(false);
16759                 }
16760                 
16761                 return true;
16762             },
16763
16764             scope : this,
16765
16766             doRelay : function(foo, bar, hname){
16767                 if(hname == 'down' || this.scope.isExpanded()){
16768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16769                 }
16770                 return true;
16771             },
16772
16773             forceKeyDown: true
16774         });
16775         
16776         
16777         this.queryDelay = Math.max(this.queryDelay || 10,
16778                 this.mode == 'local' ? 10 : 250);
16779         
16780         
16781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16782         
16783         if(this.typeAhead){
16784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16785         }
16786         if(this.editable !== false){
16787             this.inputEl().on("keyup", this.onKeyUp, this);
16788         }
16789         if(this.forceSelection){
16790             this.inputEl().on('blur', this.doForce, this);
16791         }
16792         
16793         if(this.multiple){
16794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16796         }
16797     },
16798     
16799     initTickableEvents: function()
16800     {   
16801         this.createList();
16802         
16803         if(this.hiddenName){
16804             
16805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16806             
16807             this.hiddenField.dom.value =
16808                 this.hiddenValue !== undefined ? this.hiddenValue :
16809                 this.value !== undefined ? this.value : '';
16810
16811             // prevent input submission
16812             this.el.dom.removeAttribute('name');
16813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16814              
16815              
16816         }
16817         
16818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16819         
16820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16822         if(this.triggerList){
16823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16824         }
16825          
16826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16828         
16829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16831         
16832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16834         
16835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16838         
16839         this.okBtn.hide();
16840         this.cancelBtn.hide();
16841         
16842         var _this = this;
16843         
16844         (function(){
16845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16846             _this.list.setWidth(lw);
16847         }).defer(100);
16848         
16849         this.list.on('mouseover', this.onViewOver, this);
16850         this.list.on('mousemove', this.onViewMove, this);
16851         
16852         this.list.on('scroll', this.onViewScroll, this);
16853         
16854         if(!this.tpl){
16855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16857         }
16858
16859         this.view = new Roo.View(this.list, this.tpl, {
16860             singleSelect:true,
16861             tickable:true,
16862             parent:this,
16863             store: this.store,
16864             selectedClass: this.selectedClass
16865         });
16866         
16867         //this.view.wrapEl.setDisplayed(false);
16868         this.view.on('click', this.onViewClick, this);
16869         
16870         
16871         
16872         this.store.on('beforeload', this.onBeforeLoad, this);
16873         this.store.on('load', this.onLoad, this);
16874         this.store.on('loadexception', this.onLoadException, this);
16875         
16876         if(this.editable){
16877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16878                 "up" : function(e){
16879                     this.inKeyMode = true;
16880                     this.selectPrev();
16881                 },
16882
16883                 "down" : function(e){
16884                     this.inKeyMode = true;
16885                     this.selectNext();
16886                 },
16887
16888                 "enter" : function(e){
16889                     if(this.fireEvent("specialkey", this, e)){
16890                         this.onViewClick(false);
16891                     }
16892                     
16893                     return true;
16894                 },
16895
16896                 "esc" : function(e){
16897                     this.onTickableFooterButtonClick(e, false, false);
16898                 },
16899
16900                 "tab" : function(e){
16901                     this.fireEvent("specialkey", this, e);
16902                     
16903                     this.onTickableFooterButtonClick(e, false, false);
16904                     
16905                     return true;
16906                 },
16907
16908                 scope : this,
16909
16910                 doRelay : function(e, fn, key){
16911                     if(this.scope.isExpanded()){
16912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16913                     }
16914                     return true;
16915                 },
16916
16917                 forceKeyDown: true
16918             });
16919         }
16920         
16921         this.queryDelay = Math.max(this.queryDelay || 10,
16922                 this.mode == 'local' ? 10 : 250);
16923         
16924         
16925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16926         
16927         if(this.typeAhead){
16928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16929         }
16930         
16931         if(this.editable !== false){
16932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16933         }
16934         
16935         this.indicator = this.indicatorEl();
16936         
16937         if(this.indicator){
16938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16939             this.indicator.hide();
16940         }
16941         
16942     },
16943
16944     onDestroy : function(){
16945         if(this.view){
16946             this.view.setStore(null);
16947             this.view.el.removeAllListeners();
16948             this.view.el.remove();
16949             this.view.purgeListeners();
16950         }
16951         if(this.list){
16952             this.list.dom.innerHTML  = '';
16953         }
16954         
16955         if(this.store){
16956             this.store.un('beforeload', this.onBeforeLoad, this);
16957             this.store.un('load', this.onLoad, this);
16958             this.store.un('loadexception', this.onLoadException, this);
16959         }
16960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16961     },
16962
16963     // private
16964     fireKey : function(e){
16965         if(e.isNavKeyPress() && !this.list.isVisible()){
16966             this.fireEvent("specialkey", this, e);
16967         }
16968     },
16969
16970     // private
16971     onResize: function(w, h)
16972     {
16973         
16974         
16975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16976 //        
16977 //        if(typeof w != 'number'){
16978 //            // we do not handle it!?!?
16979 //            return;
16980 //        }
16981 //        var tw = this.trigger.getWidth();
16982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16984 //        var x = w - tw;
16985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16986 //            
16987 //        //this.trigger.setStyle('left', x+'px');
16988 //        
16989 //        if(this.list && this.listWidth === undefined){
16990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16991 //            this.list.setWidth(lw);
16992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16993 //        }
16994         
16995     
16996         
16997     },
16998
16999     /**
17000      * Allow or prevent the user from directly editing the field text.  If false is passed,
17001      * the user will only be able to select from the items defined in the dropdown list.  This method
17002      * is the runtime equivalent of setting the 'editable' config option at config time.
17003      * @param {Boolean} value True to allow the user to directly edit the field text
17004      */
17005     setEditable : function(value){
17006         if(value == this.editable){
17007             return;
17008         }
17009         this.editable = value;
17010         if(!value){
17011             this.inputEl().dom.setAttribute('readOnly', true);
17012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
17013             this.inputEl().addClass('x-combo-noedit');
17014         }else{
17015             this.inputEl().dom.removeAttribute('readOnly');
17016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
17017             this.inputEl().removeClass('x-combo-noedit');
17018         }
17019     },
17020
17021     // private
17022     
17023     onBeforeLoad : function(combo,opts){
17024         if(!this.hasFocus){
17025             return;
17026         }
17027          if (!opts.add) {
17028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
17029          }
17030         this.restrictHeight();
17031         this.selectedIndex = -1;
17032     },
17033
17034     // private
17035     onLoad : function(){
17036         
17037         this.hasQuery = false;
17038         
17039         if(!this.hasFocus){
17040             return;
17041         }
17042         
17043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17044             this.loading.hide();
17045         }
17046         
17047         if(this.store.getCount() > 0){
17048             
17049             this.expand();
17050             this.restrictHeight();
17051             if(this.lastQuery == this.allQuery){
17052                 if(this.editable && !this.tickable){
17053                     this.inputEl().dom.select();
17054                 }
17055                 
17056                 if(
17057                     !this.selectByValue(this.value, true) &&
17058                     this.autoFocus && 
17059                     (
17060                         !this.store.lastOptions ||
17061                         typeof(this.store.lastOptions.add) == 'undefined' || 
17062                         this.store.lastOptions.add != true
17063                     )
17064                 ){
17065                     this.select(0, true);
17066                 }
17067             }else{
17068                 if(this.autoFocus){
17069                     this.selectNext();
17070                 }
17071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
17072                     this.taTask.delay(this.typeAheadDelay);
17073                 }
17074             }
17075         }else{
17076             this.onEmptyResults();
17077         }
17078         
17079         //this.el.focus();
17080     },
17081     // private
17082     onLoadException : function()
17083     {
17084         this.hasQuery = false;
17085         
17086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
17087             this.loading.hide();
17088         }
17089         
17090         if(this.tickable && this.editable){
17091             return;
17092         }
17093         
17094         this.collapse();
17095         // only causes errors at present
17096         //Roo.log(this.store.reader.jsonData);
17097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
17098             // fixme
17099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
17100         //}
17101         
17102         
17103     },
17104     // private
17105     onTypeAhead : function(){
17106         if(this.store.getCount() > 0){
17107             var r = this.store.getAt(0);
17108             var newValue = r.data[this.displayField];
17109             var len = newValue.length;
17110             var selStart = this.getRawValue().length;
17111             
17112             if(selStart != len){
17113                 this.setRawValue(newValue);
17114                 this.selectText(selStart, newValue.length);
17115             }
17116         }
17117     },
17118
17119     // private
17120     onSelect : function(record, index){
17121         
17122         if(this.fireEvent('beforeselect', this, record, index) !== false){
17123         
17124             this.setFromData(index > -1 ? record.data : false);
17125             
17126             this.collapse();
17127             this.fireEvent('select', this, record, index);
17128         }
17129     },
17130
17131     /**
17132      * Returns the currently selected field value or empty string if no value is set.
17133      * @return {String} value The selected value
17134      */
17135     getValue : function()
17136     {
17137         if(Roo.isIOS && this.useNativeIOS){
17138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
17139         }
17140         
17141         if(this.multiple){
17142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
17143         }
17144         
17145         if(this.valueField){
17146             return typeof this.value != 'undefined' ? this.value : '';
17147         }else{
17148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
17149         }
17150     },
17151     
17152     getRawValue : function()
17153     {
17154         if(Roo.isIOS && this.useNativeIOS){
17155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
17156         }
17157         
17158         var v = this.inputEl().getValue();
17159         
17160         return v;
17161     },
17162
17163     /**
17164      * Clears any text/value currently set in the field
17165      */
17166     clearValue : function(){
17167         
17168         if(this.hiddenField){
17169             this.hiddenField.dom.value = '';
17170         }
17171         this.value = '';
17172         this.setRawValue('');
17173         this.lastSelectionText = '';
17174         this.lastData = false;
17175         
17176         var close = this.closeTriggerEl();
17177         
17178         if(close){
17179             close.hide();
17180         }
17181         
17182         this.validate();
17183         
17184     },
17185
17186     /**
17187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
17188      * will be displayed in the field.  If the value does not match the data value of an existing item,
17189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
17190      * Otherwise the field will be blank (although the value will still be set).
17191      * @param {String} value The value to match
17192      */
17193     setValue : function(v)
17194     {
17195         if(Roo.isIOS && this.useNativeIOS){
17196             this.setIOSValue(v);
17197             return;
17198         }
17199         
17200         if(this.multiple){
17201             this.syncValue();
17202             return;
17203         }
17204         
17205         var text = v;
17206         if(this.valueField){
17207             var r = this.findRecord(this.valueField, v);
17208             if(r){
17209                 text = r.data[this.displayField];
17210             }else if(this.valueNotFoundText !== undefined){
17211                 text = this.valueNotFoundText;
17212             }
17213         }
17214         this.lastSelectionText = text;
17215         if(this.hiddenField){
17216             this.hiddenField.dom.value = v;
17217         }
17218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
17219         this.value = v;
17220         
17221         var close = this.closeTriggerEl();
17222         
17223         if(close){
17224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
17225         }
17226         
17227         this.validate();
17228     },
17229     /**
17230      * @property {Object} the last set data for the element
17231      */
17232     
17233     lastData : false,
17234     /**
17235      * Sets the value of the field based on a object which is related to the record format for the store.
17236      * @param {Object} value the value to set as. or false on reset?
17237      */
17238     setFromData : function(o){
17239         
17240         if(this.multiple){
17241             this.addItem(o);
17242             return;
17243         }
17244             
17245         var dv = ''; // display value
17246         var vv = ''; // value value..
17247         this.lastData = o;
17248         if (this.displayField) {
17249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17250         } else {
17251             // this is an error condition!!!
17252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17253         }
17254         
17255         if(this.valueField){
17256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
17257         }
17258         
17259         var close = this.closeTriggerEl();
17260         
17261         if(close){
17262             if(dv.length || vv * 1 > 0){
17263                 close.show() ;
17264                 this.blockFocus=true;
17265             } else {
17266                 close.hide();
17267             }             
17268         }
17269         
17270         if(this.hiddenField){
17271             this.hiddenField.dom.value = vv;
17272             
17273             this.lastSelectionText = dv;
17274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17275             this.value = vv;
17276             return;
17277         }
17278         // no hidden field.. - we store the value in 'value', but still display
17279         // display field!!!!
17280         this.lastSelectionText = dv;
17281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
17282         this.value = vv;
17283         
17284         
17285         
17286     },
17287     // private
17288     reset : function(){
17289         // overridden so that last data is reset..
17290         
17291         if(this.multiple){
17292             this.clearItem();
17293             return;
17294         }
17295         
17296         this.setValue(this.originalValue);
17297         //this.clearInvalid();
17298         this.lastData = false;
17299         if (this.view) {
17300             this.view.clearSelections();
17301         }
17302         
17303         this.validate();
17304     },
17305     // private
17306     findRecord : function(prop, value){
17307         var record;
17308         if(this.store.getCount() > 0){
17309             this.store.each(function(r){
17310                 if(r.data[prop] == value){
17311                     record = r;
17312                     return false;
17313                 }
17314                 return true;
17315             });
17316         }
17317         return record;
17318     },
17319     
17320     getName: function()
17321     {
17322         // returns hidden if it's set..
17323         if (!this.rendered) {return ''};
17324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
17325         
17326     },
17327     // private
17328     onViewMove : function(e, t){
17329         this.inKeyMode = false;
17330     },
17331
17332     // private
17333     onViewOver : function(e, t){
17334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
17335             return;
17336         }
17337         var item = this.view.findItemFromChild(t);
17338         
17339         if(item){
17340             var index = this.view.indexOf(item);
17341             this.select(index, false);
17342         }
17343     },
17344
17345     // private
17346     onViewClick : function(view, doFocus, el, e)
17347     {
17348         var index = this.view.getSelectedIndexes()[0];
17349         
17350         var r = this.store.getAt(index);
17351         
17352         if(this.tickable){
17353             
17354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
17355                 return;
17356             }
17357             
17358             var rm = false;
17359             var _this = this;
17360             
17361             Roo.each(this.tickItems, function(v,k){
17362                 
17363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
17364                     Roo.log(v);
17365                     _this.tickItems.splice(k, 1);
17366                     
17367                     if(typeof(e) == 'undefined' && view == false){
17368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
17369                     }
17370                     
17371                     rm = true;
17372                     return;
17373                 }
17374             });
17375             
17376             if(rm){
17377                 return;
17378             }
17379             
17380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
17381                 this.tickItems.push(r.data);
17382             }
17383             
17384             if(typeof(e) == 'undefined' && view == false){
17385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
17386             }
17387                     
17388             return;
17389         }
17390         
17391         if(r){
17392             this.onSelect(r, index);
17393         }
17394         if(doFocus !== false && !this.blockFocus){
17395             this.inputEl().focus();
17396         }
17397     },
17398
17399     // private
17400     restrictHeight : function(){
17401         //this.innerList.dom.style.height = '';
17402         //var inner = this.innerList.dom;
17403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
17404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
17405         //this.list.beginUpdate();
17406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
17407         this.list.alignTo(this.inputEl(), this.listAlign);
17408         this.list.alignTo(this.inputEl(), this.listAlign);
17409         //this.list.endUpdate();
17410     },
17411
17412     // private
17413     onEmptyResults : function(){
17414         
17415         if(this.tickable && this.editable){
17416             this.hasFocus = false;
17417             this.restrictHeight();
17418             return;
17419         }
17420         
17421         this.collapse();
17422     },
17423
17424     /**
17425      * Returns true if the dropdown list is expanded, else false.
17426      */
17427     isExpanded : function(){
17428         return this.list.isVisible();
17429     },
17430
17431     /**
17432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
17433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17434      * @param {String} value The data value of the item to select
17435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17436      * selected item if it is not currently in view (defaults to true)
17437      * @return {Boolean} True if the value matched an item in the list, else false
17438      */
17439     selectByValue : function(v, scrollIntoView){
17440         if(v !== undefined && v !== null){
17441             var r = this.findRecord(this.valueField || this.displayField, v);
17442             if(r){
17443                 this.select(this.store.indexOf(r), scrollIntoView);
17444                 return true;
17445             }
17446         }
17447         return false;
17448     },
17449
17450     /**
17451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
17452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
17453      * @param {Number} index The zero-based index of the list item to select
17454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
17455      * selected item if it is not currently in view (defaults to true)
17456      */
17457     select : function(index, scrollIntoView){
17458         this.selectedIndex = index;
17459         this.view.select(index);
17460         if(scrollIntoView !== false){
17461             var el = this.view.getNode(index);
17462             /*
17463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
17464              */
17465             if(el){
17466                 this.list.scrollChildIntoView(el, false);
17467             }
17468         }
17469     },
17470
17471     // private
17472     selectNext : function(){
17473         var ct = this.store.getCount();
17474         if(ct > 0){
17475             if(this.selectedIndex == -1){
17476                 this.select(0);
17477             }else if(this.selectedIndex < ct-1){
17478                 this.select(this.selectedIndex+1);
17479             }
17480         }
17481     },
17482
17483     // private
17484     selectPrev : function(){
17485         var ct = this.store.getCount();
17486         if(ct > 0){
17487             if(this.selectedIndex == -1){
17488                 this.select(0);
17489             }else if(this.selectedIndex != 0){
17490                 this.select(this.selectedIndex-1);
17491             }
17492         }
17493     },
17494
17495     // private
17496     onKeyUp : function(e){
17497         if(this.editable !== false && !e.isSpecialKey()){
17498             this.lastKey = e.getKey();
17499             this.dqTask.delay(this.queryDelay);
17500         }
17501     },
17502
17503     // private
17504     validateBlur : function(){
17505         return !this.list || !this.list.isVisible();   
17506     },
17507
17508     // private
17509     initQuery : function(){
17510         
17511         var v = this.getRawValue();
17512         
17513         if(this.tickable && this.editable){
17514             v = this.tickableInputEl().getValue();
17515         }
17516         
17517         this.doQuery(v);
17518     },
17519
17520     // private
17521     doForce : function(){
17522         if(this.inputEl().dom.value.length > 0){
17523             this.inputEl().dom.value =
17524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
17525              
17526         }
17527     },
17528
17529     /**
17530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
17531      * query allowing the query action to be canceled if needed.
17532      * @param {String} query The SQL query to execute
17533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
17534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
17535      * saved in the current store (defaults to false)
17536      */
17537     doQuery : function(q, forceAll){
17538         
17539         if(q === undefined || q === null){
17540             q = '';
17541         }
17542         var qe = {
17543             query: q,
17544             forceAll: forceAll,
17545             combo: this,
17546             cancel:false
17547         };
17548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
17549             return false;
17550         }
17551         q = qe.query;
17552         
17553         forceAll = qe.forceAll;
17554         if(forceAll === true || (q.length >= this.minChars)){
17555             
17556             this.hasQuery = true;
17557             
17558             if(this.lastQuery != q || this.alwaysQuery){
17559                 this.lastQuery = q;
17560                 if(this.mode == 'local'){
17561                     this.selectedIndex = -1;
17562                     if(forceAll){
17563                         this.store.clearFilter();
17564                     }else{
17565                         
17566                         if(this.specialFilter){
17567                             this.fireEvent('specialfilter', this);
17568                             this.onLoad();
17569                             return;
17570                         }
17571                         
17572                         this.store.filter(this.displayField, q);
17573                     }
17574                     
17575                     this.store.fireEvent("datachanged", this.store);
17576                     
17577                     this.onLoad();
17578                     
17579                     
17580                 }else{
17581                     
17582                     this.store.baseParams[this.queryParam] = q;
17583                     
17584                     var options = {params : this.getParams(q)};
17585                     
17586                     if(this.loadNext){
17587                         options.add = true;
17588                         options.params.start = this.page * this.pageSize;
17589                     }
17590                     
17591                     this.store.load(options);
17592                     
17593                     /*
17594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
17595                      *  we should expand the list on onLoad
17596                      *  so command out it
17597                      */
17598 //                    this.expand();
17599                 }
17600             }else{
17601                 this.selectedIndex = -1;
17602                 this.onLoad();   
17603             }
17604         }
17605         
17606         this.loadNext = false;
17607     },
17608     
17609     // private
17610     getParams : function(q){
17611         var p = {};
17612         //p[this.queryParam] = q;
17613         
17614         if(this.pageSize){
17615             p.start = 0;
17616             p.limit = this.pageSize;
17617         }
17618         return p;
17619     },
17620
17621     /**
17622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17623      */
17624     collapse : function(){
17625         if(!this.isExpanded()){
17626             return;
17627         }
17628         
17629         this.list.hide();
17630         
17631         this.hasFocus = false;
17632         
17633         if(this.tickable){
17634             this.okBtn.hide();
17635             this.cancelBtn.hide();
17636             this.trigger.show();
17637             
17638             if(this.editable){
17639                 this.tickableInputEl().dom.value = '';
17640                 this.tickableInputEl().blur();
17641             }
17642             
17643         }
17644         
17645         Roo.get(document).un('mousedown', this.collapseIf, this);
17646         Roo.get(document).un('mousewheel', this.collapseIf, this);
17647         if (!this.editable) {
17648             Roo.get(document).un('keydown', this.listKeyPress, this);
17649         }
17650         this.fireEvent('collapse', this);
17651         
17652         this.validate();
17653     },
17654
17655     // private
17656     collapseIf : function(e){
17657         var in_combo  = e.within(this.el);
17658         var in_list =  e.within(this.list);
17659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17660         
17661         if (in_combo || in_list || is_list) {
17662             //e.stopPropagation();
17663             return;
17664         }
17665         
17666         if(this.tickable){
17667             this.onTickableFooterButtonClick(e, false, false);
17668         }
17669
17670         this.collapse();
17671         
17672     },
17673
17674     /**
17675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17676      */
17677     expand : function(){
17678        
17679         if(this.isExpanded() || !this.hasFocus){
17680             return;
17681         }
17682         
17683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17684         this.list.setWidth(lw);
17685         
17686         Roo.log('expand');
17687         
17688         this.list.show();
17689         
17690         this.restrictHeight();
17691         
17692         if(this.tickable){
17693             
17694             this.tickItems = Roo.apply([], this.item);
17695             
17696             this.okBtn.show();
17697             this.cancelBtn.show();
17698             this.trigger.hide();
17699             
17700             if(this.editable){
17701                 this.tickableInputEl().focus();
17702             }
17703             
17704         }
17705         
17706         Roo.get(document).on('mousedown', this.collapseIf, this);
17707         Roo.get(document).on('mousewheel', this.collapseIf, this);
17708         if (!this.editable) {
17709             Roo.get(document).on('keydown', this.listKeyPress, this);
17710         }
17711         
17712         this.fireEvent('expand', this);
17713     },
17714
17715     // private
17716     // Implements the default empty TriggerField.onTriggerClick function
17717     onTriggerClick : function(e)
17718     {
17719         Roo.log('trigger click');
17720         
17721         if(this.disabled || !this.triggerList){
17722             return;
17723         }
17724         
17725         this.page = 0;
17726         this.loadNext = false;
17727         
17728         if(this.isExpanded()){
17729             this.collapse();
17730             if (!this.blockFocus) {
17731                 this.inputEl().focus();
17732             }
17733             
17734         }else {
17735             this.hasFocus = true;
17736             if(this.triggerAction == 'all') {
17737                 this.doQuery(this.allQuery, true);
17738             } else {
17739                 this.doQuery(this.getRawValue());
17740             }
17741             if (!this.blockFocus) {
17742                 this.inputEl().focus();
17743             }
17744         }
17745     },
17746     
17747     onTickableTriggerClick : function(e)
17748     {
17749         if(this.disabled){
17750             return;
17751         }
17752         
17753         this.page = 0;
17754         this.loadNext = false;
17755         this.hasFocus = true;
17756         
17757         if(this.triggerAction == 'all') {
17758             this.doQuery(this.allQuery, true);
17759         } else {
17760             this.doQuery(this.getRawValue());
17761         }
17762     },
17763     
17764     onSearchFieldClick : function(e)
17765     {
17766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17767             this.onTickableFooterButtonClick(e, false, false);
17768             return;
17769         }
17770         
17771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17772             return;
17773         }
17774         
17775         this.page = 0;
17776         this.loadNext = false;
17777         this.hasFocus = true;
17778         
17779         if(this.triggerAction == 'all') {
17780             this.doQuery(this.allQuery, true);
17781         } else {
17782             this.doQuery(this.getRawValue());
17783         }
17784     },
17785     
17786     listKeyPress : function(e)
17787     {
17788         //Roo.log('listkeypress');
17789         // scroll to first matching element based on key pres..
17790         if (e.isSpecialKey()) {
17791             return false;
17792         }
17793         var k = String.fromCharCode(e.getKey()).toUpperCase();
17794         //Roo.log(k);
17795         var match  = false;
17796         var csel = this.view.getSelectedNodes();
17797         var cselitem = false;
17798         if (csel.length) {
17799             var ix = this.view.indexOf(csel[0]);
17800             cselitem  = this.store.getAt(ix);
17801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17802                 cselitem = false;
17803             }
17804             
17805         }
17806         
17807         this.store.each(function(v) { 
17808             if (cselitem) {
17809                 // start at existing selection.
17810                 if (cselitem.id == v.id) {
17811                     cselitem = false;
17812                 }
17813                 return true;
17814             }
17815                 
17816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17817                 match = this.store.indexOf(v);
17818                 return false;
17819             }
17820             return true;
17821         }, this);
17822         
17823         if (match === false) {
17824             return true; // no more action?
17825         }
17826         // scroll to?
17827         this.view.select(match);
17828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17829         sn.scrollIntoView(sn.dom.parentNode, false);
17830     },
17831     
17832     onViewScroll : function(e, t){
17833         
17834         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){
17835             return;
17836         }
17837         
17838         this.hasQuery = true;
17839         
17840         this.loading = this.list.select('.loading', true).first();
17841         
17842         if(this.loading === null){
17843             this.list.createChild({
17844                 tag: 'div',
17845                 cls: 'loading roo-select2-more-results roo-select2-active',
17846                 html: 'Loading more results...'
17847             });
17848             
17849             this.loading = this.list.select('.loading', true).first();
17850             
17851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17852             
17853             this.loading.hide();
17854         }
17855         
17856         this.loading.show();
17857         
17858         var _combo = this;
17859         
17860         this.page++;
17861         this.loadNext = true;
17862         
17863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17864         
17865         return;
17866     },
17867     
17868     addItem : function(o)
17869     {   
17870         var dv = ''; // display value
17871         
17872         if (this.displayField) {
17873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17874         } else {
17875             // this is an error condition!!!
17876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17877         }
17878         
17879         if(!dv.length){
17880             return;
17881         }
17882         
17883         var choice = this.choices.createChild({
17884             tag: 'li',
17885             cls: 'roo-select2-search-choice',
17886             cn: [
17887                 {
17888                     tag: 'div',
17889                     html: dv
17890                 },
17891                 {
17892                     tag: 'a',
17893                     href: '#',
17894                     cls: 'roo-select2-search-choice-close fa fa-times',
17895                     tabindex: '-1'
17896                 }
17897             ]
17898             
17899         }, this.searchField);
17900         
17901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17902         
17903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17904         
17905         this.item.push(o);
17906         
17907         this.lastData = o;
17908         
17909         this.syncValue();
17910         
17911         this.inputEl().dom.value = '';
17912         
17913         this.validate();
17914     },
17915     
17916     onRemoveItem : function(e, _self, o)
17917     {
17918         e.preventDefault();
17919         
17920         this.lastItem = Roo.apply([], this.item);
17921         
17922         var index = this.item.indexOf(o.data) * 1;
17923         
17924         if( index < 0){
17925             Roo.log('not this item?!');
17926             return;
17927         }
17928         
17929         this.item.splice(index, 1);
17930         o.item.remove();
17931         
17932         this.syncValue();
17933         
17934         this.fireEvent('remove', this, e);
17935         
17936         this.validate();
17937         
17938     },
17939     
17940     syncValue : function()
17941     {
17942         if(!this.item.length){
17943             this.clearValue();
17944             return;
17945         }
17946             
17947         var value = [];
17948         var _this = this;
17949         Roo.each(this.item, function(i){
17950             if(_this.valueField){
17951                 value.push(i[_this.valueField]);
17952                 return;
17953             }
17954
17955             value.push(i);
17956         });
17957
17958         this.value = value.join(',');
17959
17960         if(this.hiddenField){
17961             this.hiddenField.dom.value = this.value;
17962         }
17963         
17964         this.store.fireEvent("datachanged", this.store);
17965         
17966         this.validate();
17967     },
17968     
17969     clearItem : function()
17970     {
17971         if(!this.multiple){
17972             return;
17973         }
17974         
17975         this.item = [];
17976         
17977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17978            c.remove();
17979         });
17980         
17981         this.syncValue();
17982         
17983         this.validate();
17984         
17985         if(this.tickable && !Roo.isTouch){
17986             this.view.refresh();
17987         }
17988     },
17989     
17990     inputEl: function ()
17991     {
17992         if(Roo.isIOS && this.useNativeIOS){
17993             return this.el.select('select.roo-ios-select', true).first();
17994         }
17995         
17996         if(Roo.isTouch && this.mobileTouchView){
17997             return this.el.select('input.form-control',true).first();
17998         }
17999         
18000         if(this.tickable){
18001             return this.searchField;
18002         }
18003         
18004         return this.el.select('input.form-control',true).first();
18005     },
18006     
18007     onTickableFooterButtonClick : function(e, btn, el)
18008     {
18009         e.preventDefault();
18010         
18011         this.lastItem = Roo.apply([], this.item);
18012         
18013         if(btn && btn.name == 'cancel'){
18014             this.tickItems = Roo.apply([], this.item);
18015             this.collapse();
18016             return;
18017         }
18018         
18019         this.clearItem();
18020         
18021         var _this = this;
18022         
18023         Roo.each(this.tickItems, function(o){
18024             _this.addItem(o);
18025         });
18026         
18027         this.collapse();
18028         
18029     },
18030     
18031     validate : function()
18032     {
18033         if(this.getVisibilityEl().hasClass('hidden')){
18034             return true;
18035         }
18036         
18037         var v = this.getRawValue();
18038         
18039         if(this.multiple){
18040             v = this.getValue();
18041         }
18042         
18043         if(this.disabled || this.allowBlank || v.length){
18044             this.markValid();
18045             return true;
18046         }
18047         
18048         this.markInvalid();
18049         return false;
18050     },
18051     
18052     tickableInputEl : function()
18053     {
18054         if(!this.tickable || !this.editable){
18055             return this.inputEl();
18056         }
18057         
18058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
18059     },
18060     
18061     
18062     getAutoCreateTouchView : function()
18063     {
18064         var id = Roo.id();
18065         
18066         var cfg = {
18067             cls: 'form-group' //input-group
18068         };
18069         
18070         var input =  {
18071             tag: 'input',
18072             id : id,
18073             type : this.inputType,
18074             cls : 'form-control x-combo-noedit',
18075             autocomplete: 'new-password',
18076             placeholder : this.placeholder || '',
18077             readonly : true
18078         };
18079         
18080         if (this.name) {
18081             input.name = this.name;
18082         }
18083         
18084         if (this.size) {
18085             input.cls += ' input-' + this.size;
18086         }
18087         
18088         if (this.disabled) {
18089             input.disabled = true;
18090         }
18091         
18092         var inputblock = {
18093             cls : 'roo-combobox-wrap',
18094             cn : [
18095                 input
18096             ]
18097         };
18098         
18099         if(this.before){
18100             inputblock.cls += ' input-group';
18101             
18102             inputblock.cn.unshift({
18103                 tag :'span',
18104                 cls : 'input-group-addon input-group-prepend input-group-text',
18105                 html : this.before
18106             });
18107         }
18108         
18109         if(this.removable && !this.multiple){
18110             inputblock.cls += ' roo-removable';
18111             
18112             inputblock.cn.push({
18113                 tag: 'button',
18114                 html : 'x',
18115                 cls : 'roo-combo-removable-btn close'
18116             });
18117         }
18118
18119         if(this.hasFeedback && !this.allowBlank){
18120             
18121             inputblock.cls += ' has-feedback';
18122             
18123             inputblock.cn.push({
18124                 tag: 'span',
18125                 cls: 'glyphicon form-control-feedback'
18126             });
18127             
18128         }
18129         
18130         if (this.after) {
18131             
18132             inputblock.cls += (this.before) ? '' : ' input-group';
18133             
18134             inputblock.cn.push({
18135                 tag :'span',
18136                 cls : 'input-group-addon input-group-append input-group-text',
18137                 html : this.after
18138             });
18139         }
18140
18141         
18142         var ibwrap = inputblock;
18143         
18144         if(this.multiple){
18145             ibwrap = {
18146                 tag: 'ul',
18147                 cls: 'roo-select2-choices',
18148                 cn:[
18149                     {
18150                         tag: 'li',
18151                         cls: 'roo-select2-search-field',
18152                         cn: [
18153
18154                             inputblock
18155                         ]
18156                     }
18157                 ]
18158             };
18159         
18160             
18161         }
18162         
18163         var combobox = {
18164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
18165             cn: [
18166                 {
18167                     tag: 'input',
18168                     type : 'hidden',
18169                     cls: 'form-hidden-field'
18170                 },
18171                 ibwrap
18172             ]
18173         };
18174         
18175         if(!this.multiple && this.showToggleBtn){
18176             
18177             var caret = {
18178                 cls: 'caret'
18179             };
18180             
18181             if (this.caret != false) {
18182                 caret = {
18183                      tag: 'i',
18184                      cls: 'fa fa-' + this.caret
18185                 };
18186                 
18187             }
18188             
18189             combobox.cn.push({
18190                 tag :'span',
18191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
18192                 cn : [
18193                     Roo.bootstrap.version == 3 ? caret : '',
18194                     {
18195                         tag: 'span',
18196                         cls: 'combobox-clear',
18197                         cn  : [
18198                             {
18199                                 tag : 'i',
18200                                 cls: 'icon-remove'
18201                             }
18202                         ]
18203                     }
18204                 ]
18205
18206             })
18207         }
18208         
18209         if(this.multiple){
18210             combobox.cls += ' roo-select2-container-multi';
18211         }
18212         
18213         var required =  this.allowBlank ?  {
18214                     tag : 'i',
18215                     style: 'display: none'
18216                 } : {
18217                    tag : 'i',
18218                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
18219                    tooltip : 'This field is required'
18220                 };
18221         
18222         var align = this.labelAlign || this.parentLabelAlign();
18223         
18224         if (align ==='left' && this.fieldLabel.length) {
18225
18226             cfg.cn = [
18227                 required,
18228                 {
18229                     tag: 'label',
18230                     cls : 'control-label col-form-label',
18231                     html : this.fieldLabel
18232
18233                 },
18234                 {
18235                     cls : 'roo-combobox-wrap ', 
18236                     cn: [
18237                         combobox
18238                     ]
18239                 }
18240             ];
18241             
18242             var labelCfg = cfg.cn[1];
18243             var contentCfg = cfg.cn[2];
18244             
18245
18246             if(this.indicatorpos == 'right'){
18247                 cfg.cn = [
18248                     {
18249                         tag: 'label',
18250                         'for' :  id,
18251                         cls : 'control-label col-form-label',
18252                         cn : [
18253                             {
18254                                 tag : 'span',
18255                                 html : this.fieldLabel
18256                             },
18257                             required
18258                         ]
18259                     },
18260                     {
18261                         cls : "roo-combobox-wrap ",
18262                         cn: [
18263                             combobox
18264                         ]
18265                     }
18266
18267                 ];
18268                 
18269                 labelCfg = cfg.cn[0];
18270                 contentCfg = cfg.cn[1];
18271             }
18272             
18273            
18274             
18275             if(this.labelWidth > 12){
18276                 labelCfg.style = "width: " + this.labelWidth + 'px';
18277             }
18278            
18279             if(this.labelWidth < 13 && this.labelmd == 0){
18280                 this.labelmd = this.labelWidth;
18281             }
18282             
18283             if(this.labellg > 0){
18284                 labelCfg.cls += ' col-lg-' + this.labellg;
18285                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
18286             }
18287             
18288             if(this.labelmd > 0){
18289                 labelCfg.cls += ' col-md-' + this.labelmd;
18290                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
18291             }
18292             
18293             if(this.labelsm > 0){
18294                 labelCfg.cls += ' col-sm-' + this.labelsm;
18295                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
18296             }
18297             
18298             if(this.labelxs > 0){
18299                 labelCfg.cls += ' col-xs-' + this.labelxs;
18300                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
18301             }
18302                 
18303                 
18304         } else if ( this.fieldLabel.length) {
18305             cfg.cn = [
18306                required,
18307                 {
18308                     tag: 'label',
18309                     cls : 'control-label',
18310                     html : this.fieldLabel
18311
18312                 },
18313                 {
18314                     cls : '', 
18315                     cn: [
18316                         combobox
18317                     ]
18318                 }
18319             ];
18320             
18321             if(this.indicatorpos == 'right'){
18322                 cfg.cn = [
18323                     {
18324                         tag: 'label',
18325                         cls : 'control-label',
18326                         html : this.fieldLabel,
18327                         cn : [
18328                             required
18329                         ]
18330                     },
18331                     {
18332                         cls : '', 
18333                         cn: [
18334                             combobox
18335                         ]
18336                     }
18337                 ];
18338             }
18339         } else {
18340             cfg.cn = combobox;    
18341         }
18342         
18343         
18344         var settings = this;
18345         
18346         ['xs','sm','md','lg'].map(function(size){
18347             if (settings[size]) {
18348                 cfg.cls += ' col-' + size + '-' + settings[size];
18349             }
18350         });
18351         
18352         return cfg;
18353     },
18354     
18355     initTouchView : function()
18356     {
18357         this.renderTouchView();
18358         
18359         this.touchViewEl.on('scroll', function(){
18360             this.el.dom.scrollTop = 0;
18361         }, this);
18362         
18363         this.originalValue = this.getValue();
18364         
18365         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
18366         
18367         this.inputEl().on("click", this.showTouchView, this);
18368         if (this.triggerEl) {
18369             this.triggerEl.on("click", this.showTouchView, this);
18370         }
18371         
18372         
18373         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
18374         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
18375         
18376         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
18377         
18378         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
18379         this.store.on('load', this.onTouchViewLoad, this);
18380         this.store.on('loadexception', this.onTouchViewLoadException, this);
18381         
18382         if(this.hiddenName){
18383             
18384             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
18385             
18386             this.hiddenField.dom.value =
18387                 this.hiddenValue !== undefined ? this.hiddenValue :
18388                 this.value !== undefined ? this.value : '';
18389         
18390             this.el.dom.removeAttribute('name');
18391             this.hiddenField.dom.setAttribute('name', this.hiddenName);
18392         }
18393         
18394         if(this.multiple){
18395             this.choices = this.el.select('ul.roo-select2-choices', true).first();
18396             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
18397         }
18398         
18399         if(this.removable && !this.multiple){
18400             var close = this.closeTriggerEl();
18401             if(close){
18402                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
18403                 close.on('click', this.removeBtnClick, this, close);
18404             }
18405         }
18406         /*
18407          * fix the bug in Safari iOS8
18408          */
18409         this.inputEl().on("focus", function(e){
18410             document.activeElement.blur();
18411         }, this);
18412         
18413         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
18414         
18415         return;
18416         
18417         
18418     },
18419     
18420     renderTouchView : function()
18421     {
18422         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
18423         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18424         
18425         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
18426         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18427         
18428         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
18429         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18430         this.touchViewBodyEl.setStyle('overflow', 'auto');
18431         
18432         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
18433         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18434         
18435         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
18436         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
18437         
18438     },
18439     
18440     showTouchView : function()
18441     {
18442         if(this.disabled){
18443             return;
18444         }
18445         
18446         this.touchViewHeaderEl.hide();
18447
18448         if(this.modalTitle.length){
18449             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
18450             this.touchViewHeaderEl.show();
18451         }
18452
18453         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
18454         this.touchViewEl.show();
18455
18456         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
18457         
18458         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
18459         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
18460
18461         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18462
18463         if(this.modalTitle.length){
18464             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18465         }
18466         
18467         this.touchViewBodyEl.setHeight(bodyHeight);
18468
18469         if(this.animate){
18470             var _this = this;
18471             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
18472         }else{
18473             this.touchViewEl.addClass(['in','show']);
18474         }
18475         
18476         if(this._touchViewMask){
18477             Roo.get(document.body).addClass("x-body-masked");
18478             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
18479             this._touchViewMask.setStyle('z-index', 10000);
18480             this._touchViewMask.addClass('show');
18481         }
18482         
18483         this.doTouchViewQuery();
18484         
18485     },
18486     
18487     hideTouchView : function()
18488     {
18489         this.touchViewEl.removeClass(['in','show']);
18490
18491         if(this.animate){
18492             var _this = this;
18493             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
18494         }else{
18495             this.touchViewEl.setStyle('display', 'none');
18496         }
18497         
18498         if(this._touchViewMask){
18499             this._touchViewMask.removeClass('show');
18500             Roo.get(document.body).removeClass("x-body-masked");
18501         }
18502     },
18503     
18504     setTouchViewValue : function()
18505     {
18506         if(this.multiple){
18507             this.clearItem();
18508         
18509             var _this = this;
18510
18511             Roo.each(this.tickItems, function(o){
18512                 this.addItem(o);
18513             }, this);
18514         }
18515         
18516         this.hideTouchView();
18517     },
18518     
18519     doTouchViewQuery : function()
18520     {
18521         var qe = {
18522             query: '',
18523             forceAll: true,
18524             combo: this,
18525             cancel:false
18526         };
18527         
18528         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
18529             return false;
18530         }
18531         
18532         if(!this.alwaysQuery || this.mode == 'local'){
18533             this.onTouchViewLoad();
18534             return;
18535         }
18536         
18537         this.store.load();
18538     },
18539     
18540     onTouchViewBeforeLoad : function(combo,opts)
18541     {
18542         return;
18543     },
18544
18545     // private
18546     onTouchViewLoad : function()
18547     {
18548         if(this.store.getCount() < 1){
18549             this.onTouchViewEmptyResults();
18550             return;
18551         }
18552         
18553         this.clearTouchView();
18554         
18555         var rawValue = this.getRawValue();
18556         
18557         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
18558         
18559         this.tickItems = [];
18560         
18561         this.store.data.each(function(d, rowIndex){
18562             var row = this.touchViewListGroup.createChild(template);
18563             
18564             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
18565                 row.addClass(d.data.cls);
18566             }
18567             
18568             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18569                 var cfg = {
18570                     data : d.data,
18571                     html : d.data[this.displayField]
18572                 };
18573                 
18574                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
18575                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
18576                 }
18577             }
18578             row.removeClass('selected');
18579             if(!this.multiple && this.valueField &&
18580                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
18581             {
18582                 // radio buttons..
18583                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18584                 row.addClass('selected');
18585             }
18586             
18587             if(this.multiple && this.valueField &&
18588                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
18589             {
18590                 
18591                 // checkboxes...
18592                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18593                 this.tickItems.push(d.data);
18594             }
18595             
18596             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
18597             
18598         }, this);
18599         
18600         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18601         
18602         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18603
18604         if(this.modalTitle.length){
18605             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18606         }
18607
18608         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18609         
18610         if(this.mobile_restrict_height && listHeight < bodyHeight){
18611             this.touchViewBodyEl.setHeight(listHeight);
18612         }
18613         
18614         var _this = this;
18615         
18616         if(firstChecked && listHeight > bodyHeight){
18617             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18618         }
18619         
18620     },
18621     
18622     onTouchViewLoadException : function()
18623     {
18624         this.hideTouchView();
18625     },
18626     
18627     onTouchViewEmptyResults : function()
18628     {
18629         this.clearTouchView();
18630         
18631         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18632         
18633         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18634         
18635     },
18636     
18637     clearTouchView : function()
18638     {
18639         this.touchViewListGroup.dom.innerHTML = '';
18640     },
18641     
18642     onTouchViewClick : function(e, el, o)
18643     {
18644         e.preventDefault();
18645         
18646         var row = o.row;
18647         var rowIndex = o.rowIndex;
18648         
18649         var r = this.store.getAt(rowIndex);
18650         
18651         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18652             
18653             if(!this.multiple){
18654                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18655                     c.dom.removeAttribute('checked');
18656                 }, this);
18657
18658                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18659
18660                 this.setFromData(r.data);
18661
18662                 var close = this.closeTriggerEl();
18663
18664                 if(close){
18665                     close.show();
18666                 }
18667
18668                 this.hideTouchView();
18669
18670                 this.fireEvent('select', this, r, rowIndex);
18671
18672                 return;
18673             }
18674
18675             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18676                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18677                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18678                 return;
18679             }
18680
18681             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18682             this.addItem(r.data);
18683             this.tickItems.push(r.data);
18684         }
18685     },
18686     
18687     getAutoCreateNativeIOS : function()
18688     {
18689         var cfg = {
18690             cls: 'form-group' //input-group,
18691         };
18692         
18693         var combobox =  {
18694             tag: 'select',
18695             cls : 'roo-ios-select'
18696         };
18697         
18698         if (this.name) {
18699             combobox.name = this.name;
18700         }
18701         
18702         if (this.disabled) {
18703             combobox.disabled = true;
18704         }
18705         
18706         var settings = this;
18707         
18708         ['xs','sm','md','lg'].map(function(size){
18709             if (settings[size]) {
18710                 cfg.cls += ' col-' + size + '-' + settings[size];
18711             }
18712         });
18713         
18714         cfg.cn = combobox;
18715         
18716         return cfg;
18717         
18718     },
18719     
18720     initIOSView : function()
18721     {
18722         this.store.on('load', this.onIOSViewLoad, this);
18723         
18724         return;
18725     },
18726     
18727     onIOSViewLoad : function()
18728     {
18729         if(this.store.getCount() < 1){
18730             return;
18731         }
18732         
18733         this.clearIOSView();
18734         
18735         if(this.allowBlank) {
18736             
18737             var default_text = '-- SELECT --';
18738             
18739             if(this.placeholder.length){
18740                 default_text = this.placeholder;
18741             }
18742             
18743             if(this.emptyTitle.length){
18744                 default_text += ' - ' + this.emptyTitle + ' -';
18745             }
18746             
18747             var opt = this.inputEl().createChild({
18748                 tag: 'option',
18749                 value : 0,
18750                 html : default_text
18751             });
18752             
18753             var o = {};
18754             o[this.valueField] = 0;
18755             o[this.displayField] = default_text;
18756             
18757             this.ios_options.push({
18758                 data : o,
18759                 el : opt
18760             });
18761             
18762         }
18763         
18764         this.store.data.each(function(d, rowIndex){
18765             
18766             var html = '';
18767             
18768             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18769                 html = d.data[this.displayField];
18770             }
18771             
18772             var value = '';
18773             
18774             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18775                 value = d.data[this.valueField];
18776             }
18777             
18778             var option = {
18779                 tag: 'option',
18780                 value : value,
18781                 html : html
18782             };
18783             
18784             if(this.value == d.data[this.valueField]){
18785                 option['selected'] = true;
18786             }
18787             
18788             var opt = this.inputEl().createChild(option);
18789             
18790             this.ios_options.push({
18791                 data : d.data,
18792                 el : opt
18793             });
18794             
18795         }, this);
18796         
18797         this.inputEl().on('change', function(){
18798            this.fireEvent('select', this);
18799         }, this);
18800         
18801     },
18802     
18803     clearIOSView: function()
18804     {
18805         this.inputEl().dom.innerHTML = '';
18806         
18807         this.ios_options = [];
18808     },
18809     
18810     setIOSValue: function(v)
18811     {
18812         this.value = v;
18813         
18814         if(!this.ios_options){
18815             return;
18816         }
18817         
18818         Roo.each(this.ios_options, function(opts){
18819            
18820            opts.el.dom.removeAttribute('selected');
18821            
18822            if(opts.data[this.valueField] != v){
18823                return;
18824            }
18825            
18826            opts.el.dom.setAttribute('selected', true);
18827            
18828         }, this);
18829     }
18830
18831     /** 
18832     * @cfg {Boolean} grow 
18833     * @hide 
18834     */
18835     /** 
18836     * @cfg {Number} growMin 
18837     * @hide 
18838     */
18839     /** 
18840     * @cfg {Number} growMax 
18841     * @hide 
18842     */
18843     /**
18844      * @hide
18845      * @method autoSize
18846      */
18847 });
18848
18849 Roo.apply(Roo.bootstrap.ComboBox,  {
18850     
18851     header : {
18852         tag: 'div',
18853         cls: 'modal-header',
18854         cn: [
18855             {
18856                 tag: 'h4',
18857                 cls: 'modal-title'
18858             }
18859         ]
18860     },
18861     
18862     body : {
18863         tag: 'div',
18864         cls: 'modal-body',
18865         cn: [
18866             {
18867                 tag: 'ul',
18868                 cls: 'list-group'
18869             }
18870         ]
18871     },
18872     
18873     listItemRadio : {
18874         tag: 'li',
18875         cls: 'list-group-item',
18876         cn: [
18877             {
18878                 tag: 'span',
18879                 cls: 'roo-combobox-list-group-item-value'
18880             },
18881             {
18882                 tag: 'div',
18883                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18884                 cn: [
18885                     {
18886                         tag: 'input',
18887                         type: 'radio'
18888                     },
18889                     {
18890                         tag: 'label'
18891                     }
18892                 ]
18893             }
18894         ]
18895     },
18896     
18897     listItemCheckbox : {
18898         tag: 'li',
18899         cls: 'list-group-item',
18900         cn: [
18901             {
18902                 tag: 'span',
18903                 cls: 'roo-combobox-list-group-item-value'
18904             },
18905             {
18906                 tag: 'div',
18907                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18908                 cn: [
18909                     {
18910                         tag: 'input',
18911                         type: 'checkbox'
18912                     },
18913                     {
18914                         tag: 'label'
18915                     }
18916                 ]
18917             }
18918         ]
18919     },
18920     
18921     emptyResult : {
18922         tag: 'div',
18923         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18924     },
18925     
18926     footer : {
18927         tag: 'div',
18928         cls: 'modal-footer',
18929         cn: [
18930             {
18931                 tag: 'div',
18932                 cls: 'row',
18933                 cn: [
18934                     {
18935                         tag: 'div',
18936                         cls: 'col-xs-6 text-left',
18937                         cn: {
18938                             tag: 'button',
18939                             cls: 'btn btn-danger roo-touch-view-cancel',
18940                             html: 'Cancel'
18941                         }
18942                     },
18943                     {
18944                         tag: 'div',
18945                         cls: 'col-xs-6 text-right',
18946                         cn: {
18947                             tag: 'button',
18948                             cls: 'btn btn-success roo-touch-view-ok',
18949                             html: 'OK'
18950                         }
18951                     }
18952                 ]
18953             }
18954         ]
18955         
18956     }
18957 });
18958
18959 Roo.apply(Roo.bootstrap.ComboBox,  {
18960     
18961     touchViewTemplate : {
18962         tag: 'div',
18963         cls: 'modal fade roo-combobox-touch-view',
18964         cn: [
18965             {
18966                 tag: 'div',
18967                 cls: 'modal-dialog',
18968                 style : 'position:fixed', // we have to fix position....
18969                 cn: [
18970                     {
18971                         tag: 'div',
18972                         cls: 'modal-content',
18973                         cn: [
18974                             Roo.bootstrap.ComboBox.header,
18975                             Roo.bootstrap.ComboBox.body,
18976                             Roo.bootstrap.ComboBox.footer
18977                         ]
18978                     }
18979                 ]
18980             }
18981         ]
18982     }
18983 });/*
18984  * Based on:
18985  * Ext JS Library 1.1.1
18986  * Copyright(c) 2006-2007, Ext JS, LLC.
18987  *
18988  * Originally Released Under LGPL - original licence link has changed is not relivant.
18989  *
18990  * Fork - LGPL
18991  * <script type="text/javascript">
18992  */
18993
18994 /**
18995  * @class Roo.View
18996  * @extends Roo.util.Observable
18997  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18998  * This class also supports single and multi selection modes. <br>
18999  * Create a data model bound view:
19000  <pre><code>
19001  var store = new Roo.data.Store(...);
19002
19003  var view = new Roo.View({
19004     el : "my-element",
19005     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
19006  
19007     singleSelect: true,
19008     selectedClass: "ydataview-selected",
19009     store: store
19010  });
19011
19012  // listen for node click?
19013  view.on("click", function(vw, index, node, e){
19014  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
19015  });
19016
19017  // load XML data
19018  dataModel.load("foobar.xml");
19019  </code></pre>
19020  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
19021  * <br><br>
19022  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
19023  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
19024  * 
19025  * Note: old style constructor is still suported (container, template, config)
19026  * 
19027  * @constructor
19028  * Create a new View
19029  * @param {Object} config The config object
19030  * 
19031  */
19032 Roo.View = function(config, depreciated_tpl, depreciated_config){
19033     
19034     this.parent = false;
19035     
19036     if (typeof(depreciated_tpl) == 'undefined') {
19037         // new way.. - universal constructor.
19038         Roo.apply(this, config);
19039         this.el  = Roo.get(this.el);
19040     } else {
19041         // old format..
19042         this.el  = Roo.get(config);
19043         this.tpl = depreciated_tpl;
19044         Roo.apply(this, depreciated_config);
19045     }
19046     this.wrapEl  = this.el.wrap().wrap();
19047     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
19048     
19049     
19050     if(typeof(this.tpl) == "string"){
19051         this.tpl = new Roo.Template(this.tpl);
19052     } else {
19053         // support xtype ctors..
19054         this.tpl = new Roo.factory(this.tpl, Roo);
19055     }
19056     
19057     
19058     this.tpl.compile();
19059     
19060     /** @private */
19061     this.addEvents({
19062         /**
19063          * @event beforeclick
19064          * Fires before a click is processed. Returns false to cancel the default action.
19065          * @param {Roo.View} this
19066          * @param {Number} index The index of the target node
19067          * @param {HTMLElement} node The target node
19068          * @param {Roo.EventObject} e The raw event object
19069          */
19070             "beforeclick" : true,
19071         /**
19072          * @event click
19073          * Fires when a template node is clicked.
19074          * @param {Roo.View} this
19075          * @param {Number} index The index of the target node
19076          * @param {HTMLElement} node The target node
19077          * @param {Roo.EventObject} e The raw event object
19078          */
19079             "click" : true,
19080         /**
19081          * @event dblclick
19082          * Fires when a template node is double clicked.
19083          * @param {Roo.View} this
19084          * @param {Number} index The index of the target node
19085          * @param {HTMLElement} node The target node
19086          * @param {Roo.EventObject} e The raw event object
19087          */
19088             "dblclick" : true,
19089         /**
19090          * @event contextmenu
19091          * Fires when a template node is right clicked.
19092          * @param {Roo.View} this
19093          * @param {Number} index The index of the target node
19094          * @param {HTMLElement} node The target node
19095          * @param {Roo.EventObject} e The raw event object
19096          */
19097             "contextmenu" : true,
19098         /**
19099          * @event selectionchange
19100          * Fires when the selected nodes change.
19101          * @param {Roo.View} this
19102          * @param {Array} selections Array of the selected nodes
19103          */
19104             "selectionchange" : true,
19105     
19106         /**
19107          * @event beforeselect
19108          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
19109          * @param {Roo.View} this
19110          * @param {HTMLElement} node The node to be selected
19111          * @param {Array} selections Array of currently selected nodes
19112          */
19113             "beforeselect" : true,
19114         /**
19115          * @event preparedata
19116          * Fires on every row to render, to allow you to change the data.
19117          * @param {Roo.View} this
19118          * @param {Object} data to be rendered (change this)
19119          */
19120           "preparedata" : true
19121           
19122           
19123         });
19124
19125
19126
19127     this.el.on({
19128         "click": this.onClick,
19129         "dblclick": this.onDblClick,
19130         "contextmenu": this.onContextMenu,
19131         scope:this
19132     });
19133
19134     this.selections = [];
19135     this.nodes = [];
19136     this.cmp = new Roo.CompositeElementLite([]);
19137     if(this.store){
19138         this.store = Roo.factory(this.store, Roo.data);
19139         this.setStore(this.store, true);
19140     }
19141     
19142     if ( this.footer && this.footer.xtype) {
19143            
19144          var fctr = this.wrapEl.appendChild(document.createElement("div"));
19145         
19146         this.footer.dataSource = this.store;
19147         this.footer.container = fctr;
19148         this.footer = Roo.factory(this.footer, Roo);
19149         fctr.insertFirst(this.el);
19150         
19151         // this is a bit insane - as the paging toolbar seems to detach the el..
19152 //        dom.parentNode.parentNode.parentNode
19153          // they get detached?
19154     }
19155     
19156     
19157     Roo.View.superclass.constructor.call(this);
19158     
19159     
19160 };
19161
19162 Roo.extend(Roo.View, Roo.util.Observable, {
19163     
19164      /**
19165      * @cfg {Roo.data.Store} store Data store to load data from.
19166      */
19167     store : false,
19168     
19169     /**
19170      * @cfg {String|Roo.Element} el The container element.
19171      */
19172     el : '',
19173     
19174     /**
19175      * @cfg {String|Roo.Template} tpl The template used by this View 
19176      */
19177     tpl : false,
19178     /**
19179      * @cfg {String} dataName the named area of the template to use as the data area
19180      *                          Works with domtemplates roo-name="name"
19181      */
19182     dataName: false,
19183     /**
19184      * @cfg {String} selectedClass The css class to add to selected nodes
19185      */
19186     selectedClass : "x-view-selected",
19187      /**
19188      * @cfg {String} emptyText The empty text to show when nothing is loaded.
19189      */
19190     emptyText : "",
19191     
19192     /**
19193      * @cfg {String} text to display on mask (default Loading)
19194      */
19195     mask : false,
19196     /**
19197      * @cfg {Boolean} multiSelect Allow multiple selection
19198      */
19199     multiSelect : false,
19200     /**
19201      * @cfg {Boolean} singleSelect Allow single selection
19202      */
19203     singleSelect:  false,
19204     
19205     /**
19206      * @cfg {Boolean} toggleSelect - selecting 
19207      */
19208     toggleSelect : false,
19209     
19210     /**
19211      * @cfg {Boolean} tickable - selecting 
19212      */
19213     tickable : false,
19214     
19215     /**
19216      * Returns the element this view is bound to.
19217      * @return {Roo.Element}
19218      */
19219     getEl : function(){
19220         return this.wrapEl;
19221     },
19222     
19223     
19224
19225     /**
19226      * Refreshes the view. - called by datachanged on the store. - do not call directly.
19227      */
19228     refresh : function(){
19229         //Roo.log('refresh');
19230         var t = this.tpl;
19231         
19232         // if we are using something like 'domtemplate', then
19233         // the what gets used is:
19234         // t.applySubtemplate(NAME, data, wrapping data..)
19235         // the outer template then get' applied with
19236         //     the store 'extra data'
19237         // and the body get's added to the
19238         //      roo-name="data" node?
19239         //      <span class='roo-tpl-{name}'></span> ?????
19240         
19241         
19242         
19243         this.clearSelections();
19244         this.el.update("");
19245         var html = [];
19246         var records = this.store.getRange();
19247         if(records.length < 1) {
19248             
19249             // is this valid??  = should it render a template??
19250             
19251             this.el.update(this.emptyText);
19252             return;
19253         }
19254         var el = this.el;
19255         if (this.dataName) {
19256             this.el.update(t.apply(this.store.meta)); //????
19257             el = this.el.child('.roo-tpl-' + this.dataName);
19258         }
19259         
19260         for(var i = 0, len = records.length; i < len; i++){
19261             var data = this.prepareData(records[i].data, i, records[i]);
19262             this.fireEvent("preparedata", this, data, i, records[i]);
19263             
19264             var d = Roo.apply({}, data);
19265             
19266             if(this.tickable){
19267                 Roo.apply(d, {'roo-id' : Roo.id()});
19268                 
19269                 var _this = this;
19270             
19271                 Roo.each(this.parent.item, function(item){
19272                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
19273                         return;
19274                     }
19275                     Roo.apply(d, {'roo-data-checked' : 'checked'});
19276                 });
19277             }
19278             
19279             html[html.length] = Roo.util.Format.trim(
19280                 this.dataName ?
19281                     t.applySubtemplate(this.dataName, d, this.store.meta) :
19282                     t.apply(d)
19283             );
19284         }
19285         
19286         
19287         
19288         el.update(html.join(""));
19289         this.nodes = el.dom.childNodes;
19290         this.updateIndexes(0);
19291     },
19292     
19293
19294     /**
19295      * Function to override to reformat the data that is sent to
19296      * the template for each node.
19297      * DEPRICATED - use the preparedata event handler.
19298      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
19299      * a JSON object for an UpdateManager bound view).
19300      */
19301     prepareData : function(data, index, record)
19302     {
19303         this.fireEvent("preparedata", this, data, index, record);
19304         return data;
19305     },
19306
19307     onUpdate : function(ds, record){
19308         // Roo.log('on update');   
19309         this.clearSelections();
19310         var index = this.store.indexOf(record);
19311         var n = this.nodes[index];
19312         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
19313         n.parentNode.removeChild(n);
19314         this.updateIndexes(index, index);
19315     },
19316
19317     
19318     
19319 // --------- FIXME     
19320     onAdd : function(ds, records, index)
19321     {
19322         //Roo.log(['on Add', ds, records, index] );        
19323         this.clearSelections();
19324         if(this.nodes.length == 0){
19325             this.refresh();
19326             return;
19327         }
19328         var n = this.nodes[index];
19329         for(var i = 0, len = records.length; i < len; i++){
19330             var d = this.prepareData(records[i].data, i, records[i]);
19331             if(n){
19332                 this.tpl.insertBefore(n, d);
19333             }else{
19334                 
19335                 this.tpl.append(this.el, d);
19336             }
19337         }
19338         this.updateIndexes(index);
19339     },
19340
19341     onRemove : function(ds, record, index){
19342        // Roo.log('onRemove');
19343         this.clearSelections();
19344         var el = this.dataName  ?
19345             this.el.child('.roo-tpl-' + this.dataName) :
19346             this.el; 
19347         
19348         el.dom.removeChild(this.nodes[index]);
19349         this.updateIndexes(index);
19350     },
19351
19352     /**
19353      * Refresh an individual node.
19354      * @param {Number} index
19355      */
19356     refreshNode : function(index){
19357         this.onUpdate(this.store, this.store.getAt(index));
19358     },
19359
19360     updateIndexes : function(startIndex, endIndex){
19361         var ns = this.nodes;
19362         startIndex = startIndex || 0;
19363         endIndex = endIndex || ns.length - 1;
19364         for(var i = startIndex; i <= endIndex; i++){
19365             ns[i].nodeIndex = i;
19366         }
19367     },
19368
19369     /**
19370      * Changes the data store this view uses and refresh the view.
19371      * @param {Store} store
19372      */
19373     setStore : function(store, initial){
19374         if(!initial && this.store){
19375             this.store.un("datachanged", this.refresh);
19376             this.store.un("add", this.onAdd);
19377             this.store.un("remove", this.onRemove);
19378             this.store.un("update", this.onUpdate);
19379             this.store.un("clear", this.refresh);
19380             this.store.un("beforeload", this.onBeforeLoad);
19381             this.store.un("load", this.onLoad);
19382             this.store.un("loadexception", this.onLoad);
19383         }
19384         if(store){
19385           
19386             store.on("datachanged", this.refresh, this);
19387             store.on("add", this.onAdd, this);
19388             store.on("remove", this.onRemove, this);
19389             store.on("update", this.onUpdate, this);
19390             store.on("clear", this.refresh, this);
19391             store.on("beforeload", this.onBeforeLoad, this);
19392             store.on("load", this.onLoad, this);
19393             store.on("loadexception", this.onLoad, this);
19394         }
19395         
19396         if(store){
19397             this.refresh();
19398         }
19399     },
19400     /**
19401      * onbeforeLoad - masks the loading area.
19402      *
19403      */
19404     onBeforeLoad : function(store,opts)
19405     {
19406          //Roo.log('onBeforeLoad');   
19407         if (!opts.add) {
19408             this.el.update("");
19409         }
19410         this.el.mask(this.mask ? this.mask : "Loading" ); 
19411     },
19412     onLoad : function ()
19413     {
19414         this.el.unmask();
19415     },
19416     
19417
19418     /**
19419      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
19420      * @param {HTMLElement} node
19421      * @return {HTMLElement} The template node
19422      */
19423     findItemFromChild : function(node){
19424         var el = this.dataName  ?
19425             this.el.child('.roo-tpl-' + this.dataName,true) :
19426             this.el.dom; 
19427         
19428         if(!node || node.parentNode == el){
19429                     return node;
19430             }
19431             var p = node.parentNode;
19432             while(p && p != el){
19433             if(p.parentNode == el){
19434                 return p;
19435             }
19436             p = p.parentNode;
19437         }
19438             return null;
19439     },
19440
19441     /** @ignore */
19442     onClick : function(e){
19443         var item = this.findItemFromChild(e.getTarget());
19444         if(item){
19445             var index = this.indexOf(item);
19446             if(this.onItemClick(item, index, e) !== false){
19447                 this.fireEvent("click", this, index, item, e);
19448             }
19449         }else{
19450             this.clearSelections();
19451         }
19452     },
19453
19454     /** @ignore */
19455     onContextMenu : function(e){
19456         var item = this.findItemFromChild(e.getTarget());
19457         if(item){
19458             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
19459         }
19460     },
19461
19462     /** @ignore */
19463     onDblClick : function(e){
19464         var item = this.findItemFromChild(e.getTarget());
19465         if(item){
19466             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
19467         }
19468     },
19469
19470     onItemClick : function(item, index, e)
19471     {
19472         if(this.fireEvent("beforeclick", this, index, item, e) === false){
19473             return false;
19474         }
19475         if (this.toggleSelect) {
19476             var m = this.isSelected(item) ? 'unselect' : 'select';
19477             //Roo.log(m);
19478             var _t = this;
19479             _t[m](item, true, false);
19480             return true;
19481         }
19482         if(this.multiSelect || this.singleSelect){
19483             if(this.multiSelect && e.shiftKey && this.lastSelection){
19484                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
19485             }else{
19486                 this.select(item, this.multiSelect && e.ctrlKey);
19487                 this.lastSelection = item;
19488             }
19489             
19490             if(!this.tickable){
19491                 e.preventDefault();
19492             }
19493             
19494         }
19495         return true;
19496     },
19497
19498     /**
19499      * Get the number of selected nodes.
19500      * @return {Number}
19501      */
19502     getSelectionCount : function(){
19503         return this.selections.length;
19504     },
19505
19506     /**
19507      * Get the currently selected nodes.
19508      * @return {Array} An array of HTMLElements
19509      */
19510     getSelectedNodes : function(){
19511         return this.selections;
19512     },
19513
19514     /**
19515      * Get the indexes of the selected nodes.
19516      * @return {Array}
19517      */
19518     getSelectedIndexes : function(){
19519         var indexes = [], s = this.selections;
19520         for(var i = 0, len = s.length; i < len; i++){
19521             indexes.push(s[i].nodeIndex);
19522         }
19523         return indexes;
19524     },
19525
19526     /**
19527      * Clear all selections
19528      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
19529      */
19530     clearSelections : function(suppressEvent){
19531         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
19532             this.cmp.elements = this.selections;
19533             this.cmp.removeClass(this.selectedClass);
19534             this.selections = [];
19535             if(!suppressEvent){
19536                 this.fireEvent("selectionchange", this, this.selections);
19537             }
19538         }
19539     },
19540
19541     /**
19542      * Returns true if the passed node is selected
19543      * @param {HTMLElement/Number} node The node or node index
19544      * @return {Boolean}
19545      */
19546     isSelected : function(node){
19547         var s = this.selections;
19548         if(s.length < 1){
19549             return false;
19550         }
19551         node = this.getNode(node);
19552         return s.indexOf(node) !== -1;
19553     },
19554
19555     /**
19556      * Selects nodes.
19557      * @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
19558      * @param {Boolean} keepExisting (optional) true to keep existing selections
19559      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19560      */
19561     select : function(nodeInfo, keepExisting, suppressEvent){
19562         if(nodeInfo instanceof Array){
19563             if(!keepExisting){
19564                 this.clearSelections(true);
19565             }
19566             for(var i = 0, len = nodeInfo.length; i < len; i++){
19567                 this.select(nodeInfo[i], true, true);
19568             }
19569             return;
19570         } 
19571         var node = this.getNode(nodeInfo);
19572         if(!node || this.isSelected(node)){
19573             return; // already selected.
19574         }
19575         if(!keepExisting){
19576             this.clearSelections(true);
19577         }
19578         
19579         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
19580             Roo.fly(node).addClass(this.selectedClass);
19581             this.selections.push(node);
19582             if(!suppressEvent){
19583                 this.fireEvent("selectionchange", this, this.selections);
19584             }
19585         }
19586         
19587         
19588     },
19589       /**
19590      * Unselects nodes.
19591      * @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
19592      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
19593      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
19594      */
19595     unselect : function(nodeInfo, keepExisting, suppressEvent)
19596     {
19597         if(nodeInfo instanceof Array){
19598             Roo.each(this.selections, function(s) {
19599                 this.unselect(s, nodeInfo);
19600             }, this);
19601             return;
19602         }
19603         var node = this.getNode(nodeInfo);
19604         if(!node || !this.isSelected(node)){
19605             //Roo.log("not selected");
19606             return; // not selected.
19607         }
19608         // fireevent???
19609         var ns = [];
19610         Roo.each(this.selections, function(s) {
19611             if (s == node ) {
19612                 Roo.fly(node).removeClass(this.selectedClass);
19613
19614                 return;
19615             }
19616             ns.push(s);
19617         },this);
19618         
19619         this.selections= ns;
19620         this.fireEvent("selectionchange", this, this.selections);
19621     },
19622
19623     /**
19624      * Gets a template node.
19625      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19626      * @return {HTMLElement} The node or null if it wasn't found
19627      */
19628     getNode : function(nodeInfo){
19629         if(typeof nodeInfo == "string"){
19630             return document.getElementById(nodeInfo);
19631         }else if(typeof nodeInfo == "number"){
19632             return this.nodes[nodeInfo];
19633         }
19634         return nodeInfo;
19635     },
19636
19637     /**
19638      * Gets a range template nodes.
19639      * @param {Number} startIndex
19640      * @param {Number} endIndex
19641      * @return {Array} An array of nodes
19642      */
19643     getNodes : function(start, end){
19644         var ns = this.nodes;
19645         start = start || 0;
19646         end = typeof end == "undefined" ? ns.length - 1 : end;
19647         var nodes = [];
19648         if(start <= end){
19649             for(var i = start; i <= end; i++){
19650                 nodes.push(ns[i]);
19651             }
19652         } else{
19653             for(var i = start; i >= end; i--){
19654                 nodes.push(ns[i]);
19655             }
19656         }
19657         return nodes;
19658     },
19659
19660     /**
19661      * Finds the index of the passed node
19662      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19663      * @return {Number} The index of the node or -1
19664      */
19665     indexOf : function(node){
19666         node = this.getNode(node);
19667         if(typeof node.nodeIndex == "number"){
19668             return node.nodeIndex;
19669         }
19670         var ns = this.nodes;
19671         for(var i = 0, len = ns.length; i < len; i++){
19672             if(ns[i] == node){
19673                 return i;
19674             }
19675         }
19676         return -1;
19677     }
19678 });
19679 /*
19680  * - LGPL
19681  *
19682  * based on jquery fullcalendar
19683  * 
19684  */
19685
19686 Roo.bootstrap = Roo.bootstrap || {};
19687 /**
19688  * @class Roo.bootstrap.Calendar
19689  * @extends Roo.bootstrap.Component
19690  * Bootstrap Calendar class
19691  * @cfg {Boolean} loadMask (true|false) default false
19692  * @cfg {Object} header generate the user specific header of the calendar, default false
19693
19694  * @constructor
19695  * Create a new Container
19696  * @param {Object} config The config object
19697  */
19698
19699
19700
19701 Roo.bootstrap.Calendar = function(config){
19702     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19703      this.addEvents({
19704         /**
19705              * @event select
19706              * Fires when a date is selected
19707              * @param {DatePicker} this
19708              * @param {Date} date The selected date
19709              */
19710         'select': true,
19711         /**
19712              * @event monthchange
19713              * Fires when the displayed month changes 
19714              * @param {DatePicker} this
19715              * @param {Date} date The selected month
19716              */
19717         'monthchange': true,
19718         /**
19719              * @event evententer
19720              * Fires when mouse over an event
19721              * @param {Calendar} this
19722              * @param {event} Event
19723              */
19724         'evententer': true,
19725         /**
19726              * @event eventleave
19727              * Fires when the mouse leaves an
19728              * @param {Calendar} this
19729              * @param {event}
19730              */
19731         'eventleave': true,
19732         /**
19733              * @event eventclick
19734              * Fires when the mouse click an
19735              * @param {Calendar} this
19736              * @param {event}
19737              */
19738         'eventclick': true
19739         
19740     });
19741
19742 };
19743
19744 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19745     
19746      /**
19747      * @cfg {Number} startDay
19748      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19749      */
19750     startDay : 0,
19751     
19752     loadMask : false,
19753     
19754     header : false,
19755       
19756     getAutoCreate : function(){
19757         
19758         
19759         var fc_button = function(name, corner, style, content ) {
19760             return Roo.apply({},{
19761                 tag : 'span',
19762                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19763                          (corner.length ?
19764                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19765                             ''
19766                         ),
19767                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19768                 unselectable: 'on'
19769             });
19770         };
19771         
19772         var header = {};
19773         
19774         if(!this.header){
19775             header = {
19776                 tag : 'table',
19777                 cls : 'fc-header',
19778                 style : 'width:100%',
19779                 cn : [
19780                     {
19781                         tag: 'tr',
19782                         cn : [
19783                             {
19784                                 tag : 'td',
19785                                 cls : 'fc-header-left',
19786                                 cn : [
19787                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19788                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19789                                     { tag: 'span', cls: 'fc-header-space' },
19790                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19791
19792
19793                                 ]
19794                             },
19795
19796                             {
19797                                 tag : 'td',
19798                                 cls : 'fc-header-center',
19799                                 cn : [
19800                                     {
19801                                         tag: 'span',
19802                                         cls: 'fc-header-title',
19803                                         cn : {
19804                                             tag: 'H2',
19805                                             html : 'month / year'
19806                                         }
19807                                     }
19808
19809                                 ]
19810                             },
19811                             {
19812                                 tag : 'td',
19813                                 cls : 'fc-header-right',
19814                                 cn : [
19815                               /*      fc_button('month', 'left', '', 'month' ),
19816                                     fc_button('week', '', '', 'week' ),
19817                                     fc_button('day', 'right', '', 'day' )
19818                                 */    
19819
19820                                 ]
19821                             }
19822
19823                         ]
19824                     }
19825                 ]
19826             };
19827         }
19828         
19829         header = this.header;
19830         
19831        
19832         var cal_heads = function() {
19833             var ret = [];
19834             // fixme - handle this.
19835             
19836             for (var i =0; i < Date.dayNames.length; i++) {
19837                 var d = Date.dayNames[i];
19838                 ret.push({
19839                     tag: 'th',
19840                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19841                     html : d.substring(0,3)
19842                 });
19843                 
19844             }
19845             ret[0].cls += ' fc-first';
19846             ret[6].cls += ' fc-last';
19847             return ret;
19848         };
19849         var cal_cell = function(n) {
19850             return  {
19851                 tag: 'td',
19852                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19853                 cn : [
19854                     {
19855                         cn : [
19856                             {
19857                                 cls: 'fc-day-number',
19858                                 html: 'D'
19859                             },
19860                             {
19861                                 cls: 'fc-day-content',
19862                              
19863                                 cn : [
19864                                      {
19865                                         style: 'position: relative;' // height: 17px;
19866                                     }
19867                                 ]
19868                             }
19869                             
19870                             
19871                         ]
19872                     }
19873                 ]
19874                 
19875             }
19876         };
19877         var cal_rows = function() {
19878             
19879             var ret = [];
19880             for (var r = 0; r < 6; r++) {
19881                 var row= {
19882                     tag : 'tr',
19883                     cls : 'fc-week',
19884                     cn : []
19885                 };
19886                 
19887                 for (var i =0; i < Date.dayNames.length; i++) {
19888                     var d = Date.dayNames[i];
19889                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19890
19891                 }
19892                 row.cn[0].cls+=' fc-first';
19893                 row.cn[0].cn[0].style = 'min-height:90px';
19894                 row.cn[6].cls+=' fc-last';
19895                 ret.push(row);
19896                 
19897             }
19898             ret[0].cls += ' fc-first';
19899             ret[4].cls += ' fc-prev-last';
19900             ret[5].cls += ' fc-last';
19901             return ret;
19902             
19903         };
19904         
19905         var cal_table = {
19906             tag: 'table',
19907             cls: 'fc-border-separate',
19908             style : 'width:100%',
19909             cellspacing  : 0,
19910             cn : [
19911                 { 
19912                     tag: 'thead',
19913                     cn : [
19914                         { 
19915                             tag: 'tr',
19916                             cls : 'fc-first fc-last',
19917                             cn : cal_heads()
19918                         }
19919                     ]
19920                 },
19921                 { 
19922                     tag: 'tbody',
19923                     cn : cal_rows()
19924                 }
19925                   
19926             ]
19927         };
19928          
19929          var cfg = {
19930             cls : 'fc fc-ltr',
19931             cn : [
19932                 header,
19933                 {
19934                     cls : 'fc-content',
19935                     style : "position: relative;",
19936                     cn : [
19937                         {
19938                             cls : 'fc-view fc-view-month fc-grid',
19939                             style : 'position: relative',
19940                             unselectable : 'on',
19941                             cn : [
19942                                 {
19943                                     cls : 'fc-event-container',
19944                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19945                                 },
19946                                 cal_table
19947                             ]
19948                         }
19949                     ]
19950     
19951                 }
19952            ] 
19953             
19954         };
19955         
19956          
19957         
19958         return cfg;
19959     },
19960     
19961     
19962     initEvents : function()
19963     {
19964         if(!this.store){
19965             throw "can not find store for calendar";
19966         }
19967         
19968         var mark = {
19969             tag: "div",
19970             cls:"x-dlg-mask",
19971             style: "text-align:center",
19972             cn: [
19973                 {
19974                     tag: "div",
19975                     style: "background-color:white;width:50%;margin:250 auto",
19976                     cn: [
19977                         {
19978                             tag: "img",
19979                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19980                         },
19981                         {
19982                             tag: "span",
19983                             html: "Loading"
19984                         }
19985                         
19986                     ]
19987                 }
19988             ]
19989         };
19990         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19991         
19992         var size = this.el.select('.fc-content', true).first().getSize();
19993         this.maskEl.setSize(size.width, size.height);
19994         this.maskEl.enableDisplayMode("block");
19995         if(!this.loadMask){
19996             this.maskEl.hide();
19997         }
19998         
19999         this.store = Roo.factory(this.store, Roo.data);
20000         this.store.on('load', this.onLoad, this);
20001         this.store.on('beforeload', this.onBeforeLoad, this);
20002         
20003         this.resize();
20004         
20005         this.cells = this.el.select('.fc-day',true);
20006         //Roo.log(this.cells);
20007         this.textNodes = this.el.query('.fc-day-number');
20008         this.cells.addClassOnOver('fc-state-hover');
20009         
20010         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
20011         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
20012         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
20013         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
20014         
20015         this.on('monthchange', this.onMonthChange, this);
20016         
20017         this.update(new Date().clearTime());
20018     },
20019     
20020     resize : function() {
20021         var sz  = this.el.getSize();
20022         
20023         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
20024         this.el.select('.fc-day-content div',true).setHeight(34);
20025     },
20026     
20027     
20028     // private
20029     showPrevMonth : function(e){
20030         this.update(this.activeDate.add("mo", -1));
20031     },
20032     showToday : function(e){
20033         this.update(new Date().clearTime());
20034     },
20035     // private
20036     showNextMonth : function(e){
20037         this.update(this.activeDate.add("mo", 1));
20038     },
20039
20040     // private
20041     showPrevYear : function(){
20042         this.update(this.activeDate.add("y", -1));
20043     },
20044
20045     // private
20046     showNextYear : function(){
20047         this.update(this.activeDate.add("y", 1));
20048     },
20049
20050     
20051    // private
20052     update : function(date)
20053     {
20054         var vd = this.activeDate;
20055         this.activeDate = date;
20056 //        if(vd && this.el){
20057 //            var t = date.getTime();
20058 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
20059 //                Roo.log('using add remove');
20060 //                
20061 //                this.fireEvent('monthchange', this, date);
20062 //                
20063 //                this.cells.removeClass("fc-state-highlight");
20064 //                this.cells.each(function(c){
20065 //                   if(c.dateValue == t){
20066 //                       c.addClass("fc-state-highlight");
20067 //                       setTimeout(function(){
20068 //                            try{c.dom.firstChild.focus();}catch(e){}
20069 //                       }, 50);
20070 //                       return false;
20071 //                   }
20072 //                   return true;
20073 //                });
20074 //                return;
20075 //            }
20076 //        }
20077         
20078         var days = date.getDaysInMonth();
20079         
20080         var firstOfMonth = date.getFirstDateOfMonth();
20081         var startingPos = firstOfMonth.getDay()-this.startDay;
20082         
20083         if(startingPos < this.startDay){
20084             startingPos += 7;
20085         }
20086         
20087         var pm = date.add(Date.MONTH, -1);
20088         var prevStart = pm.getDaysInMonth()-startingPos;
20089 //        
20090         this.cells = this.el.select('.fc-day',true);
20091         this.textNodes = this.el.query('.fc-day-number');
20092         this.cells.addClassOnOver('fc-state-hover');
20093         
20094         var cells = this.cells.elements;
20095         var textEls = this.textNodes;
20096         
20097         Roo.each(cells, function(cell){
20098             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
20099         });
20100         
20101         days += startingPos;
20102
20103         // convert everything to numbers so it's fast
20104         var day = 86400000;
20105         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
20106         //Roo.log(d);
20107         //Roo.log(pm);
20108         //Roo.log(prevStart);
20109         
20110         var today = new Date().clearTime().getTime();
20111         var sel = date.clearTime().getTime();
20112         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
20113         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
20114         var ddMatch = this.disabledDatesRE;
20115         var ddText = this.disabledDatesText;
20116         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
20117         var ddaysText = this.disabledDaysText;
20118         var format = this.format;
20119         
20120         var setCellClass = function(cal, cell){
20121             cell.row = 0;
20122             cell.events = [];
20123             cell.more = [];
20124             //Roo.log('set Cell Class');
20125             cell.title = "";
20126             var t = d.getTime();
20127             
20128             //Roo.log(d);
20129             
20130             cell.dateValue = t;
20131             if(t == today){
20132                 cell.className += " fc-today";
20133                 cell.className += " fc-state-highlight";
20134                 cell.title = cal.todayText;
20135             }
20136             if(t == sel){
20137                 // disable highlight in other month..
20138                 //cell.className += " fc-state-highlight";
20139                 
20140             }
20141             // disabling
20142             if(t < min) {
20143                 cell.className = " fc-state-disabled";
20144                 cell.title = cal.minText;
20145                 return;
20146             }
20147             if(t > max) {
20148                 cell.className = " fc-state-disabled";
20149                 cell.title = cal.maxText;
20150                 return;
20151             }
20152             if(ddays){
20153                 if(ddays.indexOf(d.getDay()) != -1){
20154                     cell.title = ddaysText;
20155                     cell.className = " fc-state-disabled";
20156                 }
20157             }
20158             if(ddMatch && format){
20159                 var fvalue = d.dateFormat(format);
20160                 if(ddMatch.test(fvalue)){
20161                     cell.title = ddText.replace("%0", fvalue);
20162                     cell.className = " fc-state-disabled";
20163                 }
20164             }
20165             
20166             if (!cell.initialClassName) {
20167                 cell.initialClassName = cell.dom.className;
20168             }
20169             
20170             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
20171         };
20172
20173         var i = 0;
20174         
20175         for(; i < startingPos; i++) {
20176             textEls[i].innerHTML = (++prevStart);
20177             d.setDate(d.getDate()+1);
20178             
20179             cells[i].className = "fc-past fc-other-month";
20180             setCellClass(this, cells[i]);
20181         }
20182         
20183         var intDay = 0;
20184         
20185         for(; i < days; i++){
20186             intDay = i - startingPos + 1;
20187             textEls[i].innerHTML = (intDay);
20188             d.setDate(d.getDate()+1);
20189             
20190             cells[i].className = ''; // "x-date-active";
20191             setCellClass(this, cells[i]);
20192         }
20193         var extraDays = 0;
20194         
20195         for(; i < 42; i++) {
20196             textEls[i].innerHTML = (++extraDays);
20197             d.setDate(d.getDate()+1);
20198             
20199             cells[i].className = "fc-future fc-other-month";
20200             setCellClass(this, cells[i]);
20201         }
20202         
20203         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
20204         
20205         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
20206         
20207         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
20208         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
20209         
20210         if(totalRows != 6){
20211             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
20212             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
20213         }
20214         
20215         this.fireEvent('monthchange', this, date);
20216         
20217         
20218         /*
20219         if(!this.internalRender){
20220             var main = this.el.dom.firstChild;
20221             var w = main.offsetWidth;
20222             this.el.setWidth(w + this.el.getBorderWidth("lr"));
20223             Roo.fly(main).setWidth(w);
20224             this.internalRender = true;
20225             // opera does not respect the auto grow header center column
20226             // then, after it gets a width opera refuses to recalculate
20227             // without a second pass
20228             if(Roo.isOpera && !this.secondPass){
20229                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
20230                 this.secondPass = true;
20231                 this.update.defer(10, this, [date]);
20232             }
20233         }
20234         */
20235         
20236     },
20237     
20238     findCell : function(dt) {
20239         dt = dt.clearTime().getTime();
20240         var ret = false;
20241         this.cells.each(function(c){
20242             //Roo.log("check " +c.dateValue + '?=' + dt);
20243             if(c.dateValue == dt){
20244                 ret = c;
20245                 return false;
20246             }
20247             return true;
20248         });
20249         
20250         return ret;
20251     },
20252     
20253     findCells : function(ev) {
20254         var s = ev.start.clone().clearTime().getTime();
20255        // Roo.log(s);
20256         var e= ev.end.clone().clearTime().getTime();
20257        // Roo.log(e);
20258         var ret = [];
20259         this.cells.each(function(c){
20260              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
20261             
20262             if(c.dateValue > e){
20263                 return ;
20264             }
20265             if(c.dateValue < s){
20266                 return ;
20267             }
20268             ret.push(c);
20269         });
20270         
20271         return ret;    
20272     },
20273     
20274 //    findBestRow: function(cells)
20275 //    {
20276 //        var ret = 0;
20277 //        
20278 //        for (var i =0 ; i < cells.length;i++) {
20279 //            ret  = Math.max(cells[i].rows || 0,ret);
20280 //        }
20281 //        return ret;
20282 //        
20283 //    },
20284     
20285     
20286     addItem : function(ev)
20287     {
20288         // look for vertical location slot in
20289         var cells = this.findCells(ev);
20290         
20291 //        ev.row = this.findBestRow(cells);
20292         
20293         // work out the location.
20294         
20295         var crow = false;
20296         var rows = [];
20297         for(var i =0; i < cells.length; i++) {
20298             
20299             cells[i].row = cells[0].row;
20300             
20301             if(i == 0){
20302                 cells[i].row = cells[i].row + 1;
20303             }
20304             
20305             if (!crow) {
20306                 crow = {
20307                     start : cells[i],
20308                     end :  cells[i]
20309                 };
20310                 continue;
20311             }
20312             if (crow.start.getY() == cells[i].getY()) {
20313                 // on same row.
20314                 crow.end = cells[i];
20315                 continue;
20316             }
20317             // different row.
20318             rows.push(crow);
20319             crow = {
20320                 start: cells[i],
20321                 end : cells[i]
20322             };
20323             
20324         }
20325         
20326         rows.push(crow);
20327         ev.els = [];
20328         ev.rows = rows;
20329         ev.cells = cells;
20330         
20331         cells[0].events.push(ev);
20332         
20333         this.calevents.push(ev);
20334     },
20335     
20336     clearEvents: function() {
20337         
20338         if(!this.calevents){
20339             return;
20340         }
20341         
20342         Roo.each(this.cells.elements, function(c){
20343             c.row = 0;
20344             c.events = [];
20345             c.more = [];
20346         });
20347         
20348         Roo.each(this.calevents, function(e) {
20349             Roo.each(e.els, function(el) {
20350                 el.un('mouseenter' ,this.onEventEnter, this);
20351                 el.un('mouseleave' ,this.onEventLeave, this);
20352                 el.remove();
20353             },this);
20354         },this);
20355         
20356         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
20357             e.remove();
20358         });
20359         
20360     },
20361     
20362     renderEvents: function()
20363     {   
20364         var _this = this;
20365         
20366         this.cells.each(function(c) {
20367             
20368             if(c.row < 5){
20369                 return;
20370             }
20371             
20372             var ev = c.events;
20373             
20374             var r = 4;
20375             if(c.row != c.events.length){
20376                 r = 4 - (4 - (c.row - c.events.length));
20377             }
20378             
20379             c.events = ev.slice(0, r);
20380             c.more = ev.slice(r);
20381             
20382             if(c.more.length && c.more.length == 1){
20383                 c.events.push(c.more.pop());
20384             }
20385             
20386             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
20387             
20388         });
20389             
20390         this.cells.each(function(c) {
20391             
20392             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
20393             
20394             
20395             for (var e = 0; e < c.events.length; e++){
20396                 var ev = c.events[e];
20397                 var rows = ev.rows;
20398                 
20399                 for(var i = 0; i < rows.length; i++) {
20400                 
20401                     // how many rows should it span..
20402
20403                     var  cfg = {
20404                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
20405                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
20406
20407                         unselectable : "on",
20408                         cn : [
20409                             {
20410                                 cls: 'fc-event-inner',
20411                                 cn : [
20412     //                                {
20413     //                                  tag:'span',
20414     //                                  cls: 'fc-event-time',
20415     //                                  html : cells.length > 1 ? '' : ev.time
20416     //                                },
20417                                     {
20418                                       tag:'span',
20419                                       cls: 'fc-event-title',
20420                                       html : String.format('{0}', ev.title)
20421                                     }
20422
20423
20424                                 ]
20425                             },
20426                             {
20427                                 cls: 'ui-resizable-handle ui-resizable-e',
20428                                 html : '&nbsp;&nbsp;&nbsp'
20429                             }
20430
20431                         ]
20432                     };
20433
20434                     if (i == 0) {
20435                         cfg.cls += ' fc-event-start';
20436                     }
20437                     if ((i+1) == rows.length) {
20438                         cfg.cls += ' fc-event-end';
20439                     }
20440
20441                     var ctr = _this.el.select('.fc-event-container',true).first();
20442                     var cg = ctr.createChild(cfg);
20443
20444                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
20445                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
20446
20447                     var r = (c.more.length) ? 1 : 0;
20448                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
20449                     cg.setWidth(ebox.right - sbox.x -2);
20450
20451                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
20452                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
20453                     cg.on('click', _this.onEventClick, _this, ev);
20454
20455                     ev.els.push(cg);
20456                     
20457                 }
20458                 
20459             }
20460             
20461             
20462             if(c.more.length){
20463                 var  cfg = {
20464                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
20465                     style : 'position: absolute',
20466                     unselectable : "on",
20467                     cn : [
20468                         {
20469                             cls: 'fc-event-inner',
20470                             cn : [
20471                                 {
20472                                   tag:'span',
20473                                   cls: 'fc-event-title',
20474                                   html : 'More'
20475                                 }
20476
20477
20478                             ]
20479                         },
20480                         {
20481                             cls: 'ui-resizable-handle ui-resizable-e',
20482                             html : '&nbsp;&nbsp;&nbsp'
20483                         }
20484
20485                     ]
20486                 };
20487
20488                 var ctr = _this.el.select('.fc-event-container',true).first();
20489                 var cg = ctr.createChild(cfg);
20490
20491                 var sbox = c.select('.fc-day-content',true).first().getBox();
20492                 var ebox = c.select('.fc-day-content',true).first().getBox();
20493                 //Roo.log(cg);
20494                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
20495                 cg.setWidth(ebox.right - sbox.x -2);
20496
20497                 cg.on('click', _this.onMoreEventClick, _this, c.more);
20498                 
20499             }
20500             
20501         });
20502         
20503         
20504         
20505     },
20506     
20507     onEventEnter: function (e, el,event,d) {
20508         this.fireEvent('evententer', this, el, event);
20509     },
20510     
20511     onEventLeave: function (e, el,event,d) {
20512         this.fireEvent('eventleave', this, el, event);
20513     },
20514     
20515     onEventClick: function (e, el,event,d) {
20516         this.fireEvent('eventclick', this, el, event);
20517     },
20518     
20519     onMonthChange: function () {
20520         this.store.load();
20521     },
20522     
20523     onMoreEventClick: function(e, el, more)
20524     {
20525         var _this = this;
20526         
20527         this.calpopover.placement = 'right';
20528         this.calpopover.setTitle('More');
20529         
20530         this.calpopover.setContent('');
20531         
20532         var ctr = this.calpopover.el.select('.popover-content', true).first();
20533         
20534         Roo.each(more, function(m){
20535             var cfg = {
20536                 cls : 'fc-event-hori fc-event-draggable',
20537                 html : m.title
20538             };
20539             var cg = ctr.createChild(cfg);
20540             
20541             cg.on('click', _this.onEventClick, _this, m);
20542         });
20543         
20544         this.calpopover.show(el);
20545         
20546         
20547     },
20548     
20549     onLoad: function () 
20550     {   
20551         this.calevents = [];
20552         var cal = this;
20553         
20554         if(this.store.getCount() > 0){
20555             this.store.data.each(function(d){
20556                cal.addItem({
20557                     id : d.data.id,
20558                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
20559                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
20560                     time : d.data.start_time,
20561                     title : d.data.title,
20562                     description : d.data.description,
20563                     venue : d.data.venue
20564                 });
20565             });
20566         }
20567         
20568         this.renderEvents();
20569         
20570         if(this.calevents.length && this.loadMask){
20571             this.maskEl.hide();
20572         }
20573     },
20574     
20575     onBeforeLoad: function()
20576     {
20577         this.clearEvents();
20578         if(this.loadMask){
20579             this.maskEl.show();
20580         }
20581     }
20582 });
20583
20584  
20585  /*
20586  * - LGPL
20587  *
20588  * element
20589  * 
20590  */
20591
20592 /**
20593  * @class Roo.bootstrap.Popover
20594  * @extends Roo.bootstrap.Component
20595  * Bootstrap Popover class
20596  * @cfg {String} html contents of the popover   (or false to use children..)
20597  * @cfg {String} title of popover (or false to hide)
20598  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
20599  * @cfg {String} trigger click || hover (or false to trigger manually)
20600  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20601  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20602  *      - if false and it has a 'parent' then it will be automatically added to that element
20603  *      - if string - Roo.get  will be called 
20604  * @cfg {Number} delay - delay before showing
20605  
20606  * @constructor
20607  * Create a new Popover
20608  * @param {Object} config The config object
20609  */
20610
20611 Roo.bootstrap.Popover = function(config){
20612     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20613     
20614     this.addEvents({
20615         // raw events
20616          /**
20617          * @event show
20618          * After the popover show
20619          * 
20620          * @param {Roo.bootstrap.Popover} this
20621          */
20622         "show" : true,
20623         /**
20624          * @event hide
20625          * After the popover hide
20626          * 
20627          * @param {Roo.bootstrap.Popover} this
20628          */
20629         "hide" : true
20630     });
20631 };
20632
20633 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20634     
20635     title: false,
20636     html: false,
20637     
20638     placement : 'right',
20639     trigger : 'hover', // hover
20640     modal : false,
20641     delay : 0,
20642     
20643     over: false,
20644     
20645     can_build_overlaid : false,
20646     
20647     maskEl : false, // the mask element
20648     headerEl : false,
20649     contentEl : false,
20650     alignEl : false, // when show is called with an element - this get's stored.
20651     
20652     getChildContainer : function()
20653     {
20654         return this.contentEl;
20655         
20656     },
20657     getPopoverHeader : function()
20658     {
20659         this.title = true; // flag not to hide it..
20660         this.headerEl.addClass('p-0');
20661         return this.headerEl
20662     },
20663     
20664     
20665     getAutoCreate : function(){
20666          
20667         var cfg = {
20668            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20669            style: 'display:block',
20670            cn : [
20671                 {
20672                     cls : 'arrow'
20673                 },
20674                 {
20675                     cls : 'popover-inner ',
20676                     cn : [
20677                         {
20678                             tag: 'h3',
20679                             cls: 'popover-title popover-header',
20680                             html : this.title === false ? '' : this.title
20681                         },
20682                         {
20683                             cls : 'popover-content popover-body '  + (this.cls || ''),
20684                             html : this.html || ''
20685                         }
20686                     ]
20687                     
20688                 }
20689            ]
20690         };
20691         
20692         return cfg;
20693     },
20694     /**
20695      * @param {string} the title
20696      */
20697     setTitle: function(str)
20698     {
20699         this.title = str;
20700         if (this.el) {
20701             this.headerEl.dom.innerHTML = str;
20702         }
20703         
20704     },
20705     /**
20706      * @param {string} the body content
20707      */
20708     setContent: function(str)
20709     {
20710         this.html = str;
20711         if (this.contentEl) {
20712             this.contentEl.dom.innerHTML = str;
20713         }
20714         
20715     },
20716     // as it get's added to the bottom of the page.
20717     onRender : function(ct, position)
20718     {
20719         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20720         
20721         
20722         
20723         if(!this.el){
20724             var cfg = Roo.apply({},  this.getAutoCreate());
20725             cfg.id = Roo.id();
20726             
20727             if (this.cls) {
20728                 cfg.cls += ' ' + this.cls;
20729             }
20730             if (this.style) {
20731                 cfg.style = this.style;
20732             }
20733             //Roo.log("adding to ");
20734             this.el = Roo.get(document.body).createChild(cfg, position);
20735 //            Roo.log(this.el);
20736         }
20737         
20738         this.contentEl = this.el.select('.popover-content',true).first();
20739         this.headerEl =  this.el.select('.popover-title',true).first();
20740         
20741         var nitems = [];
20742         if(typeof(this.items) != 'undefined'){
20743             var items = this.items;
20744             delete this.items;
20745
20746             for(var i =0;i < items.length;i++) {
20747                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20748             }
20749         }
20750
20751         this.items = nitems;
20752         
20753         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20754         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20755         
20756         
20757         
20758         this.initEvents();
20759     },
20760     
20761     resizeMask : function()
20762     {
20763         this.maskEl.setSize(
20764             Roo.lib.Dom.getViewWidth(true),
20765             Roo.lib.Dom.getViewHeight(true)
20766         );
20767     },
20768     
20769     initEvents : function()
20770     {
20771         
20772         if (!this.modal) { 
20773             Roo.bootstrap.Popover.register(this);
20774         }
20775          
20776         this.arrowEl = this.el.select('.arrow',true).first();
20777         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20778         this.el.enableDisplayMode('block');
20779         this.el.hide();
20780  
20781         
20782         if (this.over === false && !this.parent()) {
20783             return; 
20784         }
20785         if (this.triggers === false) {
20786             return;
20787         }
20788          
20789         // support parent
20790         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20791         var triggers = this.trigger ? this.trigger.split(' ') : [];
20792         Roo.each(triggers, function(trigger) {
20793         
20794             if (trigger == 'click') {
20795                 on_el.on('click', this.toggle, this);
20796             } else if (trigger != 'manual') {
20797                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20798                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20799       
20800                 on_el.on(eventIn  ,this.enter, this);
20801                 on_el.on(eventOut, this.leave, this);
20802             }
20803         }, this);
20804     },
20805     
20806     
20807     // private
20808     timeout : null,
20809     hoverState : null,
20810     
20811     toggle : function () {
20812         this.hoverState == 'in' ? this.leave() : this.enter();
20813     },
20814     
20815     enter : function () {
20816         
20817         clearTimeout(this.timeout);
20818     
20819         this.hoverState = 'in';
20820     
20821         if (!this.delay || !this.delay.show) {
20822             this.show();
20823             return;
20824         }
20825         var _t = this;
20826         this.timeout = setTimeout(function () {
20827             if (_t.hoverState == 'in') {
20828                 _t.show();
20829             }
20830         }, this.delay.show)
20831     },
20832     
20833     leave : function() {
20834         clearTimeout(this.timeout);
20835     
20836         this.hoverState = 'out';
20837     
20838         if (!this.delay || !this.delay.hide) {
20839             this.hide();
20840             return;
20841         }
20842         var _t = this;
20843         this.timeout = setTimeout(function () {
20844             if (_t.hoverState == 'out') {
20845                 _t.hide();
20846             }
20847         }, this.delay.hide)
20848     },
20849     /**
20850      * Show the popover
20851      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20852      * @param {string} (left|right|top|bottom) position
20853      */
20854     show : function (on_el, placement)
20855     {
20856         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20857         on_el = on_el || false; // default to false
20858          
20859         if (!on_el) {
20860             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20861                 on_el = this.parent().el;
20862             } else if (this.over) {
20863                 on_el = Roo.get(this.over);
20864             }
20865             
20866         }
20867         
20868         this.alignEl = Roo.get( on_el );
20869
20870         if (!this.el) {
20871             this.render(document.body);
20872         }
20873         
20874         
20875          
20876         
20877         if (this.title === false) {
20878             this.headerEl.hide();
20879         }
20880         
20881        
20882         this.el.show();
20883         this.el.dom.style.display = 'block';
20884          
20885  
20886         if (this.alignEl) {
20887             this.updatePosition(this.placement, true);
20888              
20889         } else {
20890             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20891             var es = this.el.getSize();
20892             var x = Roo.lib.Dom.getViewWidth()/2;
20893             var y = Roo.lib.Dom.getViewHeight()/2;
20894             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20895             
20896         }
20897
20898         
20899         //var arrow = this.el.select('.arrow',true).first();
20900         //arrow.set(align[2], 
20901         
20902         this.el.addClass('in');
20903         
20904          
20905         
20906         this.hoverState = 'in';
20907         
20908         if (this.modal) {
20909             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20910             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20911             this.maskEl.dom.style.display = 'block';
20912             this.maskEl.addClass('show');
20913         }
20914         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20915  
20916         this.fireEvent('show', this);
20917         
20918     },
20919     /**
20920      * fire this manually after loading a grid in the table for example
20921      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20922      * @param {Boolean} try and move it if we cant get right position.
20923      */
20924     updatePosition : function(placement, try_move)
20925     {
20926         // allow for calling with no parameters
20927         placement = placement   ? placement :  this.placement;
20928         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20929         
20930         this.el.removeClass([
20931             'fade','top','bottom', 'left', 'right','in',
20932             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20933         ]);
20934         this.el.addClass(placement + ' bs-popover-' + placement);
20935         
20936         if (!this.alignEl ) {
20937             return false;
20938         }
20939         
20940         switch (placement) {
20941             case 'right':
20942                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20943                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20944                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20945                     //normal display... or moved up/down.
20946                     this.el.setXY(offset);
20947                     var xy = this.alignEl.getAnchorXY('tr', false);
20948                     xy[0]+=2;xy[1]+=5;
20949                     this.arrowEl.setXY(xy);
20950                     return true;
20951                 }
20952                 // continue through...
20953                 return this.updatePosition('left', false);
20954                 
20955             
20956             case 'left':
20957                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20958                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20959                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20960                     //normal display... or moved up/down.
20961                     this.el.setXY(offset);
20962                     var xy = this.alignEl.getAnchorXY('tl', false);
20963                     xy[0]-=10;xy[1]+=5; // << fix me
20964                     this.arrowEl.setXY(xy);
20965                     return true;
20966                 }
20967                 // call self...
20968                 return this.updatePosition('right', false);
20969             
20970             case 'top':
20971                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20972                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20973                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20974                     //normal display... or moved up/down.
20975                     this.el.setXY(offset);
20976                     var xy = this.alignEl.getAnchorXY('t', false);
20977                     xy[1]-=10; // << fix me
20978                     this.arrowEl.setXY(xy);
20979                     return true;
20980                 }
20981                 // fall through
20982                return this.updatePosition('bottom', false);
20983             
20984             case 'bottom':
20985                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20986                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20987                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20988                     //normal display... or moved up/down.
20989                     this.el.setXY(offset);
20990                     var xy = this.alignEl.getAnchorXY('b', false);
20991                      xy[1]+=2; // << fix me
20992                     this.arrowEl.setXY(xy);
20993                     return true;
20994                 }
20995                 // fall through
20996                 return this.updatePosition('top', false);
20997                 
20998             
20999         }
21000         
21001         
21002         return false;
21003     },
21004     
21005     hide : function()
21006     {
21007         this.el.setXY([0,0]);
21008         this.el.removeClass('in');
21009         this.el.hide();
21010         this.hoverState = null;
21011         this.maskEl.hide(); // always..
21012         this.fireEvent('hide', this);
21013     }
21014     
21015 });
21016
21017
21018 Roo.apply(Roo.bootstrap.Popover, {
21019
21020     alignment : {
21021         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
21022         'right' : ['l-br', [10,0], 'right bs-popover-right'],
21023         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
21024         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
21025     },
21026     
21027     zIndex : 20001,
21028
21029     clickHander : false,
21030     
21031     
21032
21033     onMouseDown : function(e)
21034     {
21035         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
21036             /// what is nothing is showing..
21037             this.hideAll();
21038         }
21039          
21040     },
21041     
21042     
21043     popups : [],
21044     
21045     register : function(popup)
21046     {
21047         if (!Roo.bootstrap.Popover.clickHandler) {
21048             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
21049         }
21050         // hide other popups.
21051         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
21052         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
21053         this.hideAll(); //<< why?
21054         //this.popups.push(popup);
21055     },
21056     hideAll : function()
21057     {
21058         this.popups.forEach(function(p) {
21059             p.hide();
21060         });
21061     },
21062     onShow : function() {
21063         Roo.bootstrap.Popover.popups.push(this);
21064     },
21065     onHide : function() {
21066         Roo.bootstrap.Popover.popups.remove(this);
21067     } 
21068
21069 });/*
21070  * - LGPL
21071  *
21072  * Card header - holder for the card header elements.
21073  * 
21074  */
21075
21076 /**
21077  * @class Roo.bootstrap.PopoverNav
21078  * @extends Roo.bootstrap.NavGroup
21079  * Bootstrap Popover header navigation class
21080  * @constructor
21081  * Create a new Popover Header Navigation 
21082  * @param {Object} config The config object
21083  */
21084
21085 Roo.bootstrap.PopoverNav = function(config){
21086     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
21087 };
21088
21089 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
21090     
21091     
21092     container_method : 'getPopoverHeader' 
21093     
21094      
21095     
21096     
21097    
21098 });
21099
21100  
21101
21102  /*
21103  * - LGPL
21104  *
21105  * Progress
21106  * 
21107  */
21108
21109 /**
21110  * @class Roo.bootstrap.Progress
21111  * @extends Roo.bootstrap.Component
21112  * Bootstrap Progress class
21113  * @cfg {Boolean} striped striped of the progress bar
21114  * @cfg {Boolean} active animated of the progress bar
21115  * 
21116  * 
21117  * @constructor
21118  * Create a new Progress
21119  * @param {Object} config The config object
21120  */
21121
21122 Roo.bootstrap.Progress = function(config){
21123     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
21124 };
21125
21126 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
21127     
21128     striped : false,
21129     active: false,
21130     
21131     getAutoCreate : function(){
21132         var cfg = {
21133             tag: 'div',
21134             cls: 'progress'
21135         };
21136         
21137         
21138         if(this.striped){
21139             cfg.cls += ' progress-striped';
21140         }
21141       
21142         if(this.active){
21143             cfg.cls += ' active';
21144         }
21145         
21146         
21147         return cfg;
21148     }
21149    
21150 });
21151
21152  
21153
21154  /*
21155  * - LGPL
21156  *
21157  * ProgressBar
21158  * 
21159  */
21160
21161 /**
21162  * @class Roo.bootstrap.ProgressBar
21163  * @extends Roo.bootstrap.Component
21164  * Bootstrap ProgressBar class
21165  * @cfg {Number} aria_valuenow aria-value now
21166  * @cfg {Number} aria_valuemin aria-value min
21167  * @cfg {Number} aria_valuemax aria-value max
21168  * @cfg {String} label label for the progress bar
21169  * @cfg {String} panel (success | info | warning | danger )
21170  * @cfg {String} role role of the progress bar
21171  * @cfg {String} sr_only text
21172  * 
21173  * 
21174  * @constructor
21175  * Create a new ProgressBar
21176  * @param {Object} config The config object
21177  */
21178
21179 Roo.bootstrap.ProgressBar = function(config){
21180     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
21181 };
21182
21183 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
21184     
21185     aria_valuenow : 0,
21186     aria_valuemin : 0,
21187     aria_valuemax : 100,
21188     label : false,
21189     panel : false,
21190     role : false,
21191     sr_only: false,
21192     
21193     getAutoCreate : function()
21194     {
21195         
21196         var cfg = {
21197             tag: 'div',
21198             cls: 'progress-bar',
21199             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
21200         };
21201         
21202         if(this.sr_only){
21203             cfg.cn = {
21204                 tag: 'span',
21205                 cls: 'sr-only',
21206                 html: this.sr_only
21207             }
21208         }
21209         
21210         if(this.role){
21211             cfg.role = this.role;
21212         }
21213         
21214         if(this.aria_valuenow){
21215             cfg['aria-valuenow'] = this.aria_valuenow;
21216         }
21217         
21218         if(this.aria_valuemin){
21219             cfg['aria-valuemin'] = this.aria_valuemin;
21220         }
21221         
21222         if(this.aria_valuemax){
21223             cfg['aria-valuemax'] = this.aria_valuemax;
21224         }
21225         
21226         if(this.label && !this.sr_only){
21227             cfg.html = this.label;
21228         }
21229         
21230         if(this.panel){
21231             cfg.cls += ' progress-bar-' + this.panel;
21232         }
21233         
21234         return cfg;
21235     },
21236     
21237     update : function(aria_valuenow)
21238     {
21239         this.aria_valuenow = aria_valuenow;
21240         
21241         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
21242     }
21243    
21244 });
21245
21246  
21247
21248  /*
21249  * - LGPL
21250  *
21251  * column
21252  * 
21253  */
21254
21255 /**
21256  * @class Roo.bootstrap.TabGroup
21257  * @extends Roo.bootstrap.Column
21258  * Bootstrap Column class
21259  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
21260  * @cfg {Boolean} carousel true to make the group behave like a carousel
21261  * @cfg {Boolean} bullets show bullets for the panels
21262  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
21263  * @cfg {Number} timer auto slide timer .. default 0 millisecond
21264  * @cfg {Boolean} showarrow (true|false) show arrow default true
21265  * 
21266  * @constructor
21267  * Create a new TabGroup
21268  * @param {Object} config The config object
21269  */
21270
21271 Roo.bootstrap.TabGroup = function(config){
21272     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
21273     if (!this.navId) {
21274         this.navId = Roo.id();
21275     }
21276     this.tabs = [];
21277     Roo.bootstrap.TabGroup.register(this);
21278     
21279 };
21280
21281 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
21282     
21283     carousel : false,
21284     transition : false,
21285     bullets : 0,
21286     timer : 0,
21287     autoslide : false,
21288     slideFn : false,
21289     slideOnTouch : false,
21290     showarrow : true,
21291     
21292     getAutoCreate : function()
21293     {
21294         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
21295         
21296         cfg.cls += ' tab-content';
21297         
21298         if (this.carousel) {
21299             cfg.cls += ' carousel slide';
21300             
21301             cfg.cn = [{
21302                cls : 'carousel-inner',
21303                cn : []
21304             }];
21305         
21306             if(this.bullets  && !Roo.isTouch){
21307                 
21308                 var bullets = {
21309                     cls : 'carousel-bullets',
21310                     cn : []
21311                 };
21312                
21313                 if(this.bullets_cls){
21314                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
21315                 }
21316                 
21317                 bullets.cn.push({
21318                     cls : 'clear'
21319                 });
21320                 
21321                 cfg.cn[0].cn.push(bullets);
21322             }
21323             
21324             if(this.showarrow){
21325                 cfg.cn[0].cn.push({
21326                     tag : 'div',
21327                     class : 'carousel-arrow',
21328                     cn : [
21329                         {
21330                             tag : 'div',
21331                             class : 'carousel-prev',
21332                             cn : [
21333                                 {
21334                                     tag : 'i',
21335                                     class : 'fa fa-chevron-left'
21336                                 }
21337                             ]
21338                         },
21339                         {
21340                             tag : 'div',
21341                             class : 'carousel-next',
21342                             cn : [
21343                                 {
21344                                     tag : 'i',
21345                                     class : 'fa fa-chevron-right'
21346                                 }
21347                             ]
21348                         }
21349                     ]
21350                 });
21351             }
21352             
21353         }
21354         
21355         return cfg;
21356     },
21357     
21358     initEvents:  function()
21359     {
21360 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
21361 //            this.el.on("touchstart", this.onTouchStart, this);
21362 //        }
21363         
21364         if(this.autoslide){
21365             var _this = this;
21366             
21367             this.slideFn = window.setInterval(function() {
21368                 _this.showPanelNext();
21369             }, this.timer);
21370         }
21371         
21372         if(this.showarrow){
21373             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
21374             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
21375         }
21376         
21377         
21378     },
21379     
21380 //    onTouchStart : function(e, el, o)
21381 //    {
21382 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
21383 //            return;
21384 //        }
21385 //        
21386 //        this.showPanelNext();
21387 //    },
21388     
21389     
21390     getChildContainer : function()
21391     {
21392         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
21393     },
21394     
21395     /**
21396     * register a Navigation item
21397     * @param {Roo.bootstrap.NavItem} the navitem to add
21398     */
21399     register : function(item)
21400     {
21401         this.tabs.push( item);
21402         item.navId = this.navId; // not really needed..
21403         this.addBullet();
21404     
21405     },
21406     
21407     getActivePanel : function()
21408     {
21409         var r = false;
21410         Roo.each(this.tabs, function(t) {
21411             if (t.active) {
21412                 r = t;
21413                 return false;
21414             }
21415             return null;
21416         });
21417         return r;
21418         
21419     },
21420     getPanelByName : function(n)
21421     {
21422         var r = false;
21423         Roo.each(this.tabs, function(t) {
21424             if (t.tabId == n) {
21425                 r = t;
21426                 return false;
21427             }
21428             return null;
21429         });
21430         return r;
21431     },
21432     indexOfPanel : function(p)
21433     {
21434         var r = false;
21435         Roo.each(this.tabs, function(t,i) {
21436             if (t.tabId == p.tabId) {
21437                 r = i;
21438                 return false;
21439             }
21440             return null;
21441         });
21442         return r;
21443     },
21444     /**
21445      * show a specific panel
21446      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
21447      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
21448      */
21449     showPanel : function (pan)
21450     {
21451         if(this.transition || typeof(pan) == 'undefined'){
21452             Roo.log("waiting for the transitionend");
21453             return false;
21454         }
21455         
21456         if (typeof(pan) == 'number') {
21457             pan = this.tabs[pan];
21458         }
21459         
21460         if (typeof(pan) == 'string') {
21461             pan = this.getPanelByName(pan);
21462         }
21463         
21464         var cur = this.getActivePanel();
21465         
21466         if(!pan || !cur){
21467             Roo.log('pan or acitve pan is undefined');
21468             return false;
21469         }
21470         
21471         if (pan.tabId == this.getActivePanel().tabId) {
21472             return true;
21473         }
21474         
21475         if (false === cur.fireEvent('beforedeactivate')) {
21476             return false;
21477         }
21478         
21479         if(this.bullets > 0 && !Roo.isTouch){
21480             this.setActiveBullet(this.indexOfPanel(pan));
21481         }
21482         
21483         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
21484             
21485             //class="carousel-item carousel-item-next carousel-item-left"
21486             
21487             this.transition = true;
21488             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
21489             var lr = dir == 'next' ? 'left' : 'right';
21490             pan.el.addClass(dir); // or prev
21491             pan.el.addClass('carousel-item-' + dir); // or prev
21492             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
21493             cur.el.addClass(lr); // or right
21494             pan.el.addClass(lr);
21495             cur.el.addClass('carousel-item-' +lr); // or right
21496             pan.el.addClass('carousel-item-' +lr);
21497             
21498             
21499             var _this = this;
21500             cur.el.on('transitionend', function() {
21501                 Roo.log("trans end?");
21502                 
21503                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
21504                 pan.setActive(true);
21505                 
21506                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
21507                 cur.setActive(false);
21508                 
21509                 _this.transition = false;
21510                 
21511             }, this, { single:  true } );
21512             
21513             return true;
21514         }
21515         
21516         cur.setActive(false);
21517         pan.setActive(true);
21518         
21519         return true;
21520         
21521     },
21522     showPanelNext : function()
21523     {
21524         var i = this.indexOfPanel(this.getActivePanel());
21525         
21526         if (i >= this.tabs.length - 1 && !this.autoslide) {
21527             return;
21528         }
21529         
21530         if (i >= this.tabs.length - 1 && this.autoslide) {
21531             i = -1;
21532         }
21533         
21534         this.showPanel(this.tabs[i+1]);
21535     },
21536     
21537     showPanelPrev : function()
21538     {
21539         var i = this.indexOfPanel(this.getActivePanel());
21540         
21541         if (i  < 1 && !this.autoslide) {
21542             return;
21543         }
21544         
21545         if (i < 1 && this.autoslide) {
21546             i = this.tabs.length;
21547         }
21548         
21549         this.showPanel(this.tabs[i-1]);
21550     },
21551     
21552     
21553     addBullet: function()
21554     {
21555         if(!this.bullets || Roo.isTouch){
21556             return;
21557         }
21558         var ctr = this.el.select('.carousel-bullets',true).first();
21559         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
21560         var bullet = ctr.createChild({
21561             cls : 'bullet bullet-' + i
21562         },ctr.dom.lastChild);
21563         
21564         
21565         var _this = this;
21566         
21567         bullet.on('click', (function(e, el, o, ii, t){
21568
21569             e.preventDefault();
21570
21571             this.showPanel(ii);
21572
21573             if(this.autoslide && this.slideFn){
21574                 clearInterval(this.slideFn);
21575                 this.slideFn = window.setInterval(function() {
21576                     _this.showPanelNext();
21577                 }, this.timer);
21578             }
21579
21580         }).createDelegate(this, [i, bullet], true));
21581                 
21582         
21583     },
21584      
21585     setActiveBullet : function(i)
21586     {
21587         if(Roo.isTouch){
21588             return;
21589         }
21590         
21591         Roo.each(this.el.select('.bullet', true).elements, function(el){
21592             el.removeClass('selected');
21593         });
21594
21595         var bullet = this.el.select('.bullet-' + i, true).first();
21596         
21597         if(!bullet){
21598             return;
21599         }
21600         
21601         bullet.addClass('selected');
21602     }
21603     
21604     
21605   
21606 });
21607
21608  
21609
21610  
21611  
21612 Roo.apply(Roo.bootstrap.TabGroup, {
21613     
21614     groups: {},
21615      /**
21616     * register a Navigation Group
21617     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21618     */
21619     register : function(navgrp)
21620     {
21621         this.groups[navgrp.navId] = navgrp;
21622         
21623     },
21624     /**
21625     * fetch a Navigation Group based on the navigation ID
21626     * if one does not exist , it will get created.
21627     * @param {string} the navgroup to add
21628     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21629     */
21630     get: function(navId) {
21631         if (typeof(this.groups[navId]) == 'undefined') {
21632             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21633         }
21634         return this.groups[navId] ;
21635     }
21636     
21637     
21638     
21639 });
21640
21641  /*
21642  * - LGPL
21643  *
21644  * TabPanel
21645  * 
21646  */
21647
21648 /**
21649  * @class Roo.bootstrap.TabPanel
21650  * @extends Roo.bootstrap.Component
21651  * Bootstrap TabPanel class
21652  * @cfg {Boolean} active panel active
21653  * @cfg {String} html panel content
21654  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21655  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21656  * @cfg {String} href click to link..
21657  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21658  * 
21659  * 
21660  * @constructor
21661  * Create a new TabPanel
21662  * @param {Object} config The config object
21663  */
21664
21665 Roo.bootstrap.TabPanel = function(config){
21666     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21667     this.addEvents({
21668         /**
21669              * @event changed
21670              * Fires when the active status changes
21671              * @param {Roo.bootstrap.TabPanel} this
21672              * @param {Boolean} state the new state
21673             
21674          */
21675         'changed': true,
21676         /**
21677              * @event beforedeactivate
21678              * Fires before a tab is de-activated - can be used to do validation on a form.
21679              * @param {Roo.bootstrap.TabPanel} this
21680              * @return {Boolean} false if there is an error
21681             
21682          */
21683         'beforedeactivate': true
21684      });
21685     
21686     this.tabId = this.tabId || Roo.id();
21687   
21688 };
21689
21690 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21691     
21692     active: false,
21693     html: false,
21694     tabId: false,
21695     navId : false,
21696     href : '',
21697     touchSlide : false,
21698     getAutoCreate : function(){
21699         
21700         
21701         var cfg = {
21702             tag: 'div',
21703             // item is needed for carousel - not sure if it has any effect otherwise
21704             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21705             html: this.html || ''
21706         };
21707         
21708         if(this.active){
21709             cfg.cls += ' active';
21710         }
21711         
21712         if(this.tabId){
21713             cfg.tabId = this.tabId;
21714         }
21715         
21716         
21717         
21718         return cfg;
21719     },
21720     
21721     initEvents:  function()
21722     {
21723         var p = this.parent();
21724         
21725         this.navId = this.navId || p.navId;
21726         
21727         if (typeof(this.navId) != 'undefined') {
21728             // not really needed.. but just in case.. parent should be a NavGroup.
21729             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21730             
21731             tg.register(this);
21732             
21733             var i = tg.tabs.length - 1;
21734             
21735             if(this.active && tg.bullets > 0 && i < tg.bullets){
21736                 tg.setActiveBullet(i);
21737             }
21738         }
21739         
21740         this.el.on('click', this.onClick, this);
21741         
21742         if(Roo.isTouch && this.touchSlide){
21743             this.el.on("touchstart", this.onTouchStart, this);
21744             this.el.on("touchmove", this.onTouchMove, this);
21745             this.el.on("touchend", this.onTouchEnd, this);
21746         }
21747         
21748     },
21749     
21750     onRender : function(ct, position)
21751     {
21752         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21753     },
21754     
21755     setActive : function(state)
21756     {
21757         Roo.log("panel - set active " + this.tabId + "=" + state);
21758         
21759         this.active = state;
21760         if (!state) {
21761             this.el.removeClass('active');
21762             
21763         } else  if (!this.el.hasClass('active')) {
21764             this.el.addClass('active');
21765         }
21766         
21767         this.fireEvent('changed', this, state);
21768     },
21769     
21770     onClick : function(e)
21771     {
21772         e.preventDefault();
21773         
21774         if(!this.href.length){
21775             return;
21776         }
21777         
21778         window.location.href = this.href;
21779     },
21780     
21781     startX : 0,
21782     startY : 0,
21783     endX : 0,
21784     endY : 0,
21785     swiping : false,
21786     
21787     onTouchStart : function(e)
21788     {
21789         this.swiping = false;
21790         
21791         this.startX = e.browserEvent.touches[0].clientX;
21792         this.startY = e.browserEvent.touches[0].clientY;
21793     },
21794     
21795     onTouchMove : function(e)
21796     {
21797         this.swiping = true;
21798         
21799         this.endX = e.browserEvent.touches[0].clientX;
21800         this.endY = e.browserEvent.touches[0].clientY;
21801     },
21802     
21803     onTouchEnd : function(e)
21804     {
21805         if(!this.swiping){
21806             this.onClick(e);
21807             return;
21808         }
21809         
21810         var tabGroup = this.parent();
21811         
21812         if(this.endX > this.startX){ // swiping right
21813             tabGroup.showPanelPrev();
21814             return;
21815         }
21816         
21817         if(this.startX > this.endX){ // swiping left
21818             tabGroup.showPanelNext();
21819             return;
21820         }
21821     }
21822     
21823     
21824 });
21825  
21826
21827  
21828
21829  /*
21830  * - LGPL
21831  *
21832  * DateField
21833  * 
21834  */
21835
21836 /**
21837  * @class Roo.bootstrap.DateField
21838  * @extends Roo.bootstrap.Input
21839  * Bootstrap DateField class
21840  * @cfg {Number} weekStart default 0
21841  * @cfg {String} viewMode default empty, (months|years)
21842  * @cfg {String} minViewMode default empty, (months|years)
21843  * @cfg {Number} startDate default -Infinity
21844  * @cfg {Number} endDate default Infinity
21845  * @cfg {Boolean} todayHighlight default false
21846  * @cfg {Boolean} todayBtn default false
21847  * @cfg {Boolean} calendarWeeks default false
21848  * @cfg {Object} daysOfWeekDisabled default empty
21849  * @cfg {Boolean} singleMode default false (true | false)
21850  * 
21851  * @cfg {Boolean} keyboardNavigation default true
21852  * @cfg {String} language default en
21853  * 
21854  * @constructor
21855  * Create a new DateField
21856  * @param {Object} config The config object
21857  */
21858
21859 Roo.bootstrap.DateField = function(config){
21860     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21861      this.addEvents({
21862             /**
21863              * @event show
21864              * Fires when this field show.
21865              * @param {Roo.bootstrap.DateField} this
21866              * @param {Mixed} date The date value
21867              */
21868             show : true,
21869             /**
21870              * @event show
21871              * Fires when this field hide.
21872              * @param {Roo.bootstrap.DateField} this
21873              * @param {Mixed} date The date value
21874              */
21875             hide : true,
21876             /**
21877              * @event select
21878              * Fires when select a date.
21879              * @param {Roo.bootstrap.DateField} this
21880              * @param {Mixed} date The date value
21881              */
21882             select : true,
21883             /**
21884              * @event beforeselect
21885              * Fires when before select a date.
21886              * @param {Roo.bootstrap.DateField} this
21887              * @param {Mixed} date The date value
21888              */
21889             beforeselect : true
21890         });
21891 };
21892
21893 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21894     
21895     /**
21896      * @cfg {String} format
21897      * The default date format string which can be overriden for localization support.  The format must be
21898      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21899      */
21900     format : "m/d/y",
21901     /**
21902      * @cfg {String} altFormats
21903      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21904      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21905      */
21906     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21907     
21908     weekStart : 0,
21909     
21910     viewMode : '',
21911     
21912     minViewMode : '',
21913     
21914     todayHighlight : false,
21915     
21916     todayBtn: false,
21917     
21918     language: 'en',
21919     
21920     keyboardNavigation: true,
21921     
21922     calendarWeeks: false,
21923     
21924     startDate: -Infinity,
21925     
21926     endDate: Infinity,
21927     
21928     daysOfWeekDisabled: [],
21929     
21930     _events: [],
21931     
21932     singleMode : false,
21933     
21934     UTCDate: function()
21935     {
21936         return new Date(Date.UTC.apply(Date, arguments));
21937     },
21938     
21939     UTCToday: function()
21940     {
21941         var today = new Date();
21942         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21943     },
21944     
21945     getDate: function() {
21946             var d = this.getUTCDate();
21947             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21948     },
21949     
21950     getUTCDate: function() {
21951             return this.date;
21952     },
21953     
21954     setDate: function(d) {
21955             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21956     },
21957     
21958     setUTCDate: function(d) {
21959             this.date = d;
21960             this.setValue(this.formatDate(this.date));
21961     },
21962         
21963     onRender: function(ct, position)
21964     {
21965         
21966         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21967         
21968         this.language = this.language || 'en';
21969         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21970         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21971         
21972         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21973         this.format = this.format || 'm/d/y';
21974         this.isInline = false;
21975         this.isInput = true;
21976         this.component = this.el.select('.add-on', true).first() || false;
21977         this.component = (this.component && this.component.length === 0) ? false : this.component;
21978         this.hasInput = this.component && this.inputEl().length;
21979         
21980         if (typeof(this.minViewMode === 'string')) {
21981             switch (this.minViewMode) {
21982                 case 'months':
21983                     this.minViewMode = 1;
21984                     break;
21985                 case 'years':
21986                     this.minViewMode = 2;
21987                     break;
21988                 default:
21989                     this.minViewMode = 0;
21990                     break;
21991             }
21992         }
21993         
21994         if (typeof(this.viewMode === 'string')) {
21995             switch (this.viewMode) {
21996                 case 'months':
21997                     this.viewMode = 1;
21998                     break;
21999                 case 'years':
22000                     this.viewMode = 2;
22001                     break;
22002                 default:
22003                     this.viewMode = 0;
22004                     break;
22005             }
22006         }
22007                 
22008         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
22009         
22010 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
22011         
22012         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22013         
22014         this.picker().on('mousedown', this.onMousedown, this);
22015         this.picker().on('click', this.onClick, this);
22016         
22017         this.picker().addClass('datepicker-dropdown');
22018         
22019         this.startViewMode = this.viewMode;
22020         
22021         if(this.singleMode){
22022             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
22023                 v.setVisibilityMode(Roo.Element.DISPLAY);
22024                 v.hide();
22025             });
22026             
22027             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22028                 v.setStyle('width', '189px');
22029             });
22030         }
22031         
22032         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
22033             if(!this.calendarWeeks){
22034                 v.remove();
22035                 return;
22036             }
22037             
22038             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22039             v.attr('colspan', function(i, val){
22040                 return parseInt(val) + 1;
22041             });
22042         });
22043                         
22044         
22045         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
22046         
22047         this.setStartDate(this.startDate);
22048         this.setEndDate(this.endDate);
22049         
22050         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
22051         
22052         this.fillDow();
22053         this.fillMonths();
22054         this.update();
22055         this.showMode();
22056         
22057         if(this.isInline) {
22058             this.showPopup();
22059         }
22060     },
22061     
22062     picker : function()
22063     {
22064         return this.pickerEl;
22065 //        return this.el.select('.datepicker', true).first();
22066     },
22067     
22068     fillDow: function()
22069     {
22070         var dowCnt = this.weekStart;
22071         
22072         var dow = {
22073             tag: 'tr',
22074             cn: [
22075                 
22076             ]
22077         };
22078         
22079         if(this.calendarWeeks){
22080             dow.cn.push({
22081                 tag: 'th',
22082                 cls: 'cw',
22083                 html: '&nbsp;'
22084             })
22085         }
22086         
22087         while (dowCnt < this.weekStart + 7) {
22088             dow.cn.push({
22089                 tag: 'th',
22090                 cls: 'dow',
22091                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
22092             });
22093         }
22094         
22095         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
22096     },
22097     
22098     fillMonths: function()
22099     {    
22100         var i = 0;
22101         var months = this.picker().select('>.datepicker-months td', true).first();
22102         
22103         months.dom.innerHTML = '';
22104         
22105         while (i < 12) {
22106             var month = {
22107                 tag: 'span',
22108                 cls: 'month',
22109                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
22110             };
22111             
22112             months.createChild(month);
22113         }
22114         
22115     },
22116     
22117     update: function()
22118     {
22119         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;
22120         
22121         if (this.date < this.startDate) {
22122             this.viewDate = new Date(this.startDate);
22123         } else if (this.date > this.endDate) {
22124             this.viewDate = new Date(this.endDate);
22125         } else {
22126             this.viewDate = new Date(this.date);
22127         }
22128         
22129         this.fill();
22130     },
22131     
22132     fill: function() 
22133     {
22134         var d = new Date(this.viewDate),
22135                 year = d.getUTCFullYear(),
22136                 month = d.getUTCMonth(),
22137                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
22138                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
22139                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
22140                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
22141                 currentDate = this.date && this.date.valueOf(),
22142                 today = this.UTCToday();
22143         
22144         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
22145         
22146 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
22147         
22148 //        this.picker.select('>tfoot th.today').
22149 //                                              .text(dates[this.language].today)
22150 //                                              .toggle(this.todayBtn !== false);
22151     
22152         this.updateNavArrows();
22153         this.fillMonths();
22154                                                 
22155         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
22156         
22157         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
22158          
22159         prevMonth.setUTCDate(day);
22160         
22161         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
22162         
22163         var nextMonth = new Date(prevMonth);
22164         
22165         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
22166         
22167         nextMonth = nextMonth.valueOf();
22168         
22169         var fillMonths = false;
22170         
22171         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
22172         
22173         while(prevMonth.valueOf() <= nextMonth) {
22174             var clsName = '';
22175             
22176             if (prevMonth.getUTCDay() === this.weekStart) {
22177                 if(fillMonths){
22178                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
22179                 }
22180                     
22181                 fillMonths = {
22182                     tag: 'tr',
22183                     cn: []
22184                 };
22185                 
22186                 if(this.calendarWeeks){
22187                     // ISO 8601: First week contains first thursday.
22188                     // ISO also states week starts on Monday, but we can be more abstract here.
22189                     var
22190                     // Start of current week: based on weekstart/current date
22191                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
22192                     // Thursday of this week
22193                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
22194                     // First Thursday of year, year from thursday
22195                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
22196                     // Calendar week: ms between thursdays, div ms per day, div 7 days
22197                     calWeek =  (th - yth) / 864e5 / 7 + 1;
22198                     
22199                     fillMonths.cn.push({
22200                         tag: 'td',
22201                         cls: 'cw',
22202                         html: calWeek
22203                     });
22204                 }
22205             }
22206             
22207             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
22208                 clsName += ' old';
22209             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
22210                 clsName += ' new';
22211             }
22212             if (this.todayHighlight &&
22213                 prevMonth.getUTCFullYear() == today.getFullYear() &&
22214                 prevMonth.getUTCMonth() == today.getMonth() &&
22215                 prevMonth.getUTCDate() == today.getDate()) {
22216                 clsName += ' today';
22217             }
22218             
22219             if (currentDate && prevMonth.valueOf() === currentDate) {
22220                 clsName += ' active';
22221             }
22222             
22223             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
22224                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
22225                     clsName += ' disabled';
22226             }
22227             
22228             fillMonths.cn.push({
22229                 tag: 'td',
22230                 cls: 'day ' + clsName,
22231                 html: prevMonth.getDate()
22232             });
22233             
22234             prevMonth.setDate(prevMonth.getDate()+1);
22235         }
22236           
22237         var currentYear = this.date && this.date.getUTCFullYear();
22238         var currentMonth = this.date && this.date.getUTCMonth();
22239         
22240         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
22241         
22242         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
22243             v.removeClass('active');
22244             
22245             if(currentYear === year && k === currentMonth){
22246                 v.addClass('active');
22247             }
22248             
22249             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
22250                 v.addClass('disabled');
22251             }
22252             
22253         });
22254         
22255         
22256         year = parseInt(year/10, 10) * 10;
22257         
22258         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
22259         
22260         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
22261         
22262         year -= 1;
22263         for (var i = -1; i < 11; i++) {
22264             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
22265                 tag: 'span',
22266                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
22267                 html: year
22268             });
22269             
22270             year += 1;
22271         }
22272     },
22273     
22274     showMode: function(dir) 
22275     {
22276         if (dir) {
22277             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
22278         }
22279         
22280         Roo.each(this.picker().select('>div',true).elements, function(v){
22281             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22282             v.hide();
22283         });
22284         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
22285     },
22286     
22287     place: function()
22288     {
22289         if(this.isInline) {
22290             return;
22291         }
22292         
22293         this.picker().removeClass(['bottom', 'top']);
22294         
22295         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22296             /*
22297              * place to the top of element!
22298              *
22299              */
22300             
22301             this.picker().addClass('top');
22302             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22303             
22304             return;
22305         }
22306         
22307         this.picker().addClass('bottom');
22308         
22309         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22310     },
22311     
22312     parseDate : function(value)
22313     {
22314         if(!value || value instanceof Date){
22315             return value;
22316         }
22317         var v = Date.parseDate(value, this.format);
22318         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
22319             v = Date.parseDate(value, 'Y-m-d');
22320         }
22321         if(!v && this.altFormats){
22322             if(!this.altFormatsArray){
22323                 this.altFormatsArray = this.altFormats.split("|");
22324             }
22325             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
22326                 v = Date.parseDate(value, this.altFormatsArray[i]);
22327             }
22328         }
22329         return v;
22330     },
22331     
22332     formatDate : function(date, fmt)
22333     {   
22334         return (!date || !(date instanceof Date)) ?
22335         date : date.dateFormat(fmt || this.format);
22336     },
22337     
22338     onFocus : function()
22339     {
22340         Roo.bootstrap.DateField.superclass.onFocus.call(this);
22341         this.showPopup();
22342     },
22343     
22344     onBlur : function()
22345     {
22346         Roo.bootstrap.DateField.superclass.onBlur.call(this);
22347         
22348         var d = this.inputEl().getValue();
22349         
22350         this.setValue(d);
22351                 
22352         this.hidePopup();
22353     },
22354     
22355     showPopup : function()
22356     {
22357         this.picker().show();
22358         this.update();
22359         this.place();
22360         
22361         this.fireEvent('showpopup', this, this.date);
22362     },
22363     
22364     hidePopup : function()
22365     {
22366         if(this.isInline) {
22367             return;
22368         }
22369         this.picker().hide();
22370         this.viewMode = this.startViewMode;
22371         this.showMode();
22372         
22373         this.fireEvent('hidepopup', this, this.date);
22374         
22375     },
22376     
22377     onMousedown: function(e)
22378     {
22379         e.stopPropagation();
22380         e.preventDefault();
22381     },
22382     
22383     keyup: function(e)
22384     {
22385         Roo.bootstrap.DateField.superclass.keyup.call(this);
22386         this.update();
22387     },
22388
22389     setValue: function(v)
22390     {
22391         if(this.fireEvent('beforeselect', this, v) !== false){
22392             var d = new Date(this.parseDate(v) ).clearTime();
22393         
22394             if(isNaN(d.getTime())){
22395                 this.date = this.viewDate = '';
22396                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22397                 return;
22398             }
22399
22400             v = this.formatDate(d);
22401
22402             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
22403
22404             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
22405
22406             this.update();
22407
22408             this.fireEvent('select', this, this.date);
22409         }
22410     },
22411     
22412     getValue: function()
22413     {
22414         return this.formatDate(this.date);
22415     },
22416     
22417     fireKey: function(e)
22418     {
22419         if (!this.picker().isVisible()){
22420             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22421                 this.showPopup();
22422             }
22423             return;
22424         }
22425         
22426         var dateChanged = false,
22427         dir, day, month,
22428         newDate, newViewDate;
22429         
22430         switch(e.keyCode){
22431             case 27: // escape
22432                 this.hidePopup();
22433                 e.preventDefault();
22434                 break;
22435             case 37: // left
22436             case 39: // right
22437                 if (!this.keyboardNavigation) {
22438                     break;
22439                 }
22440                 dir = e.keyCode == 37 ? -1 : 1;
22441                 
22442                 if (e.ctrlKey){
22443                     newDate = this.moveYear(this.date, dir);
22444                     newViewDate = this.moveYear(this.viewDate, dir);
22445                 } else if (e.shiftKey){
22446                     newDate = this.moveMonth(this.date, dir);
22447                     newViewDate = this.moveMonth(this.viewDate, dir);
22448                 } else {
22449                     newDate = new Date(this.date);
22450                     newDate.setUTCDate(this.date.getUTCDate() + dir);
22451                     newViewDate = new Date(this.viewDate);
22452                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
22453                 }
22454                 if (this.dateWithinRange(newDate)){
22455                     this.date = newDate;
22456                     this.viewDate = newViewDate;
22457                     this.setValue(this.formatDate(this.date));
22458 //                    this.update();
22459                     e.preventDefault();
22460                     dateChanged = true;
22461                 }
22462                 break;
22463             case 38: // up
22464             case 40: // down
22465                 if (!this.keyboardNavigation) {
22466                     break;
22467                 }
22468                 dir = e.keyCode == 38 ? -1 : 1;
22469                 if (e.ctrlKey){
22470                     newDate = this.moveYear(this.date, dir);
22471                     newViewDate = this.moveYear(this.viewDate, dir);
22472                 } else if (e.shiftKey){
22473                     newDate = this.moveMonth(this.date, dir);
22474                     newViewDate = this.moveMonth(this.viewDate, dir);
22475                 } else {
22476                     newDate = new Date(this.date);
22477                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
22478                     newViewDate = new Date(this.viewDate);
22479                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
22480                 }
22481                 if (this.dateWithinRange(newDate)){
22482                     this.date = newDate;
22483                     this.viewDate = newViewDate;
22484                     this.setValue(this.formatDate(this.date));
22485 //                    this.update();
22486                     e.preventDefault();
22487                     dateChanged = true;
22488                 }
22489                 break;
22490             case 13: // enter
22491                 this.setValue(this.formatDate(this.date));
22492                 this.hidePopup();
22493                 e.preventDefault();
22494                 break;
22495             case 9: // tab
22496                 this.setValue(this.formatDate(this.date));
22497                 this.hidePopup();
22498                 break;
22499             case 16: // shift
22500             case 17: // ctrl
22501             case 18: // alt
22502                 break;
22503             default :
22504                 this.hidePopup();
22505                 
22506         }
22507     },
22508     
22509     
22510     onClick: function(e) 
22511     {
22512         e.stopPropagation();
22513         e.preventDefault();
22514         
22515         var target = e.getTarget();
22516         
22517         if(target.nodeName.toLowerCase() === 'i'){
22518             target = Roo.get(target).dom.parentNode;
22519         }
22520         
22521         var nodeName = target.nodeName;
22522         var className = target.className;
22523         var html = target.innerHTML;
22524         //Roo.log(nodeName);
22525         
22526         switch(nodeName.toLowerCase()) {
22527             case 'th':
22528                 switch(className) {
22529                     case 'switch':
22530                         this.showMode(1);
22531                         break;
22532                     case 'prev':
22533                     case 'next':
22534                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
22535                         switch(this.viewMode){
22536                                 case 0:
22537                                         this.viewDate = this.moveMonth(this.viewDate, dir);
22538                                         break;
22539                                 case 1:
22540                                 case 2:
22541                                         this.viewDate = this.moveYear(this.viewDate, dir);
22542                                         break;
22543                         }
22544                         this.fill();
22545                         break;
22546                     case 'today':
22547                         var date = new Date();
22548                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
22549 //                        this.fill()
22550                         this.setValue(this.formatDate(this.date));
22551                         
22552                         this.hidePopup();
22553                         break;
22554                 }
22555                 break;
22556             case 'span':
22557                 if (className.indexOf('disabled') < 0) {
22558                 if (!this.viewDate) {
22559                     this.viewDate = new Date();
22560                 }
22561                 this.viewDate.setUTCDate(1);
22562                     if (className.indexOf('month') > -1) {
22563                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
22564                     } else {
22565                         var year = parseInt(html, 10) || 0;
22566                         this.viewDate.setUTCFullYear(year);
22567                         
22568                     }
22569                     
22570                     if(this.singleMode){
22571                         this.setValue(this.formatDate(this.viewDate));
22572                         this.hidePopup();
22573                         return;
22574                     }
22575                     
22576                     this.showMode(-1);
22577                     this.fill();
22578                 }
22579                 break;
22580                 
22581             case 'td':
22582                 //Roo.log(className);
22583                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
22584                     var day = parseInt(html, 10) || 1;
22585                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
22586                         month = (this.viewDate || new Date()).getUTCMonth();
22587
22588                     if (className.indexOf('old') > -1) {
22589                         if(month === 0 ){
22590                             month = 11;
22591                             year -= 1;
22592                         }else{
22593                             month -= 1;
22594                         }
22595                     } else if (className.indexOf('new') > -1) {
22596                         if (month == 11) {
22597                             month = 0;
22598                             year += 1;
22599                         } else {
22600                             month += 1;
22601                         }
22602                     }
22603                     //Roo.log([year,month,day]);
22604                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22605                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22606 //                    this.fill();
22607                     //Roo.log(this.formatDate(this.date));
22608                     this.setValue(this.formatDate(this.date));
22609                     this.hidePopup();
22610                 }
22611                 break;
22612         }
22613     },
22614     
22615     setStartDate: function(startDate)
22616     {
22617         this.startDate = startDate || -Infinity;
22618         if (this.startDate !== -Infinity) {
22619             this.startDate = this.parseDate(this.startDate);
22620         }
22621         this.update();
22622         this.updateNavArrows();
22623     },
22624
22625     setEndDate: function(endDate)
22626     {
22627         this.endDate = endDate || Infinity;
22628         if (this.endDate !== Infinity) {
22629             this.endDate = this.parseDate(this.endDate);
22630         }
22631         this.update();
22632         this.updateNavArrows();
22633     },
22634     
22635     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22636     {
22637         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22638         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22639             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22640         }
22641         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22642             return parseInt(d, 10);
22643         });
22644         this.update();
22645         this.updateNavArrows();
22646     },
22647     
22648     updateNavArrows: function() 
22649     {
22650         if(this.singleMode){
22651             return;
22652         }
22653         
22654         var d = new Date(this.viewDate),
22655         year = d.getUTCFullYear(),
22656         month = d.getUTCMonth();
22657         
22658         Roo.each(this.picker().select('.prev', true).elements, function(v){
22659             v.show();
22660             switch (this.viewMode) {
22661                 case 0:
22662
22663                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22664                         v.hide();
22665                     }
22666                     break;
22667                 case 1:
22668                 case 2:
22669                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22670                         v.hide();
22671                     }
22672                     break;
22673             }
22674         });
22675         
22676         Roo.each(this.picker().select('.next', true).elements, function(v){
22677             v.show();
22678             switch (this.viewMode) {
22679                 case 0:
22680
22681                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22682                         v.hide();
22683                     }
22684                     break;
22685                 case 1:
22686                 case 2:
22687                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22688                         v.hide();
22689                     }
22690                     break;
22691             }
22692         })
22693     },
22694     
22695     moveMonth: function(date, dir)
22696     {
22697         if (!dir) {
22698             return date;
22699         }
22700         var new_date = new Date(date.valueOf()),
22701         day = new_date.getUTCDate(),
22702         month = new_date.getUTCMonth(),
22703         mag = Math.abs(dir),
22704         new_month, test;
22705         dir = dir > 0 ? 1 : -1;
22706         if (mag == 1){
22707             test = dir == -1
22708             // If going back one month, make sure month is not current month
22709             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22710             ? function(){
22711                 return new_date.getUTCMonth() == month;
22712             }
22713             // If going forward one month, make sure month is as expected
22714             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22715             : function(){
22716                 return new_date.getUTCMonth() != new_month;
22717             };
22718             new_month = month + dir;
22719             new_date.setUTCMonth(new_month);
22720             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22721             if (new_month < 0 || new_month > 11) {
22722                 new_month = (new_month + 12) % 12;
22723             }
22724         } else {
22725             // For magnitudes >1, move one month at a time...
22726             for (var i=0; i<mag; i++) {
22727                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22728                 new_date = this.moveMonth(new_date, dir);
22729             }
22730             // ...then reset the day, keeping it in the new month
22731             new_month = new_date.getUTCMonth();
22732             new_date.setUTCDate(day);
22733             test = function(){
22734                 return new_month != new_date.getUTCMonth();
22735             };
22736         }
22737         // Common date-resetting loop -- if date is beyond end of month, make it
22738         // end of month
22739         while (test()){
22740             new_date.setUTCDate(--day);
22741             new_date.setUTCMonth(new_month);
22742         }
22743         return new_date;
22744     },
22745
22746     moveYear: function(date, dir)
22747     {
22748         return this.moveMonth(date, dir*12);
22749     },
22750
22751     dateWithinRange: function(date)
22752     {
22753         return date >= this.startDate && date <= this.endDate;
22754     },
22755
22756     
22757     remove: function() 
22758     {
22759         this.picker().remove();
22760     },
22761     
22762     validateValue : function(value)
22763     {
22764         if(this.getVisibilityEl().hasClass('hidden')){
22765             return true;
22766         }
22767         
22768         if(value.length < 1)  {
22769             if(this.allowBlank){
22770                 return true;
22771             }
22772             return false;
22773         }
22774         
22775         if(value.length < this.minLength){
22776             return false;
22777         }
22778         if(value.length > this.maxLength){
22779             return false;
22780         }
22781         if(this.vtype){
22782             var vt = Roo.form.VTypes;
22783             if(!vt[this.vtype](value, this)){
22784                 return false;
22785             }
22786         }
22787         if(typeof this.validator == "function"){
22788             var msg = this.validator(value);
22789             if(msg !== true){
22790                 return false;
22791             }
22792         }
22793         
22794         if(this.regex && !this.regex.test(value)){
22795             return false;
22796         }
22797         
22798         if(typeof(this.parseDate(value)) == 'undefined'){
22799             return false;
22800         }
22801         
22802         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22803             return false;
22804         }      
22805         
22806         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22807             return false;
22808         } 
22809         
22810         
22811         return true;
22812     },
22813     
22814     reset : function()
22815     {
22816         this.date = this.viewDate = '';
22817         
22818         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22819     }
22820    
22821 });
22822
22823 Roo.apply(Roo.bootstrap.DateField,  {
22824     
22825     head : {
22826         tag: 'thead',
22827         cn: [
22828         {
22829             tag: 'tr',
22830             cn: [
22831             {
22832                 tag: 'th',
22833                 cls: 'prev',
22834                 html: '<i class="fa fa-arrow-left"/>'
22835             },
22836             {
22837                 tag: 'th',
22838                 cls: 'switch',
22839                 colspan: '5'
22840             },
22841             {
22842                 tag: 'th',
22843                 cls: 'next',
22844                 html: '<i class="fa fa-arrow-right"/>'
22845             }
22846
22847             ]
22848         }
22849         ]
22850     },
22851     
22852     content : {
22853         tag: 'tbody',
22854         cn: [
22855         {
22856             tag: 'tr',
22857             cn: [
22858             {
22859                 tag: 'td',
22860                 colspan: '7'
22861             }
22862             ]
22863         }
22864         ]
22865     },
22866     
22867     footer : {
22868         tag: 'tfoot',
22869         cn: [
22870         {
22871             tag: 'tr',
22872             cn: [
22873             {
22874                 tag: 'th',
22875                 colspan: '7',
22876                 cls: 'today'
22877             }
22878                     
22879             ]
22880         }
22881         ]
22882     },
22883     
22884     dates:{
22885         en: {
22886             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22887             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22888             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22889             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22890             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22891             today: "Today"
22892         }
22893     },
22894     
22895     modes: [
22896     {
22897         clsName: 'days',
22898         navFnc: 'Month',
22899         navStep: 1
22900     },
22901     {
22902         clsName: 'months',
22903         navFnc: 'FullYear',
22904         navStep: 1
22905     },
22906     {
22907         clsName: 'years',
22908         navFnc: 'FullYear',
22909         navStep: 10
22910     }]
22911 });
22912
22913 Roo.apply(Roo.bootstrap.DateField,  {
22914   
22915     template : {
22916         tag: 'div',
22917         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22918         cn: [
22919         {
22920             tag: 'div',
22921             cls: 'datepicker-days',
22922             cn: [
22923             {
22924                 tag: 'table',
22925                 cls: 'table-condensed',
22926                 cn:[
22927                 Roo.bootstrap.DateField.head,
22928                 {
22929                     tag: 'tbody'
22930                 },
22931                 Roo.bootstrap.DateField.footer
22932                 ]
22933             }
22934             ]
22935         },
22936         {
22937             tag: 'div',
22938             cls: 'datepicker-months',
22939             cn: [
22940             {
22941                 tag: 'table',
22942                 cls: 'table-condensed',
22943                 cn:[
22944                 Roo.bootstrap.DateField.head,
22945                 Roo.bootstrap.DateField.content,
22946                 Roo.bootstrap.DateField.footer
22947                 ]
22948             }
22949             ]
22950         },
22951         {
22952             tag: 'div',
22953             cls: 'datepicker-years',
22954             cn: [
22955             {
22956                 tag: 'table',
22957                 cls: 'table-condensed',
22958                 cn:[
22959                 Roo.bootstrap.DateField.head,
22960                 Roo.bootstrap.DateField.content,
22961                 Roo.bootstrap.DateField.footer
22962                 ]
22963             }
22964             ]
22965         }
22966         ]
22967     }
22968 });
22969
22970  
22971
22972  /*
22973  * - LGPL
22974  *
22975  * TimeField
22976  * 
22977  */
22978
22979 /**
22980  * @class Roo.bootstrap.TimeField
22981  * @extends Roo.bootstrap.Input
22982  * Bootstrap DateField class
22983  * 
22984  * 
22985  * @constructor
22986  * Create a new TimeField
22987  * @param {Object} config The config object
22988  */
22989
22990 Roo.bootstrap.TimeField = function(config){
22991     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22992     this.addEvents({
22993             /**
22994              * @event show
22995              * Fires when this field show.
22996              * @param {Roo.bootstrap.DateField} thisthis
22997              * @param {Mixed} date The date value
22998              */
22999             show : true,
23000             /**
23001              * @event show
23002              * Fires when this field hide.
23003              * @param {Roo.bootstrap.DateField} this
23004              * @param {Mixed} date The date value
23005              */
23006             hide : true,
23007             /**
23008              * @event select
23009              * Fires when select a date.
23010              * @param {Roo.bootstrap.DateField} this
23011              * @param {Mixed} date The date value
23012              */
23013             select : true
23014         });
23015 };
23016
23017 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
23018     
23019     /**
23020      * @cfg {String} format
23021      * The default time format string which can be overriden for localization support.  The format must be
23022      * valid according to {@link Date#parseDate} (defaults to 'H:i').
23023      */
23024     format : "H:i",
23025
23026     getAutoCreate : function()
23027     {
23028         this.after = '<i class="fa far fa-clock"></i>';
23029         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
23030         
23031          
23032     },
23033     onRender: function(ct, position)
23034     {
23035         
23036         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
23037                 
23038         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
23039         
23040         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23041         
23042         this.pop = this.picker().select('>.datepicker-time',true).first();
23043         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23044         
23045         this.picker().on('mousedown', this.onMousedown, this);
23046         this.picker().on('click', this.onClick, this);
23047         
23048         this.picker().addClass('datepicker-dropdown');
23049     
23050         this.fillTime();
23051         this.update();
23052             
23053         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
23054         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
23055         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
23056         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
23057         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
23058         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
23059
23060     },
23061     
23062     fireKey: function(e){
23063         if (!this.picker().isVisible()){
23064             if (e.keyCode == 27) { // allow escape to hide and re-show picker
23065                 this.show();
23066             }
23067             return;
23068         }
23069
23070         e.preventDefault();
23071         
23072         switch(e.keyCode){
23073             case 27: // escape
23074                 this.hide();
23075                 break;
23076             case 37: // left
23077             case 39: // right
23078                 this.onTogglePeriod();
23079                 break;
23080             case 38: // up
23081                 this.onIncrementMinutes();
23082                 break;
23083             case 40: // down
23084                 this.onDecrementMinutes();
23085                 break;
23086             case 13: // enter
23087             case 9: // tab
23088                 this.setTime();
23089                 break;
23090         }
23091     },
23092     
23093     onClick: function(e) {
23094         e.stopPropagation();
23095         e.preventDefault();
23096     },
23097     
23098     picker : function()
23099     {
23100         return this.pickerEl;
23101     },
23102     
23103     fillTime: function()
23104     {    
23105         var time = this.pop.select('tbody', true).first();
23106         
23107         time.dom.innerHTML = '';
23108         
23109         time.createChild({
23110             tag: 'tr',
23111             cn: [
23112                 {
23113                     tag: 'td',
23114                     cn: [
23115                         {
23116                             tag: 'a',
23117                             href: '#',
23118                             cls: 'btn',
23119                             cn: [
23120                                 {
23121                                     tag: 'i',
23122                                     cls: 'hours-up fa fas fa-chevron-up'
23123                                 }
23124                             ]
23125                         } 
23126                     ]
23127                 },
23128                 {
23129                     tag: 'td',
23130                     cls: 'separator'
23131                 },
23132                 {
23133                     tag: 'td',
23134                     cn: [
23135                         {
23136                             tag: 'a',
23137                             href: '#',
23138                             cls: 'btn',
23139                             cn: [
23140                                 {
23141                                     tag: 'i',
23142                                     cls: 'minutes-up fa fas fa-chevron-up'
23143                                 }
23144                             ]
23145                         }
23146                     ]
23147                 },
23148                 {
23149                     tag: 'td',
23150                     cls: 'separator'
23151                 }
23152             ]
23153         });
23154         
23155         time.createChild({
23156             tag: 'tr',
23157             cn: [
23158                 {
23159                     tag: 'td',
23160                     cn: [
23161                         {
23162                             tag: 'span',
23163                             cls: 'timepicker-hour',
23164                             html: '00'
23165                         }  
23166                     ]
23167                 },
23168                 {
23169                     tag: 'td',
23170                     cls: 'separator',
23171                     html: ':'
23172                 },
23173                 {
23174                     tag: 'td',
23175                     cn: [
23176                         {
23177                             tag: 'span',
23178                             cls: 'timepicker-minute',
23179                             html: '00'
23180                         }  
23181                     ]
23182                 },
23183                 {
23184                     tag: 'td',
23185                     cls: 'separator'
23186                 },
23187                 {
23188                     tag: 'td',
23189                     cn: [
23190                         {
23191                             tag: 'button',
23192                             type: 'button',
23193                             cls: 'btn btn-primary period',
23194                             html: 'AM'
23195                             
23196                         }
23197                     ]
23198                 }
23199             ]
23200         });
23201         
23202         time.createChild({
23203             tag: 'tr',
23204             cn: [
23205                 {
23206                     tag: 'td',
23207                     cn: [
23208                         {
23209                             tag: 'a',
23210                             href: '#',
23211                             cls: 'btn',
23212                             cn: [
23213                                 {
23214                                     tag: 'span',
23215                                     cls: 'hours-down fa fas fa-chevron-down'
23216                                 }
23217                             ]
23218                         }
23219                     ]
23220                 },
23221                 {
23222                     tag: 'td',
23223                     cls: 'separator'
23224                 },
23225                 {
23226                     tag: 'td',
23227                     cn: [
23228                         {
23229                             tag: 'a',
23230                             href: '#',
23231                             cls: 'btn',
23232                             cn: [
23233                                 {
23234                                     tag: 'span',
23235                                     cls: 'minutes-down fa fas fa-chevron-down'
23236                                 }
23237                             ]
23238                         }
23239                     ]
23240                 },
23241                 {
23242                     tag: 'td',
23243                     cls: 'separator'
23244                 }
23245             ]
23246         });
23247         
23248     },
23249     
23250     update: function()
23251     {
23252         
23253         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
23254         
23255         this.fill();
23256     },
23257     
23258     fill: function() 
23259     {
23260         var hours = this.time.getHours();
23261         var minutes = this.time.getMinutes();
23262         var period = 'AM';
23263         
23264         if(hours > 11){
23265             period = 'PM';
23266         }
23267         
23268         if(hours == 0){
23269             hours = 12;
23270         }
23271         
23272         
23273         if(hours > 12){
23274             hours = hours - 12;
23275         }
23276         
23277         if(hours < 10){
23278             hours = '0' + hours;
23279         }
23280         
23281         if(minutes < 10){
23282             minutes = '0' + minutes;
23283         }
23284         
23285         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
23286         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
23287         this.pop.select('button', true).first().dom.innerHTML = period;
23288         
23289     },
23290     
23291     place: function()
23292     {   
23293         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
23294         
23295         var cls = ['bottom'];
23296         
23297         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
23298             cls.pop();
23299             cls.push('top');
23300         }
23301         
23302         cls.push('right');
23303         
23304         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
23305             cls.pop();
23306             cls.push('left');
23307         }
23308         //this.picker().setXY(20000,20000);
23309         this.picker().addClass(cls.join('-'));
23310         
23311         var _this = this;
23312         
23313         Roo.each(cls, function(c){
23314             if(c == 'bottom'){
23315                 (function() {
23316                  //  
23317                 }).defer(200);
23318                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
23319                 //_this.picker().setTop(_this.inputEl().getHeight());
23320                 return;
23321             }
23322             if(c == 'top'){
23323                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
23324                 
23325                 //_this.picker().setTop(0 - _this.picker().getHeight());
23326                 return;
23327             }
23328             /*
23329             if(c == 'left'){
23330                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
23331                 return;
23332             }
23333             if(c == 'right'){
23334                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
23335                 return;
23336             }
23337             */
23338         });
23339         
23340     },
23341   
23342     onFocus : function()
23343     {
23344         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
23345         this.show();
23346     },
23347     
23348     onBlur : function()
23349     {
23350         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
23351         this.hide();
23352     },
23353     
23354     show : function()
23355     {
23356         this.picker().show();
23357         this.pop.show();
23358         this.update();
23359         this.place();
23360         
23361         this.fireEvent('show', this, this.date);
23362     },
23363     
23364     hide : function()
23365     {
23366         this.picker().hide();
23367         this.pop.hide();
23368         
23369         this.fireEvent('hide', this, this.date);
23370     },
23371     
23372     setTime : function()
23373     {
23374         this.hide();
23375         this.setValue(this.time.format(this.format));
23376         
23377         this.fireEvent('select', this, this.date);
23378         
23379         
23380     },
23381     
23382     onMousedown: function(e){
23383         e.stopPropagation();
23384         e.preventDefault();
23385     },
23386     
23387     onIncrementHours: function()
23388     {
23389         Roo.log('onIncrementHours');
23390         this.time = this.time.add(Date.HOUR, 1);
23391         this.update();
23392         
23393     },
23394     
23395     onDecrementHours: function()
23396     {
23397         Roo.log('onDecrementHours');
23398         this.time = this.time.add(Date.HOUR, -1);
23399         this.update();
23400     },
23401     
23402     onIncrementMinutes: function()
23403     {
23404         Roo.log('onIncrementMinutes');
23405         this.time = this.time.add(Date.MINUTE, 1);
23406         this.update();
23407     },
23408     
23409     onDecrementMinutes: function()
23410     {
23411         Roo.log('onDecrementMinutes');
23412         this.time = this.time.add(Date.MINUTE, -1);
23413         this.update();
23414     },
23415     
23416     onTogglePeriod: function()
23417     {
23418         Roo.log('onTogglePeriod');
23419         this.time = this.time.add(Date.HOUR, 12);
23420         this.update();
23421     }
23422     
23423    
23424 });
23425  
23426
23427 Roo.apply(Roo.bootstrap.TimeField,  {
23428   
23429     template : {
23430         tag: 'div',
23431         cls: 'datepicker dropdown-menu',
23432         cn: [
23433             {
23434                 tag: 'div',
23435                 cls: 'datepicker-time',
23436                 cn: [
23437                 {
23438                     tag: 'table',
23439                     cls: 'table-condensed',
23440                     cn:[
23441                         {
23442                             tag: 'tbody',
23443                             cn: [
23444                                 {
23445                                     tag: 'tr',
23446                                     cn: [
23447                                     {
23448                                         tag: 'td',
23449                                         colspan: '7'
23450                                     }
23451                                     ]
23452                                 }
23453                             ]
23454                         },
23455                         {
23456                             tag: 'tfoot',
23457                             cn: [
23458                                 {
23459                                     tag: 'tr',
23460                                     cn: [
23461                                     {
23462                                         tag: 'th',
23463                                         colspan: '7',
23464                                         cls: '',
23465                                         cn: [
23466                                             {
23467                                                 tag: 'button',
23468                                                 cls: 'btn btn-info ok',
23469                                                 html: 'OK'
23470                                             }
23471                                         ]
23472                                     }
23473                     
23474                                     ]
23475                                 }
23476                             ]
23477                         }
23478                     ]
23479                 }
23480                 ]
23481             }
23482         ]
23483     }
23484 });
23485
23486  
23487
23488  /*
23489  * - LGPL
23490  *
23491  * MonthField
23492  * 
23493  */
23494
23495 /**
23496  * @class Roo.bootstrap.MonthField
23497  * @extends Roo.bootstrap.Input
23498  * Bootstrap MonthField class
23499  * 
23500  * @cfg {String} language default en
23501  * 
23502  * @constructor
23503  * Create a new MonthField
23504  * @param {Object} config The config object
23505  */
23506
23507 Roo.bootstrap.MonthField = function(config){
23508     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
23509     
23510     this.addEvents({
23511         /**
23512          * @event show
23513          * Fires when this field show.
23514          * @param {Roo.bootstrap.MonthField} this
23515          * @param {Mixed} date The date value
23516          */
23517         show : true,
23518         /**
23519          * @event show
23520          * Fires when this field hide.
23521          * @param {Roo.bootstrap.MonthField} this
23522          * @param {Mixed} date The date value
23523          */
23524         hide : true,
23525         /**
23526          * @event select
23527          * Fires when select a date.
23528          * @param {Roo.bootstrap.MonthField} this
23529          * @param {String} oldvalue The old value
23530          * @param {String} newvalue The new value
23531          */
23532         select : true
23533     });
23534 };
23535
23536 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
23537     
23538     onRender: function(ct, position)
23539     {
23540         
23541         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
23542         
23543         this.language = this.language || 'en';
23544         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
23545         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
23546         
23547         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
23548         this.isInline = false;
23549         this.isInput = true;
23550         this.component = this.el.select('.add-on', true).first() || false;
23551         this.component = (this.component && this.component.length === 0) ? false : this.component;
23552         this.hasInput = this.component && this.inputEL().length;
23553         
23554         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
23555         
23556         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
23557         
23558         this.picker().on('mousedown', this.onMousedown, this);
23559         this.picker().on('click', this.onClick, this);
23560         
23561         this.picker().addClass('datepicker-dropdown');
23562         
23563         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
23564             v.setStyle('width', '189px');
23565         });
23566         
23567         this.fillMonths();
23568         
23569         this.update();
23570         
23571         if(this.isInline) {
23572             this.show();
23573         }
23574         
23575     },
23576     
23577     setValue: function(v, suppressEvent)
23578     {   
23579         var o = this.getValue();
23580         
23581         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
23582         
23583         this.update();
23584
23585         if(suppressEvent !== true){
23586             this.fireEvent('select', this, o, v);
23587         }
23588         
23589     },
23590     
23591     getValue: function()
23592     {
23593         return this.value;
23594     },
23595     
23596     onClick: function(e) 
23597     {
23598         e.stopPropagation();
23599         e.preventDefault();
23600         
23601         var target = e.getTarget();
23602         
23603         if(target.nodeName.toLowerCase() === 'i'){
23604             target = Roo.get(target).dom.parentNode;
23605         }
23606         
23607         var nodeName = target.nodeName;
23608         var className = target.className;
23609         var html = target.innerHTML;
23610         
23611         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23612             return;
23613         }
23614         
23615         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23616         
23617         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23618         
23619         this.hide();
23620                         
23621     },
23622     
23623     picker : function()
23624     {
23625         return this.pickerEl;
23626     },
23627     
23628     fillMonths: function()
23629     {    
23630         var i = 0;
23631         var months = this.picker().select('>.datepicker-months td', true).first();
23632         
23633         months.dom.innerHTML = '';
23634         
23635         while (i < 12) {
23636             var month = {
23637                 tag: 'span',
23638                 cls: 'month',
23639                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23640             };
23641             
23642             months.createChild(month);
23643         }
23644         
23645     },
23646     
23647     update: function()
23648     {
23649         var _this = this;
23650         
23651         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23652             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23653         }
23654         
23655         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23656             e.removeClass('active');
23657             
23658             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23659                 e.addClass('active');
23660             }
23661         })
23662     },
23663     
23664     place: function()
23665     {
23666         if(this.isInline) {
23667             return;
23668         }
23669         
23670         this.picker().removeClass(['bottom', 'top']);
23671         
23672         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23673             /*
23674              * place to the top of element!
23675              *
23676              */
23677             
23678             this.picker().addClass('top');
23679             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23680             
23681             return;
23682         }
23683         
23684         this.picker().addClass('bottom');
23685         
23686         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23687     },
23688     
23689     onFocus : function()
23690     {
23691         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23692         this.show();
23693     },
23694     
23695     onBlur : function()
23696     {
23697         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23698         
23699         var d = this.inputEl().getValue();
23700         
23701         this.setValue(d);
23702                 
23703         this.hide();
23704     },
23705     
23706     show : function()
23707     {
23708         this.picker().show();
23709         this.picker().select('>.datepicker-months', true).first().show();
23710         this.update();
23711         this.place();
23712         
23713         this.fireEvent('show', this, this.date);
23714     },
23715     
23716     hide : function()
23717     {
23718         if(this.isInline) {
23719             return;
23720         }
23721         this.picker().hide();
23722         this.fireEvent('hide', this, this.date);
23723         
23724     },
23725     
23726     onMousedown: function(e)
23727     {
23728         e.stopPropagation();
23729         e.preventDefault();
23730     },
23731     
23732     keyup: function(e)
23733     {
23734         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23735         this.update();
23736     },
23737
23738     fireKey: function(e)
23739     {
23740         if (!this.picker().isVisible()){
23741             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23742                 this.show();
23743             }
23744             return;
23745         }
23746         
23747         var dir;
23748         
23749         switch(e.keyCode){
23750             case 27: // escape
23751                 this.hide();
23752                 e.preventDefault();
23753                 break;
23754             case 37: // left
23755             case 39: // right
23756                 dir = e.keyCode == 37 ? -1 : 1;
23757                 
23758                 this.vIndex = this.vIndex + dir;
23759                 
23760                 if(this.vIndex < 0){
23761                     this.vIndex = 0;
23762                 }
23763                 
23764                 if(this.vIndex > 11){
23765                     this.vIndex = 11;
23766                 }
23767                 
23768                 if(isNaN(this.vIndex)){
23769                     this.vIndex = 0;
23770                 }
23771                 
23772                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23773                 
23774                 break;
23775             case 38: // up
23776             case 40: // down
23777                 
23778                 dir = e.keyCode == 38 ? -1 : 1;
23779                 
23780                 this.vIndex = this.vIndex + dir * 4;
23781                 
23782                 if(this.vIndex < 0){
23783                     this.vIndex = 0;
23784                 }
23785                 
23786                 if(this.vIndex > 11){
23787                     this.vIndex = 11;
23788                 }
23789                 
23790                 if(isNaN(this.vIndex)){
23791                     this.vIndex = 0;
23792                 }
23793                 
23794                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23795                 break;
23796                 
23797             case 13: // enter
23798                 
23799                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23800                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23801                 }
23802                 
23803                 this.hide();
23804                 e.preventDefault();
23805                 break;
23806             case 9: // tab
23807                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23808                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23809                 }
23810                 this.hide();
23811                 break;
23812             case 16: // shift
23813             case 17: // ctrl
23814             case 18: // alt
23815                 break;
23816             default :
23817                 this.hide();
23818                 
23819         }
23820     },
23821     
23822     remove: function() 
23823     {
23824         this.picker().remove();
23825     }
23826    
23827 });
23828
23829 Roo.apply(Roo.bootstrap.MonthField,  {
23830     
23831     content : {
23832         tag: 'tbody',
23833         cn: [
23834         {
23835             tag: 'tr',
23836             cn: [
23837             {
23838                 tag: 'td',
23839                 colspan: '7'
23840             }
23841             ]
23842         }
23843         ]
23844     },
23845     
23846     dates:{
23847         en: {
23848             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23849             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23850         }
23851     }
23852 });
23853
23854 Roo.apply(Roo.bootstrap.MonthField,  {
23855   
23856     template : {
23857         tag: 'div',
23858         cls: 'datepicker dropdown-menu roo-dynamic',
23859         cn: [
23860             {
23861                 tag: 'div',
23862                 cls: 'datepicker-months',
23863                 cn: [
23864                 {
23865                     tag: 'table',
23866                     cls: 'table-condensed',
23867                     cn:[
23868                         Roo.bootstrap.DateField.content
23869                     ]
23870                 }
23871                 ]
23872             }
23873         ]
23874     }
23875 });
23876
23877  
23878
23879  
23880  /*
23881  * - LGPL
23882  *
23883  * CheckBox
23884  * 
23885  */
23886
23887 /**
23888  * @class Roo.bootstrap.CheckBox
23889  * @extends Roo.bootstrap.Input
23890  * Bootstrap CheckBox class
23891  * 
23892  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23893  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23894  * @cfg {String} boxLabel The text that appears beside the checkbox
23895  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23896  * @cfg {Boolean} checked initnal the element
23897  * @cfg {Boolean} inline inline the element (default false)
23898  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23899  * @cfg {String} tooltip label tooltip
23900  * 
23901  * @constructor
23902  * Create a new CheckBox
23903  * @param {Object} config The config object
23904  */
23905
23906 Roo.bootstrap.CheckBox = function(config){
23907     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23908    
23909     this.addEvents({
23910         /**
23911         * @event check
23912         * Fires when the element is checked or unchecked.
23913         * @param {Roo.bootstrap.CheckBox} this This input
23914         * @param {Boolean} checked The new checked value
23915         */
23916        check : true,
23917        /**
23918         * @event click
23919         * Fires when the element is click.
23920         * @param {Roo.bootstrap.CheckBox} this This input
23921         */
23922        click : true
23923     });
23924     
23925 };
23926
23927 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23928   
23929     inputType: 'checkbox',
23930     inputValue: 1,
23931     valueOff: 0,
23932     boxLabel: false,
23933     checked: false,
23934     weight : false,
23935     inline: false,
23936     tooltip : '',
23937     
23938     // checkbox success does not make any sense really.. 
23939     invalidClass : "",
23940     validClass : "",
23941     
23942     
23943     getAutoCreate : function()
23944     {
23945         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23946         
23947         var id = Roo.id();
23948         
23949         var cfg = {};
23950         
23951         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23952         
23953         if(this.inline){
23954             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23955         }
23956         
23957         var input =  {
23958             tag: 'input',
23959             id : id,
23960             type : this.inputType,
23961             value : this.inputValue,
23962             cls : 'roo-' + this.inputType, //'form-box',
23963             placeholder : this.placeholder || ''
23964             
23965         };
23966         
23967         if(this.inputType != 'radio'){
23968             var hidden =  {
23969                 tag: 'input',
23970                 type : 'hidden',
23971                 cls : 'roo-hidden-value',
23972                 value : this.checked ? this.inputValue : this.valueOff
23973             };
23974         }
23975         
23976             
23977         if (this.weight) { // Validity check?
23978             cfg.cls += " " + this.inputType + "-" + this.weight;
23979         }
23980         
23981         if (this.disabled) {
23982             input.disabled=true;
23983         }
23984         
23985         if(this.checked){
23986             input.checked = this.checked;
23987         }
23988         
23989         if (this.name) {
23990             
23991             input.name = this.name;
23992             
23993             if(this.inputType != 'radio'){
23994                 hidden.name = this.name;
23995                 input.name = '_hidden_' + this.name;
23996             }
23997         }
23998         
23999         if (this.size) {
24000             input.cls += ' input-' + this.size;
24001         }
24002         
24003         var settings=this;
24004         
24005         ['xs','sm','md','lg'].map(function(size){
24006             if (settings[size]) {
24007                 cfg.cls += ' col-' + size + '-' + settings[size];
24008             }
24009         });
24010         
24011         var inputblock = input;
24012          
24013         if (this.before || this.after) {
24014             
24015             inputblock = {
24016                 cls : 'input-group',
24017                 cn :  [] 
24018             };
24019             
24020             if (this.before) {
24021                 inputblock.cn.push({
24022                     tag :'span',
24023                     cls : 'input-group-addon',
24024                     html : this.before
24025                 });
24026             }
24027             
24028             inputblock.cn.push(input);
24029             
24030             if(this.inputType != 'radio'){
24031                 inputblock.cn.push(hidden);
24032             }
24033             
24034             if (this.after) {
24035                 inputblock.cn.push({
24036                     tag :'span',
24037                     cls : 'input-group-addon',
24038                     html : this.after
24039                 });
24040             }
24041             
24042         }
24043         var boxLabelCfg = false;
24044         
24045         if(this.boxLabel){
24046            
24047             boxLabelCfg = {
24048                 tag: 'label',
24049                 //'for': id, // box label is handled by onclick - so no for...
24050                 cls: 'box-label',
24051                 html: this.boxLabel
24052             };
24053             if(this.tooltip){
24054                 boxLabelCfg.tooltip = this.tooltip;
24055             }
24056              
24057         }
24058         
24059         
24060         if (align ==='left' && this.fieldLabel.length) {
24061 //                Roo.log("left and has label");
24062             cfg.cn = [
24063                 {
24064                     tag: 'label',
24065                     'for' :  id,
24066                     cls : 'control-label',
24067                     html : this.fieldLabel
24068                 },
24069                 {
24070                     cls : "", 
24071                     cn: [
24072                         inputblock
24073                     ]
24074                 }
24075             ];
24076             
24077             if (boxLabelCfg) {
24078                 cfg.cn[1].cn.push(boxLabelCfg);
24079             }
24080             
24081             if(this.labelWidth > 12){
24082                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
24083             }
24084             
24085             if(this.labelWidth < 13 && this.labelmd == 0){
24086                 this.labelmd = this.labelWidth;
24087             }
24088             
24089             if(this.labellg > 0){
24090                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
24091                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
24092             }
24093             
24094             if(this.labelmd > 0){
24095                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
24096                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
24097             }
24098             
24099             if(this.labelsm > 0){
24100                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
24101                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
24102             }
24103             
24104             if(this.labelxs > 0){
24105                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
24106                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
24107             }
24108             
24109         } else if ( this.fieldLabel.length) {
24110 //                Roo.log(" label");
24111                 cfg.cn = [
24112                    
24113                     {
24114                         tag: this.boxLabel ? 'span' : 'label',
24115                         'for': id,
24116                         cls: 'control-label box-input-label',
24117                         //cls : 'input-group-addon',
24118                         html : this.fieldLabel
24119                     },
24120                     
24121                     inputblock
24122                     
24123                 ];
24124                 if (boxLabelCfg) {
24125                     cfg.cn.push(boxLabelCfg);
24126                 }
24127
24128         } else {
24129             
24130 //                Roo.log(" no label && no align");
24131                 cfg.cn = [  inputblock ] ;
24132                 if (boxLabelCfg) {
24133                     cfg.cn.push(boxLabelCfg);
24134                 }
24135
24136                 
24137         }
24138         
24139        
24140         
24141         if(this.inputType != 'radio'){
24142             cfg.cn.push(hidden);
24143         }
24144         
24145         return cfg;
24146         
24147     },
24148     
24149     /**
24150      * return the real input element.
24151      */
24152     inputEl: function ()
24153     {
24154         return this.el.select('input.roo-' + this.inputType,true).first();
24155     },
24156     hiddenEl: function ()
24157     {
24158         return this.el.select('input.roo-hidden-value',true).first();
24159     },
24160     
24161     labelEl: function()
24162     {
24163         return this.el.select('label.control-label',true).first();
24164     },
24165     /* depricated... */
24166     
24167     label: function()
24168     {
24169         return this.labelEl();
24170     },
24171     
24172     boxLabelEl: function()
24173     {
24174         return this.el.select('label.box-label',true).first();
24175     },
24176     
24177     initEvents : function()
24178     {
24179 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
24180         
24181         this.inputEl().on('click', this.onClick,  this);
24182         
24183         if (this.boxLabel) { 
24184             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
24185         }
24186         
24187         this.startValue = this.getValue();
24188         
24189         if(this.groupId){
24190             Roo.bootstrap.CheckBox.register(this);
24191         }
24192     },
24193     
24194     onClick : function(e)
24195     {   
24196         if(this.fireEvent('click', this, e) !== false){
24197             this.setChecked(!this.checked);
24198         }
24199         
24200     },
24201     
24202     setChecked : function(state,suppressEvent)
24203     {
24204         this.startValue = this.getValue();
24205
24206         if(this.inputType == 'radio'){
24207             
24208             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24209                 e.dom.checked = false;
24210             });
24211             
24212             this.inputEl().dom.checked = true;
24213             
24214             this.inputEl().dom.value = this.inputValue;
24215             
24216             if(suppressEvent !== true){
24217                 this.fireEvent('check', this, true);
24218             }
24219             
24220             this.validate();
24221             
24222             return;
24223         }
24224         
24225         this.checked = state;
24226         
24227         this.inputEl().dom.checked = state;
24228         
24229         
24230         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
24231         
24232         if(suppressEvent !== true){
24233             this.fireEvent('check', this, state);
24234         }
24235         
24236         this.validate();
24237     },
24238     
24239     getValue : function()
24240     {
24241         if(this.inputType == 'radio'){
24242             return this.getGroupValue();
24243         }
24244         
24245         return this.hiddenEl().dom.value;
24246         
24247     },
24248     
24249     getGroupValue : function()
24250     {
24251         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
24252             return '';
24253         }
24254         
24255         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
24256     },
24257     
24258     setValue : function(v,suppressEvent)
24259     {
24260         if(this.inputType == 'radio'){
24261             this.setGroupValue(v, suppressEvent);
24262             return;
24263         }
24264         
24265         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
24266         
24267         this.validate();
24268     },
24269     
24270     setGroupValue : function(v, suppressEvent)
24271     {
24272         this.startValue = this.getValue();
24273         
24274         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24275             e.dom.checked = false;
24276             
24277             if(e.dom.value == v){
24278                 e.dom.checked = true;
24279             }
24280         });
24281         
24282         if(suppressEvent !== true){
24283             this.fireEvent('check', this, true);
24284         }
24285
24286         this.validate();
24287         
24288         return;
24289     },
24290     
24291     validate : function()
24292     {
24293         if(this.getVisibilityEl().hasClass('hidden')){
24294             return true;
24295         }
24296         
24297         if(
24298                 this.disabled || 
24299                 (this.inputType == 'radio' && this.validateRadio()) ||
24300                 (this.inputType == 'checkbox' && this.validateCheckbox())
24301         ){
24302             this.markValid();
24303             return true;
24304         }
24305         
24306         this.markInvalid();
24307         return false;
24308     },
24309     
24310     validateRadio : function()
24311     {
24312         if(this.getVisibilityEl().hasClass('hidden')){
24313             return true;
24314         }
24315         
24316         if(this.allowBlank){
24317             return true;
24318         }
24319         
24320         var valid = false;
24321         
24322         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24323             if(!e.dom.checked){
24324                 return;
24325             }
24326             
24327             valid = true;
24328             
24329             return false;
24330         });
24331         
24332         return valid;
24333     },
24334     
24335     validateCheckbox : function()
24336     {
24337         if(!this.groupId){
24338             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
24339             //return (this.getValue() == this.inputValue) ? true : false;
24340         }
24341         
24342         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24343         
24344         if(!group){
24345             return false;
24346         }
24347         
24348         var r = false;
24349         
24350         for(var i in group){
24351             if(group[i].el.isVisible(true)){
24352                 r = false;
24353                 break;
24354             }
24355             
24356             r = true;
24357         }
24358         
24359         for(var i in group){
24360             if(r){
24361                 break;
24362             }
24363             
24364             r = (group[i].getValue() == group[i].inputValue) ? true : false;
24365         }
24366         
24367         return r;
24368     },
24369     
24370     /**
24371      * Mark this field as valid
24372      */
24373     markValid : function()
24374     {
24375         var _this = this;
24376         
24377         this.fireEvent('valid', this);
24378         
24379         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24380         
24381         if(this.groupId){
24382             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24383         }
24384         
24385         if(label){
24386             label.markValid();
24387         }
24388
24389         if(this.inputType == 'radio'){
24390             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24391                 var fg = e.findParent('.form-group', false, true);
24392                 if (Roo.bootstrap.version == 3) {
24393                     fg.removeClass([_this.invalidClass, _this.validClass]);
24394                     fg.addClass(_this.validClass);
24395                 } else {
24396                     fg.removeClass(['is-valid', 'is-invalid']);
24397                     fg.addClass('is-valid');
24398                 }
24399             });
24400             
24401             return;
24402         }
24403
24404         if(!this.groupId){
24405             var fg = this.el.findParent('.form-group', false, true);
24406             if (Roo.bootstrap.version == 3) {
24407                 fg.removeClass([this.invalidClass, this.validClass]);
24408                 fg.addClass(this.validClass);
24409             } else {
24410                 fg.removeClass(['is-valid', 'is-invalid']);
24411                 fg.addClass('is-valid');
24412             }
24413             return;
24414         }
24415         
24416         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24417         
24418         if(!group){
24419             return;
24420         }
24421         
24422         for(var i in group){
24423             var fg = group[i].el.findParent('.form-group', false, true);
24424             if (Roo.bootstrap.version == 3) {
24425                 fg.removeClass([this.invalidClass, this.validClass]);
24426                 fg.addClass(this.validClass);
24427             } else {
24428                 fg.removeClass(['is-valid', 'is-invalid']);
24429                 fg.addClass('is-valid');
24430             }
24431         }
24432     },
24433     
24434      /**
24435      * Mark this field as invalid
24436      * @param {String} msg The validation message
24437      */
24438     markInvalid : function(msg)
24439     {
24440         if(this.allowBlank){
24441             return;
24442         }
24443         
24444         var _this = this;
24445         
24446         this.fireEvent('invalid', this, msg);
24447         
24448         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24449         
24450         if(this.groupId){
24451             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
24452         }
24453         
24454         if(label){
24455             label.markInvalid();
24456         }
24457             
24458         if(this.inputType == 'radio'){
24459             
24460             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24461                 var fg = e.findParent('.form-group', false, true);
24462                 if (Roo.bootstrap.version == 3) {
24463                     fg.removeClass([_this.invalidClass, _this.validClass]);
24464                     fg.addClass(_this.invalidClass);
24465                 } else {
24466                     fg.removeClass(['is-invalid', 'is-valid']);
24467                     fg.addClass('is-invalid');
24468                 }
24469             });
24470             
24471             return;
24472         }
24473         
24474         if(!this.groupId){
24475             var fg = this.el.findParent('.form-group', false, true);
24476             if (Roo.bootstrap.version == 3) {
24477                 fg.removeClass([_this.invalidClass, _this.validClass]);
24478                 fg.addClass(_this.invalidClass);
24479             } else {
24480                 fg.removeClass(['is-invalid', 'is-valid']);
24481                 fg.addClass('is-invalid');
24482             }
24483             return;
24484         }
24485         
24486         var group = Roo.bootstrap.CheckBox.get(this.groupId);
24487         
24488         if(!group){
24489             return;
24490         }
24491         
24492         for(var i in group){
24493             var fg = group[i].el.findParent('.form-group', false, true);
24494             if (Roo.bootstrap.version == 3) {
24495                 fg.removeClass([_this.invalidClass, _this.validClass]);
24496                 fg.addClass(_this.invalidClass);
24497             } else {
24498                 fg.removeClass(['is-invalid', 'is-valid']);
24499                 fg.addClass('is-invalid');
24500             }
24501         }
24502         
24503     },
24504     
24505     clearInvalid : function()
24506     {
24507         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
24508         
24509         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
24510         
24511         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
24512         
24513         if (label && label.iconEl) {
24514             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
24515             label.iconEl.removeClass(['is-invalid', 'is-valid']);
24516         }
24517     },
24518     
24519     disable : function()
24520     {
24521         if(this.inputType != 'radio'){
24522             Roo.bootstrap.CheckBox.superclass.disable.call(this);
24523             return;
24524         }
24525         
24526         var _this = this;
24527         
24528         if(this.rendered){
24529             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24530                 _this.getActionEl().addClass(this.disabledClass);
24531                 e.dom.disabled = true;
24532             });
24533         }
24534         
24535         this.disabled = true;
24536         this.fireEvent("disable", this);
24537         return this;
24538     },
24539
24540     enable : function()
24541     {
24542         if(this.inputType != 'radio'){
24543             Roo.bootstrap.CheckBox.superclass.enable.call(this);
24544             return;
24545         }
24546         
24547         var _this = this;
24548         
24549         if(this.rendered){
24550             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
24551                 _this.getActionEl().removeClass(this.disabledClass);
24552                 e.dom.disabled = false;
24553             });
24554         }
24555         
24556         this.disabled = false;
24557         this.fireEvent("enable", this);
24558         return this;
24559     },
24560     
24561     setBoxLabel : function(v)
24562     {
24563         this.boxLabel = v;
24564         
24565         if(this.rendered){
24566             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24567         }
24568     }
24569
24570 });
24571
24572 Roo.apply(Roo.bootstrap.CheckBox, {
24573     
24574     groups: {},
24575     
24576      /**
24577     * register a CheckBox Group
24578     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
24579     */
24580     register : function(checkbox)
24581     {
24582         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
24583             this.groups[checkbox.groupId] = {};
24584         }
24585         
24586         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
24587             return;
24588         }
24589         
24590         this.groups[checkbox.groupId][checkbox.name] = checkbox;
24591         
24592     },
24593     /**
24594     * fetch a CheckBox Group based on the group ID
24595     * @param {string} the group ID
24596     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
24597     */
24598     get: function(groupId) {
24599         if (typeof(this.groups[groupId]) == 'undefined') {
24600             return false;
24601         }
24602         
24603         return this.groups[groupId] ;
24604     }
24605     
24606     
24607 });
24608 /*
24609  * - LGPL
24610  *
24611  * RadioItem
24612  * 
24613  */
24614
24615 /**
24616  * @class Roo.bootstrap.Radio
24617  * @extends Roo.bootstrap.Component
24618  * Bootstrap Radio class
24619  * @cfg {String} boxLabel - the label associated
24620  * @cfg {String} value - the value of radio
24621  * 
24622  * @constructor
24623  * Create a new Radio
24624  * @param {Object} config The config object
24625  */
24626 Roo.bootstrap.Radio = function(config){
24627     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24628     
24629 };
24630
24631 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24632     
24633     boxLabel : '',
24634     
24635     value : '',
24636     
24637     getAutoCreate : function()
24638     {
24639         var cfg = {
24640             tag : 'div',
24641             cls : 'form-group radio',
24642             cn : [
24643                 {
24644                     tag : 'label',
24645                     cls : 'box-label',
24646                     html : this.boxLabel
24647                 }
24648             ]
24649         };
24650         
24651         return cfg;
24652     },
24653     
24654     initEvents : function() 
24655     {
24656         this.parent().register(this);
24657         
24658         this.el.on('click', this.onClick, this);
24659         
24660     },
24661     
24662     onClick : function(e)
24663     {
24664         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24665             this.setChecked(true);
24666         }
24667     },
24668     
24669     setChecked : function(state, suppressEvent)
24670     {
24671         this.parent().setValue(this.value, suppressEvent);
24672         
24673     },
24674     
24675     setBoxLabel : function(v)
24676     {
24677         this.boxLabel = v;
24678         
24679         if(this.rendered){
24680             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24681         }
24682     }
24683     
24684 });
24685  
24686
24687  /*
24688  * - LGPL
24689  *
24690  * Input
24691  * 
24692  */
24693
24694 /**
24695  * @class Roo.bootstrap.SecurePass
24696  * @extends Roo.bootstrap.Input
24697  * Bootstrap SecurePass class
24698  *
24699  * 
24700  * @constructor
24701  * Create a new SecurePass
24702  * @param {Object} config The config object
24703  */
24704  
24705 Roo.bootstrap.SecurePass = function (config) {
24706     // these go here, so the translation tool can replace them..
24707     this.errors = {
24708         PwdEmpty: "Please type a password, and then retype it to confirm.",
24709         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24710         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24711         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24712         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24713         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24714         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24715         TooWeak: "Your password is Too Weak."
24716     },
24717     this.meterLabel = "Password strength:";
24718     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24719     this.meterClass = [
24720         "roo-password-meter-tooweak", 
24721         "roo-password-meter-weak", 
24722         "roo-password-meter-medium", 
24723         "roo-password-meter-strong", 
24724         "roo-password-meter-grey"
24725     ];
24726     
24727     this.errors = {};
24728     
24729     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24730 }
24731
24732 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24733     /**
24734      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24735      * {
24736      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24737      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24738      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24739      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24740      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24741      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24742      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24743      * })
24744      */
24745     // private
24746     
24747     meterWidth: 300,
24748     errorMsg :'',    
24749     errors: false,
24750     imageRoot: '/',
24751     /**
24752      * @cfg {String/Object} Label for the strength meter (defaults to
24753      * 'Password strength:')
24754      */
24755     // private
24756     meterLabel: '',
24757     /**
24758      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24759      * ['Weak', 'Medium', 'Strong'])
24760      */
24761     // private    
24762     pwdStrengths: false,    
24763     // private
24764     strength: 0,
24765     // private
24766     _lastPwd: null,
24767     // private
24768     kCapitalLetter: 0,
24769     kSmallLetter: 1,
24770     kDigit: 2,
24771     kPunctuation: 3,
24772     
24773     insecure: false,
24774     // private
24775     initEvents: function ()
24776     {
24777         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24778
24779         if (this.el.is('input[type=password]') && Roo.isSafari) {
24780             this.el.on('keydown', this.SafariOnKeyDown, this);
24781         }
24782
24783         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24784     },
24785     // private
24786     onRender: function (ct, position)
24787     {
24788         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24789         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24790         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24791
24792         this.trigger.createChild({
24793                    cn: [
24794                     {
24795                     //id: 'PwdMeter',
24796                     tag: 'div',
24797                     cls: 'roo-password-meter-grey col-xs-12',
24798                     style: {
24799                         //width: 0,
24800                         //width: this.meterWidth + 'px'                                                
24801                         }
24802                     },
24803                     {                            
24804                          cls: 'roo-password-meter-text'                          
24805                     }
24806                 ]            
24807         });
24808
24809          
24810         if (this.hideTrigger) {
24811             this.trigger.setDisplayed(false);
24812         }
24813         this.setSize(this.width || '', this.height || '');
24814     },
24815     // private
24816     onDestroy: function ()
24817     {
24818         if (this.trigger) {
24819             this.trigger.removeAllListeners();
24820             this.trigger.remove();
24821         }
24822         if (this.wrap) {
24823             this.wrap.remove();
24824         }
24825         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24826     },
24827     // private
24828     checkStrength: function ()
24829     {
24830         var pwd = this.inputEl().getValue();
24831         if (pwd == this._lastPwd) {
24832             return;
24833         }
24834
24835         var strength;
24836         if (this.ClientSideStrongPassword(pwd)) {
24837             strength = 3;
24838         } else if (this.ClientSideMediumPassword(pwd)) {
24839             strength = 2;
24840         } else if (this.ClientSideWeakPassword(pwd)) {
24841             strength = 1;
24842         } else {
24843             strength = 0;
24844         }
24845         
24846         Roo.log('strength1: ' + strength);
24847         
24848         //var pm = this.trigger.child('div/div/div').dom;
24849         var pm = this.trigger.child('div/div');
24850         pm.removeClass(this.meterClass);
24851         pm.addClass(this.meterClass[strength]);
24852                 
24853         
24854         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24855                 
24856         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24857         
24858         this._lastPwd = pwd;
24859     },
24860     reset: function ()
24861     {
24862         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24863         
24864         this._lastPwd = '';
24865         
24866         var pm = this.trigger.child('div/div');
24867         pm.removeClass(this.meterClass);
24868         pm.addClass('roo-password-meter-grey');        
24869         
24870         
24871         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24872         
24873         pt.innerHTML = '';
24874         this.inputEl().dom.type='password';
24875     },
24876     // private
24877     validateValue: function (value)
24878     {
24879         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24880             return false;
24881         }
24882         if (value.length == 0) {
24883             if (this.allowBlank) {
24884                 this.clearInvalid();
24885                 return true;
24886             }
24887
24888             this.markInvalid(this.errors.PwdEmpty);
24889             this.errorMsg = this.errors.PwdEmpty;
24890             return false;
24891         }
24892         
24893         if(this.insecure){
24894             return true;
24895         }
24896         
24897         if (!value.match(/[\x21-\x7e]+/)) {
24898             this.markInvalid(this.errors.PwdBadChar);
24899             this.errorMsg = this.errors.PwdBadChar;
24900             return false;
24901         }
24902         if (value.length < 6) {
24903             this.markInvalid(this.errors.PwdShort);
24904             this.errorMsg = this.errors.PwdShort;
24905             return false;
24906         }
24907         if (value.length > 16) {
24908             this.markInvalid(this.errors.PwdLong);
24909             this.errorMsg = this.errors.PwdLong;
24910             return false;
24911         }
24912         var strength;
24913         if (this.ClientSideStrongPassword(value)) {
24914             strength = 3;
24915         } else if (this.ClientSideMediumPassword(value)) {
24916             strength = 2;
24917         } else if (this.ClientSideWeakPassword(value)) {
24918             strength = 1;
24919         } else {
24920             strength = 0;
24921         }
24922
24923         
24924         if (strength < 2) {
24925             //this.markInvalid(this.errors.TooWeak);
24926             this.errorMsg = this.errors.TooWeak;
24927             //return false;
24928         }
24929         
24930         
24931         console.log('strength2: ' + strength);
24932         
24933         //var pm = this.trigger.child('div/div/div').dom;
24934         
24935         var pm = this.trigger.child('div/div');
24936         pm.removeClass(this.meterClass);
24937         pm.addClass(this.meterClass[strength]);
24938                 
24939         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24940                 
24941         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24942         
24943         this.errorMsg = ''; 
24944         return true;
24945     },
24946     // private
24947     CharacterSetChecks: function (type)
24948     {
24949         this.type = type;
24950         this.fResult = false;
24951     },
24952     // private
24953     isctype: function (character, type)
24954     {
24955         switch (type) {  
24956             case this.kCapitalLetter:
24957                 if (character >= 'A' && character <= 'Z') {
24958                     return true;
24959                 }
24960                 break;
24961             
24962             case this.kSmallLetter:
24963                 if (character >= 'a' && character <= 'z') {
24964                     return true;
24965                 }
24966                 break;
24967             
24968             case this.kDigit:
24969                 if (character >= '0' && character <= '9') {
24970                     return true;
24971                 }
24972                 break;
24973             
24974             case this.kPunctuation:
24975                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24976                     return true;
24977                 }
24978                 break;
24979             
24980             default:
24981                 return false;
24982         }
24983
24984     },
24985     // private
24986     IsLongEnough: function (pwd, size)
24987     {
24988         return !(pwd == null || isNaN(size) || pwd.length < size);
24989     },
24990     // private
24991     SpansEnoughCharacterSets: function (word, nb)
24992     {
24993         if (!this.IsLongEnough(word, nb))
24994         {
24995             return false;
24996         }
24997
24998         var characterSetChecks = new Array(
24999             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
25000             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
25001         );
25002         
25003         for (var index = 0; index < word.length; ++index) {
25004             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25005                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
25006                     characterSetChecks[nCharSet].fResult = true;
25007                     break;
25008                 }
25009             }
25010         }
25011
25012         var nCharSets = 0;
25013         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
25014             if (characterSetChecks[nCharSet].fResult) {
25015                 ++nCharSets;
25016             }
25017         }
25018
25019         if (nCharSets < nb) {
25020             return false;
25021         }
25022         return true;
25023     },
25024     // private
25025     ClientSideStrongPassword: function (pwd)
25026     {
25027         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
25028     },
25029     // private
25030     ClientSideMediumPassword: function (pwd)
25031     {
25032         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
25033     },
25034     // private
25035     ClientSideWeakPassword: function (pwd)
25036     {
25037         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
25038     }
25039           
25040 })//<script type="text/javascript">
25041
25042 /*
25043  * Based  Ext JS Library 1.1.1
25044  * Copyright(c) 2006-2007, Ext JS, LLC.
25045  * LGPL
25046  *
25047  */
25048  
25049 /**
25050  * @class Roo.HtmlEditorCore
25051  * @extends Roo.Component
25052  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
25053  *
25054  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
25055  */
25056
25057 Roo.HtmlEditorCore = function(config){
25058     
25059     
25060     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
25061     
25062     
25063     this.addEvents({
25064         /**
25065          * @event initialize
25066          * Fires when the editor is fully initialized (including the iframe)
25067          * @param {Roo.HtmlEditorCore} this
25068          */
25069         initialize: true,
25070         /**
25071          * @event activate
25072          * Fires when the editor is first receives the focus. Any insertion must wait
25073          * until after this event.
25074          * @param {Roo.HtmlEditorCore} this
25075          */
25076         activate: true,
25077          /**
25078          * @event beforesync
25079          * Fires before the textarea is updated with content from the editor iframe. Return false
25080          * to cancel the sync.
25081          * @param {Roo.HtmlEditorCore} this
25082          * @param {String} html
25083          */
25084         beforesync: true,
25085          /**
25086          * @event beforepush
25087          * Fires before the iframe editor is updated with content from the textarea. Return false
25088          * to cancel the push.
25089          * @param {Roo.HtmlEditorCore} this
25090          * @param {String} html
25091          */
25092         beforepush: true,
25093          /**
25094          * @event sync
25095          * Fires when the textarea is updated with content from the editor iframe.
25096          * @param {Roo.HtmlEditorCore} this
25097          * @param {String} html
25098          */
25099         sync: true,
25100          /**
25101          * @event push
25102          * Fires when the iframe editor is updated with content from the textarea.
25103          * @param {Roo.HtmlEditorCore} this
25104          * @param {String} html
25105          */
25106         push: true,
25107         
25108         /**
25109          * @event editorevent
25110          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25111          * @param {Roo.HtmlEditorCore} this
25112          */
25113         editorevent: true
25114         
25115     });
25116     
25117     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
25118     
25119     // defaults : white / black...
25120     this.applyBlacklists();
25121     
25122     
25123     
25124 };
25125
25126
25127 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
25128
25129
25130      /**
25131      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
25132      */
25133     
25134     owner : false,
25135     
25136      /**
25137      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25138      *                        Roo.resizable.
25139      */
25140     resizable : false,
25141      /**
25142      * @cfg {Number} height (in pixels)
25143      */   
25144     height: 300,
25145    /**
25146      * @cfg {Number} width (in pixels)
25147      */   
25148     width: 500,
25149     
25150     /**
25151      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25152      * 
25153      */
25154     stylesheets: false,
25155     
25156     // id of frame..
25157     frameId: false,
25158     
25159     // private properties
25160     validationEvent : false,
25161     deferHeight: true,
25162     initialized : false,
25163     activated : false,
25164     sourceEditMode : false,
25165     onFocus : Roo.emptyFn,
25166     iframePad:3,
25167     hideMode:'offsets',
25168     
25169     clearUp: true,
25170     
25171     // blacklist + whitelisted elements..
25172     black: false,
25173     white: false,
25174      
25175     bodyCls : '',
25176
25177     /**
25178      * Protected method that will not generally be called directly. It
25179      * is called when the editor initializes the iframe with HTML contents. Override this method if you
25180      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
25181      */
25182     getDocMarkup : function(){
25183         // body styles..
25184         var st = '';
25185         
25186         // inherit styels from page...?? 
25187         if (this.stylesheets === false) {
25188             
25189             Roo.get(document.head).select('style').each(function(node) {
25190                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25191             });
25192             
25193             Roo.get(document.head).select('link').each(function(node) { 
25194                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
25195             });
25196             
25197         } else if (!this.stylesheets.length) {
25198                 // simple..
25199                 st = '<style type="text/css">' +
25200                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25201                    '</style>';
25202         } else {
25203             for (var i in this.stylesheets) { 
25204                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
25205             }
25206             
25207         }
25208         
25209         st +=  '<style type="text/css">' +
25210             'IMG { cursor: pointer } ' +
25211         '</style>';
25212
25213         var cls = 'roo-htmleditor-body';
25214         
25215         if(this.bodyCls.length){
25216             cls += ' ' + this.bodyCls;
25217         }
25218         
25219         return '<html><head>' + st  +
25220             //<style type="text/css">' +
25221             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
25222             //'</style>' +
25223             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
25224     },
25225
25226     // private
25227     onRender : function(ct, position)
25228     {
25229         var _t = this;
25230         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
25231         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
25232         
25233         
25234         this.el.dom.style.border = '0 none';
25235         this.el.dom.setAttribute('tabIndex', -1);
25236         this.el.addClass('x-hidden hide');
25237         
25238         
25239         
25240         if(Roo.isIE){ // fix IE 1px bogus margin
25241             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
25242         }
25243        
25244         
25245         this.frameId = Roo.id();
25246         
25247          
25248         
25249         var iframe = this.owner.wrap.createChild({
25250             tag: 'iframe',
25251             cls: 'form-control', // bootstrap..
25252             id: this.frameId,
25253             name: this.frameId,
25254             frameBorder : 'no',
25255             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
25256         }, this.el
25257         );
25258         
25259         
25260         this.iframe = iframe.dom;
25261
25262          this.assignDocWin();
25263         
25264         this.doc.designMode = 'on';
25265        
25266         this.doc.open();
25267         this.doc.write(this.getDocMarkup());
25268         this.doc.close();
25269
25270         
25271         var task = { // must defer to wait for browser to be ready
25272             run : function(){
25273                 //console.log("run task?" + this.doc.readyState);
25274                 this.assignDocWin();
25275                 if(this.doc.body || this.doc.readyState == 'complete'){
25276                     try {
25277                         this.doc.designMode="on";
25278                     } catch (e) {
25279                         return;
25280                     }
25281                     Roo.TaskMgr.stop(task);
25282                     this.initEditor.defer(10, this);
25283                 }
25284             },
25285             interval : 10,
25286             duration: 10000,
25287             scope: this
25288         };
25289         Roo.TaskMgr.start(task);
25290
25291     },
25292
25293     // private
25294     onResize : function(w, h)
25295     {
25296          Roo.log('resize: ' +w + ',' + h );
25297         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
25298         if(!this.iframe){
25299             return;
25300         }
25301         if(typeof w == 'number'){
25302             
25303             this.iframe.style.width = w + 'px';
25304         }
25305         if(typeof h == 'number'){
25306             
25307             this.iframe.style.height = h + 'px';
25308             if(this.doc){
25309                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
25310             }
25311         }
25312         
25313     },
25314
25315     /**
25316      * Toggles the editor between standard and source edit mode.
25317      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25318      */
25319     toggleSourceEdit : function(sourceEditMode){
25320         
25321         this.sourceEditMode = sourceEditMode === true;
25322         
25323         if(this.sourceEditMode){
25324  
25325             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
25326             
25327         }else{
25328             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
25329             //this.iframe.className = '';
25330             this.deferFocus();
25331         }
25332         //this.setSize(this.owner.wrap.getSize());
25333         //this.fireEvent('editmodechange', this, this.sourceEditMode);
25334     },
25335
25336     
25337   
25338
25339     /**
25340      * Protected method that will not generally be called directly. If you need/want
25341      * custom HTML cleanup, this is the method you should override.
25342      * @param {String} html The HTML to be cleaned
25343      * return {String} The cleaned HTML
25344      */
25345     cleanHtml : function(html){
25346         html = String(html);
25347         if(html.length > 5){
25348             if(Roo.isSafari){ // strip safari nonsense
25349                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
25350             }
25351         }
25352         if(html == '&nbsp;'){
25353             html = '';
25354         }
25355         return html;
25356     },
25357
25358     /**
25359      * HTML Editor -> Textarea
25360      * Protected method that will not generally be called directly. Syncs the contents
25361      * of the editor iframe with the textarea.
25362      */
25363     syncValue : function(){
25364         if(this.initialized){
25365             var bd = (this.doc.body || this.doc.documentElement);
25366             //this.cleanUpPaste(); -- this is done else where and causes havoc..
25367             var html = bd.innerHTML;
25368             if(Roo.isSafari){
25369                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
25370                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
25371                 if(m && m[1]){
25372                     html = '<div style="'+m[0]+'">' + html + '</div>';
25373                 }
25374             }
25375             html = this.cleanHtml(html);
25376             // fix up the special chars.. normaly like back quotes in word...
25377             // however we do not want to do this with chinese..
25378             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
25379                 
25380                 var cc = match.charCodeAt();
25381
25382                 // Get the character value, handling surrogate pairs
25383                 if (match.length == 2) {
25384                     // It's a surrogate pair, calculate the Unicode code point
25385                     var high = match.charCodeAt(0) - 0xD800;
25386                     var low  = match.charCodeAt(1) - 0xDC00;
25387                     cc = (high * 0x400) + low + 0x10000;
25388                 }  else if (
25389                     (cc >= 0x4E00 && cc < 0xA000 ) ||
25390                     (cc >= 0x3400 && cc < 0x4E00 ) ||
25391                     (cc >= 0xf900 && cc < 0xfb00 )
25392                 ) {
25393                         return match;
25394                 }  
25395          
25396                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
25397                 return "&#" + cc + ";";
25398                 
25399                 
25400             });
25401             
25402             
25403              
25404             if(this.owner.fireEvent('beforesync', this, html) !== false){
25405                 this.el.dom.value = html;
25406                 this.owner.fireEvent('sync', this, html);
25407             }
25408         }
25409     },
25410
25411     /**
25412      * Protected method that will not generally be called directly. Pushes the value of the textarea
25413      * into the iframe editor.
25414      */
25415     pushValue : function(){
25416         if(this.initialized){
25417             var v = this.el.dom.value.trim();
25418             
25419 //            if(v.length < 1){
25420 //                v = '&#160;';
25421 //            }
25422             
25423             if(this.owner.fireEvent('beforepush', this, v) !== false){
25424                 var d = (this.doc.body || this.doc.documentElement);
25425                 d.innerHTML = v;
25426                 this.cleanUpPaste();
25427                 this.el.dom.value = d.innerHTML;
25428                 this.owner.fireEvent('push', this, v);
25429             }
25430         }
25431     },
25432
25433     // private
25434     deferFocus : function(){
25435         this.focus.defer(10, this);
25436     },
25437
25438     // doc'ed in Field
25439     focus : function(){
25440         if(this.win && !this.sourceEditMode){
25441             this.win.focus();
25442         }else{
25443             this.el.focus();
25444         }
25445     },
25446     
25447     assignDocWin: function()
25448     {
25449         var iframe = this.iframe;
25450         
25451          if(Roo.isIE){
25452             this.doc = iframe.contentWindow.document;
25453             this.win = iframe.contentWindow;
25454         } else {
25455 //            if (!Roo.get(this.frameId)) {
25456 //                return;
25457 //            }
25458 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25459 //            this.win = Roo.get(this.frameId).dom.contentWindow;
25460             
25461             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
25462                 return;
25463             }
25464             
25465             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
25466             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
25467         }
25468     },
25469     
25470     // private
25471     initEditor : function(){
25472         //console.log("INIT EDITOR");
25473         this.assignDocWin();
25474         
25475         
25476         
25477         this.doc.designMode="on";
25478         this.doc.open();
25479         this.doc.write(this.getDocMarkup());
25480         this.doc.close();
25481         
25482         var dbody = (this.doc.body || this.doc.documentElement);
25483         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
25484         // this copies styles from the containing element into thsi one..
25485         // not sure why we need all of this..
25486         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
25487         
25488         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
25489         //ss['background-attachment'] = 'fixed'; // w3c
25490         dbody.bgProperties = 'fixed'; // ie
25491         //Roo.DomHelper.applyStyles(dbody, ss);
25492         Roo.EventManager.on(this.doc, {
25493             //'mousedown': this.onEditorEvent,
25494             'mouseup': this.onEditorEvent,
25495             'dblclick': this.onEditorEvent,
25496             'click': this.onEditorEvent,
25497             'keyup': this.onEditorEvent,
25498             buffer:100,
25499             scope: this
25500         });
25501         if(Roo.isGecko){
25502             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
25503         }
25504         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
25505             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
25506         }
25507         this.initialized = true;
25508
25509         this.owner.fireEvent('initialize', this);
25510         this.pushValue();
25511     },
25512
25513     // private
25514     onDestroy : function(){
25515         
25516         
25517         
25518         if(this.rendered){
25519             
25520             //for (var i =0; i < this.toolbars.length;i++) {
25521             //    // fixme - ask toolbars for heights?
25522             //    this.toolbars[i].onDestroy();
25523            // }
25524             
25525             //this.wrap.dom.innerHTML = '';
25526             //this.wrap.remove();
25527         }
25528     },
25529
25530     // private
25531     onFirstFocus : function(){
25532         
25533         this.assignDocWin();
25534         
25535         
25536         this.activated = true;
25537          
25538     
25539         if(Roo.isGecko){ // prevent silly gecko errors
25540             this.win.focus();
25541             var s = this.win.getSelection();
25542             if(!s.focusNode || s.focusNode.nodeType != 3){
25543                 var r = s.getRangeAt(0);
25544                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
25545                 r.collapse(true);
25546                 this.deferFocus();
25547             }
25548             try{
25549                 this.execCmd('useCSS', true);
25550                 this.execCmd('styleWithCSS', false);
25551             }catch(e){}
25552         }
25553         this.owner.fireEvent('activate', this);
25554     },
25555
25556     // private
25557     adjustFont: function(btn){
25558         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
25559         //if(Roo.isSafari){ // safari
25560         //    adjust *= 2;
25561        // }
25562         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
25563         if(Roo.isSafari){ // safari
25564             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
25565             v =  (v < 10) ? 10 : v;
25566             v =  (v > 48) ? 48 : v;
25567             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
25568             
25569         }
25570         
25571         
25572         v = Math.max(1, v+adjust);
25573         
25574         this.execCmd('FontSize', v  );
25575     },
25576
25577     onEditorEvent : function(e)
25578     {
25579         this.owner.fireEvent('editorevent', this, e);
25580       //  this.updateToolbar();
25581         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
25582     },
25583
25584     insertTag : function(tg)
25585     {
25586         // could be a bit smarter... -> wrap the current selected tRoo..
25587         if (tg.toLowerCase() == 'span' ||
25588             tg.toLowerCase() == 'code' ||
25589             tg.toLowerCase() == 'sup' ||
25590             tg.toLowerCase() == 'sub' 
25591             ) {
25592             
25593             range = this.createRange(this.getSelection());
25594             var wrappingNode = this.doc.createElement(tg.toLowerCase());
25595             wrappingNode.appendChild(range.extractContents());
25596             range.insertNode(wrappingNode);
25597
25598             return;
25599             
25600             
25601             
25602         }
25603         this.execCmd("formatblock",   tg);
25604         
25605     },
25606     
25607     insertText : function(txt)
25608     {
25609         
25610         
25611         var range = this.createRange();
25612         range.deleteContents();
25613                //alert(Sender.getAttribute('label'));
25614                
25615         range.insertNode(this.doc.createTextNode(txt));
25616     } ,
25617     
25618      
25619
25620     /**
25621      * Executes a Midas editor command on the editor document and performs necessary focus and
25622      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25623      * @param {String} cmd The Midas command
25624      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25625      */
25626     relayCmd : function(cmd, value){
25627         this.win.focus();
25628         this.execCmd(cmd, value);
25629         this.owner.fireEvent('editorevent', this);
25630         //this.updateToolbar();
25631         this.owner.deferFocus();
25632     },
25633
25634     /**
25635      * Executes a Midas editor command directly on the editor document.
25636      * For visual commands, you should use {@link #relayCmd} instead.
25637      * <b>This should only be called after the editor is initialized.</b>
25638      * @param {String} cmd The Midas command
25639      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25640      */
25641     execCmd : function(cmd, value){
25642         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25643         this.syncValue();
25644     },
25645  
25646  
25647    
25648     /**
25649      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25650      * to insert tRoo.
25651      * @param {String} text | dom node.. 
25652      */
25653     insertAtCursor : function(text)
25654     {
25655         
25656         if(!this.activated){
25657             return;
25658         }
25659         /*
25660         if(Roo.isIE){
25661             this.win.focus();
25662             var r = this.doc.selection.createRange();
25663             if(r){
25664                 r.collapse(true);
25665                 r.pasteHTML(text);
25666                 this.syncValue();
25667                 this.deferFocus();
25668             
25669             }
25670             return;
25671         }
25672         */
25673         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25674             this.win.focus();
25675             
25676             
25677             // from jquery ui (MIT licenced)
25678             var range, node;
25679             var win = this.win;
25680             
25681             if (win.getSelection && win.getSelection().getRangeAt) {
25682                 range = win.getSelection().getRangeAt(0);
25683                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25684                 range.insertNode(node);
25685             } else if (win.document.selection && win.document.selection.createRange) {
25686                 // no firefox support
25687                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25688                 win.document.selection.createRange().pasteHTML(txt);
25689             } else {
25690                 // no firefox support
25691                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25692                 this.execCmd('InsertHTML', txt);
25693             } 
25694             
25695             this.syncValue();
25696             
25697             this.deferFocus();
25698         }
25699     },
25700  // private
25701     mozKeyPress : function(e){
25702         if(e.ctrlKey){
25703             var c = e.getCharCode(), cmd;
25704           
25705             if(c > 0){
25706                 c = String.fromCharCode(c).toLowerCase();
25707                 switch(c){
25708                     case 'b':
25709                         cmd = 'bold';
25710                         break;
25711                     case 'i':
25712                         cmd = 'italic';
25713                         break;
25714                     
25715                     case 'u':
25716                         cmd = 'underline';
25717                         break;
25718                     
25719                     case 'v':
25720                         this.cleanUpPaste.defer(100, this);
25721                         return;
25722                         
25723                 }
25724                 if(cmd){
25725                     this.win.focus();
25726                     this.execCmd(cmd);
25727                     this.deferFocus();
25728                     e.preventDefault();
25729                 }
25730                 
25731             }
25732         }
25733     },
25734
25735     // private
25736     fixKeys : function(){ // load time branching for fastest keydown performance
25737         if(Roo.isIE){
25738             return function(e){
25739                 var k = e.getKey(), r;
25740                 if(k == e.TAB){
25741                     e.stopEvent();
25742                     r = this.doc.selection.createRange();
25743                     if(r){
25744                         r.collapse(true);
25745                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25746                         this.deferFocus();
25747                     }
25748                     return;
25749                 }
25750                 
25751                 if(k == e.ENTER){
25752                     r = this.doc.selection.createRange();
25753                     if(r){
25754                         var target = r.parentElement();
25755                         if(!target || target.tagName.toLowerCase() != 'li'){
25756                             e.stopEvent();
25757                             r.pasteHTML('<br />');
25758                             r.collapse(false);
25759                             r.select();
25760                         }
25761                     }
25762                 }
25763                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25764                     this.cleanUpPaste.defer(100, this);
25765                     return;
25766                 }
25767                 
25768                 
25769             };
25770         }else if(Roo.isOpera){
25771             return function(e){
25772                 var k = e.getKey();
25773                 if(k == e.TAB){
25774                     e.stopEvent();
25775                     this.win.focus();
25776                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25777                     this.deferFocus();
25778                 }
25779                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25780                     this.cleanUpPaste.defer(100, this);
25781                     return;
25782                 }
25783                 
25784             };
25785         }else if(Roo.isSafari){
25786             return function(e){
25787                 var k = e.getKey();
25788                 
25789                 if(k == e.TAB){
25790                     e.stopEvent();
25791                     this.execCmd('InsertText','\t');
25792                     this.deferFocus();
25793                     return;
25794                 }
25795                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25796                     this.cleanUpPaste.defer(100, this);
25797                     return;
25798                 }
25799                 
25800              };
25801         }
25802     }(),
25803     
25804     getAllAncestors: function()
25805     {
25806         var p = this.getSelectedNode();
25807         var a = [];
25808         if (!p) {
25809             a.push(p); // push blank onto stack..
25810             p = this.getParentElement();
25811         }
25812         
25813         
25814         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25815             a.push(p);
25816             p = p.parentNode;
25817         }
25818         a.push(this.doc.body);
25819         return a;
25820     },
25821     lastSel : false,
25822     lastSelNode : false,
25823     
25824     
25825     getSelection : function() 
25826     {
25827         this.assignDocWin();
25828         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25829     },
25830     
25831     getSelectedNode: function() 
25832     {
25833         // this may only work on Gecko!!!
25834         
25835         // should we cache this!!!!
25836         
25837         
25838         
25839          
25840         var range = this.createRange(this.getSelection()).cloneRange();
25841         
25842         if (Roo.isIE) {
25843             var parent = range.parentElement();
25844             while (true) {
25845                 var testRange = range.duplicate();
25846                 testRange.moveToElementText(parent);
25847                 if (testRange.inRange(range)) {
25848                     break;
25849                 }
25850                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25851                     break;
25852                 }
25853                 parent = parent.parentElement;
25854             }
25855             return parent;
25856         }
25857         
25858         // is ancestor a text element.
25859         var ac =  range.commonAncestorContainer;
25860         if (ac.nodeType == 3) {
25861             ac = ac.parentNode;
25862         }
25863         
25864         var ar = ac.childNodes;
25865          
25866         var nodes = [];
25867         var other_nodes = [];
25868         var has_other_nodes = false;
25869         for (var i=0;i<ar.length;i++) {
25870             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25871                 continue;
25872             }
25873             // fullly contained node.
25874             
25875             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25876                 nodes.push(ar[i]);
25877                 continue;
25878             }
25879             
25880             // probably selected..
25881             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25882                 other_nodes.push(ar[i]);
25883                 continue;
25884             }
25885             // outer..
25886             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25887                 continue;
25888             }
25889             
25890             
25891             has_other_nodes = true;
25892         }
25893         if (!nodes.length && other_nodes.length) {
25894             nodes= other_nodes;
25895         }
25896         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25897             return false;
25898         }
25899         
25900         return nodes[0];
25901     },
25902     createRange: function(sel)
25903     {
25904         // this has strange effects when using with 
25905         // top toolbar - not sure if it's a great idea.
25906         //this.editor.contentWindow.focus();
25907         if (typeof sel != "undefined") {
25908             try {
25909                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25910             } catch(e) {
25911                 return this.doc.createRange();
25912             }
25913         } else {
25914             return this.doc.createRange();
25915         }
25916     },
25917     getParentElement: function()
25918     {
25919         
25920         this.assignDocWin();
25921         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25922         
25923         var range = this.createRange(sel);
25924          
25925         try {
25926             var p = range.commonAncestorContainer;
25927             while (p.nodeType == 3) { // text node
25928                 p = p.parentNode;
25929             }
25930             return p;
25931         } catch (e) {
25932             return null;
25933         }
25934     
25935     },
25936     /***
25937      *
25938      * Range intersection.. the hard stuff...
25939      *  '-1' = before
25940      *  '0' = hits..
25941      *  '1' = after.
25942      *         [ -- selected range --- ]
25943      *   [fail]                        [fail]
25944      *
25945      *    basically..
25946      *      if end is before start or  hits it. fail.
25947      *      if start is after end or hits it fail.
25948      *
25949      *   if either hits (but other is outside. - then it's not 
25950      *   
25951      *    
25952      **/
25953     
25954     
25955     // @see http://www.thismuchiknow.co.uk/?p=64.
25956     rangeIntersectsNode : function(range, node)
25957     {
25958         var nodeRange = node.ownerDocument.createRange();
25959         try {
25960             nodeRange.selectNode(node);
25961         } catch (e) {
25962             nodeRange.selectNodeContents(node);
25963         }
25964     
25965         var rangeStartRange = range.cloneRange();
25966         rangeStartRange.collapse(true);
25967     
25968         var rangeEndRange = range.cloneRange();
25969         rangeEndRange.collapse(false);
25970     
25971         var nodeStartRange = nodeRange.cloneRange();
25972         nodeStartRange.collapse(true);
25973     
25974         var nodeEndRange = nodeRange.cloneRange();
25975         nodeEndRange.collapse(false);
25976     
25977         return rangeStartRange.compareBoundaryPoints(
25978                  Range.START_TO_START, nodeEndRange) == -1 &&
25979                rangeEndRange.compareBoundaryPoints(
25980                  Range.START_TO_START, nodeStartRange) == 1;
25981         
25982          
25983     },
25984     rangeCompareNode : function(range, node)
25985     {
25986         var nodeRange = node.ownerDocument.createRange();
25987         try {
25988             nodeRange.selectNode(node);
25989         } catch (e) {
25990             nodeRange.selectNodeContents(node);
25991         }
25992         
25993         
25994         range.collapse(true);
25995     
25996         nodeRange.collapse(true);
25997      
25998         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25999         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
26000          
26001         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
26002         
26003         var nodeIsBefore   =  ss == 1;
26004         var nodeIsAfter    = ee == -1;
26005         
26006         if (nodeIsBefore && nodeIsAfter) {
26007             return 0; // outer
26008         }
26009         if (!nodeIsBefore && nodeIsAfter) {
26010             return 1; //right trailed.
26011         }
26012         
26013         if (nodeIsBefore && !nodeIsAfter) {
26014             return 2;  // left trailed.
26015         }
26016         // fully contined.
26017         return 3;
26018     },
26019
26020     // private? - in a new class?
26021     cleanUpPaste :  function()
26022     {
26023         // cleans up the whole document..
26024         Roo.log('cleanuppaste');
26025         
26026         this.cleanUpChildren(this.doc.body);
26027         var clean = this.cleanWordChars(this.doc.body.innerHTML);
26028         if (clean != this.doc.body.innerHTML) {
26029             this.doc.body.innerHTML = clean;
26030         }
26031         
26032     },
26033     
26034     cleanWordChars : function(input) {// change the chars to hex code
26035         var he = Roo.HtmlEditorCore;
26036         
26037         var output = input;
26038         Roo.each(he.swapCodes, function(sw) { 
26039             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
26040             
26041             output = output.replace(swapper, sw[1]);
26042         });
26043         
26044         return output;
26045     },
26046     
26047     
26048     cleanUpChildren : function (n)
26049     {
26050         if (!n.childNodes.length) {
26051             return;
26052         }
26053         for (var i = n.childNodes.length-1; i > -1 ; i--) {
26054            this.cleanUpChild(n.childNodes[i]);
26055         }
26056     },
26057     
26058     
26059         
26060     
26061     cleanUpChild : function (node)
26062     {
26063         var ed = this;
26064         //console.log(node);
26065         if (node.nodeName == "#text") {
26066             // clean up silly Windows -- stuff?
26067             return; 
26068         }
26069         if (node.nodeName == "#comment") {
26070             node.parentNode.removeChild(node);
26071             // clean up silly Windows -- stuff?
26072             return; 
26073         }
26074         var lcname = node.tagName.toLowerCase();
26075         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
26076         // whitelist of tags..
26077         
26078         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
26079             // remove node.
26080             node.parentNode.removeChild(node);
26081             return;
26082             
26083         }
26084         
26085         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
26086         
26087         // spans with no attributes - just remove them..
26088         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
26089             remove_keep_children = true;
26090         }
26091         
26092         // remove <a name=....> as rendering on yahoo mailer is borked with this.
26093         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
26094         
26095         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
26096         //    remove_keep_children = true;
26097         //}
26098         
26099         if (remove_keep_children) {
26100             this.cleanUpChildren(node);
26101             // inserts everything just before this node...
26102             while (node.childNodes.length) {
26103                 var cn = node.childNodes[0];
26104                 node.removeChild(cn);
26105                 node.parentNode.insertBefore(cn, node);
26106             }
26107             node.parentNode.removeChild(node);
26108             return;
26109         }
26110         
26111         if (!node.attributes || !node.attributes.length) {
26112             
26113           
26114             
26115             
26116             this.cleanUpChildren(node);
26117             return;
26118         }
26119         
26120         function cleanAttr(n,v)
26121         {
26122             
26123             if (v.match(/^\./) || v.match(/^\//)) {
26124                 return;
26125             }
26126             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
26127                 return;
26128             }
26129             if (v.match(/^#/)) {
26130                 return;
26131             }
26132             if (v.match(/^\{/)) { // allow template editing.
26133                 return;
26134             }
26135 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
26136             node.removeAttribute(n);
26137             
26138         }
26139         
26140         var cwhite = this.cwhite;
26141         var cblack = this.cblack;
26142             
26143         function cleanStyle(n,v)
26144         {
26145             if (v.match(/expression/)) { //XSS?? should we even bother..
26146                 node.removeAttribute(n);
26147                 return;
26148             }
26149             
26150             var parts = v.split(/;/);
26151             var clean = [];
26152             
26153             Roo.each(parts, function(p) {
26154                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
26155                 if (!p.length) {
26156                     return true;
26157                 }
26158                 var l = p.split(':').shift().replace(/\s+/g,'');
26159                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
26160                 
26161                 if ( cwhite.length && cblack.indexOf(l) > -1) {
26162 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26163                     //node.removeAttribute(n);
26164                     return true;
26165                 }
26166                 //Roo.log()
26167                 // only allow 'c whitelisted system attributes'
26168                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
26169 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
26170                     //node.removeAttribute(n);
26171                     return true;
26172                 }
26173                 
26174                 
26175                  
26176                 
26177                 clean.push(p);
26178                 return true;
26179             });
26180             if (clean.length) { 
26181                 node.setAttribute(n, clean.join(';'));
26182             } else {
26183                 node.removeAttribute(n);
26184             }
26185             
26186         }
26187         
26188         
26189         for (var i = node.attributes.length-1; i > -1 ; i--) {
26190             var a = node.attributes[i];
26191             //console.log(a);
26192             
26193             if (a.name.toLowerCase().substr(0,2)=='on')  {
26194                 node.removeAttribute(a.name);
26195                 continue;
26196             }
26197             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
26198                 node.removeAttribute(a.name);
26199                 continue;
26200             }
26201             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
26202                 cleanAttr(a.name,a.value); // fixme..
26203                 continue;
26204             }
26205             if (a.name == 'style') {
26206                 cleanStyle(a.name,a.value);
26207                 continue;
26208             }
26209             /// clean up MS crap..
26210             // tecnically this should be a list of valid class'es..
26211             
26212             
26213             if (a.name == 'class') {
26214                 if (a.value.match(/^Mso/)) {
26215                     node.removeAttribute('class');
26216                 }
26217                 
26218                 if (a.value.match(/^body$/)) {
26219                     node.removeAttribute('class');
26220                 }
26221                 continue;
26222             }
26223             
26224             // style cleanup!?
26225             // class cleanup?
26226             
26227         }
26228         
26229         
26230         this.cleanUpChildren(node);
26231         
26232         
26233     },
26234     
26235     /**
26236      * Clean up MS wordisms...
26237      */
26238     cleanWord : function(node)
26239     {
26240         if (!node) {
26241             this.cleanWord(this.doc.body);
26242             return;
26243         }
26244         
26245         if(
26246                 node.nodeName == 'SPAN' &&
26247                 !node.hasAttributes() &&
26248                 node.childNodes.length == 1 &&
26249                 node.firstChild.nodeName == "#text"  
26250         ) {
26251             var textNode = node.firstChild;
26252             node.removeChild(textNode);
26253             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26254                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
26255             }
26256             node.parentNode.insertBefore(textNode, node);
26257             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
26258                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
26259             }
26260             node.parentNode.removeChild(node);
26261         }
26262         
26263         if (node.nodeName == "#text") {
26264             // clean up silly Windows -- stuff?
26265             return; 
26266         }
26267         if (node.nodeName == "#comment") {
26268             node.parentNode.removeChild(node);
26269             // clean up silly Windows -- stuff?
26270             return; 
26271         }
26272         
26273         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
26274             node.parentNode.removeChild(node);
26275             return;
26276         }
26277         //Roo.log(node.tagName);
26278         // remove - but keep children..
26279         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
26280             //Roo.log('-- removed');
26281             while (node.childNodes.length) {
26282                 var cn = node.childNodes[0];
26283                 node.removeChild(cn);
26284                 node.parentNode.insertBefore(cn, node);
26285                 // move node to parent - and clean it..
26286                 this.cleanWord(cn);
26287             }
26288             node.parentNode.removeChild(node);
26289             /// no need to iterate chidlren = it's got none..
26290             //this.iterateChildren(node, this.cleanWord);
26291             return;
26292         }
26293         // clean styles
26294         if (node.className.length) {
26295             
26296             var cn = node.className.split(/\W+/);
26297             var cna = [];
26298             Roo.each(cn, function(cls) {
26299                 if (cls.match(/Mso[a-zA-Z]+/)) {
26300                     return;
26301                 }
26302                 cna.push(cls);
26303             });
26304             node.className = cna.length ? cna.join(' ') : '';
26305             if (!cna.length) {
26306                 node.removeAttribute("class");
26307             }
26308         }
26309         
26310         if (node.hasAttribute("lang")) {
26311             node.removeAttribute("lang");
26312         }
26313         
26314         if (node.hasAttribute("style")) {
26315             
26316             var styles = node.getAttribute("style").split(";");
26317             var nstyle = [];
26318             Roo.each(styles, function(s) {
26319                 if (!s.match(/:/)) {
26320                     return;
26321                 }
26322                 var kv = s.split(":");
26323                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
26324                     return;
26325                 }
26326                 // what ever is left... we allow.
26327                 nstyle.push(s);
26328             });
26329             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26330             if (!nstyle.length) {
26331                 node.removeAttribute('style');
26332             }
26333         }
26334         this.iterateChildren(node, this.cleanWord);
26335         
26336         
26337         
26338     },
26339     /**
26340      * iterateChildren of a Node, calling fn each time, using this as the scole..
26341      * @param {DomNode} node node to iterate children of.
26342      * @param {Function} fn method of this class to call on each item.
26343      */
26344     iterateChildren : function(node, fn)
26345     {
26346         if (!node.childNodes.length) {
26347                 return;
26348         }
26349         for (var i = node.childNodes.length-1; i > -1 ; i--) {
26350            fn.call(this, node.childNodes[i])
26351         }
26352     },
26353     
26354     
26355     /**
26356      * cleanTableWidths.
26357      *
26358      * Quite often pasting from word etc.. results in tables with column and widths.
26359      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
26360      *
26361      */
26362     cleanTableWidths : function(node)
26363     {
26364          
26365          
26366         if (!node) {
26367             this.cleanTableWidths(this.doc.body);
26368             return;
26369         }
26370         
26371         // ignore list...
26372         if (node.nodeName == "#text" || node.nodeName == "#comment") {
26373             return; 
26374         }
26375         Roo.log(node.tagName);
26376         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
26377             this.iterateChildren(node, this.cleanTableWidths);
26378             return;
26379         }
26380         if (node.hasAttribute('width')) {
26381             node.removeAttribute('width');
26382         }
26383         
26384          
26385         if (node.hasAttribute("style")) {
26386             // pretty basic...
26387             
26388             var styles = node.getAttribute("style").split(";");
26389             var nstyle = [];
26390             Roo.each(styles, function(s) {
26391                 if (!s.match(/:/)) {
26392                     return;
26393                 }
26394                 var kv = s.split(":");
26395                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
26396                     return;
26397                 }
26398                 // what ever is left... we allow.
26399                 nstyle.push(s);
26400             });
26401             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
26402             if (!nstyle.length) {
26403                 node.removeAttribute('style');
26404             }
26405         }
26406         
26407         this.iterateChildren(node, this.cleanTableWidths);
26408         
26409         
26410     },
26411     
26412     
26413     
26414     
26415     domToHTML : function(currentElement, depth, nopadtext) {
26416         
26417         depth = depth || 0;
26418         nopadtext = nopadtext || false;
26419     
26420         if (!currentElement) {
26421             return this.domToHTML(this.doc.body);
26422         }
26423         
26424         //Roo.log(currentElement);
26425         var j;
26426         var allText = false;
26427         var nodeName = currentElement.nodeName;
26428         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
26429         
26430         if  (nodeName == '#text') {
26431             
26432             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
26433         }
26434         
26435         
26436         var ret = '';
26437         if (nodeName != 'BODY') {
26438              
26439             var i = 0;
26440             // Prints the node tagName, such as <A>, <IMG>, etc
26441             if (tagName) {
26442                 var attr = [];
26443                 for(i = 0; i < currentElement.attributes.length;i++) {
26444                     // quoting?
26445                     var aname = currentElement.attributes.item(i).name;
26446                     if (!currentElement.attributes.item(i).value.length) {
26447                         continue;
26448                     }
26449                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
26450                 }
26451                 
26452                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
26453             } 
26454             else {
26455                 
26456                 // eack
26457             }
26458         } else {
26459             tagName = false;
26460         }
26461         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
26462             return ret;
26463         }
26464         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
26465             nopadtext = true;
26466         }
26467         
26468         
26469         // Traverse the tree
26470         i = 0;
26471         var currentElementChild = currentElement.childNodes.item(i);
26472         var allText = true;
26473         var innerHTML  = '';
26474         lastnode = '';
26475         while (currentElementChild) {
26476             // Formatting code (indent the tree so it looks nice on the screen)
26477             var nopad = nopadtext;
26478             if (lastnode == 'SPAN') {
26479                 nopad  = true;
26480             }
26481             // text
26482             if  (currentElementChild.nodeName == '#text') {
26483                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
26484                 toadd = nopadtext ? toadd : toadd.trim();
26485                 if (!nopad && toadd.length > 80) {
26486                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
26487                 }
26488                 innerHTML  += toadd;
26489                 
26490                 i++;
26491                 currentElementChild = currentElement.childNodes.item(i);
26492                 lastNode = '';
26493                 continue;
26494             }
26495             allText = false;
26496             
26497             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
26498                 
26499             // Recursively traverse the tree structure of the child node
26500             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
26501             lastnode = currentElementChild.nodeName;
26502             i++;
26503             currentElementChild=currentElement.childNodes.item(i);
26504         }
26505         
26506         ret += innerHTML;
26507         
26508         if (!allText) {
26509                 // The remaining code is mostly for formatting the tree
26510             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
26511         }
26512         
26513         
26514         if (tagName) {
26515             ret+= "</"+tagName+">";
26516         }
26517         return ret;
26518         
26519     },
26520         
26521     applyBlacklists : function()
26522     {
26523         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
26524         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
26525         
26526         this.white = [];
26527         this.black = [];
26528         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
26529             if (b.indexOf(tag) > -1) {
26530                 return;
26531             }
26532             this.white.push(tag);
26533             
26534         }, this);
26535         
26536         Roo.each(w, function(tag) {
26537             if (b.indexOf(tag) > -1) {
26538                 return;
26539             }
26540             if (this.white.indexOf(tag) > -1) {
26541                 return;
26542             }
26543             this.white.push(tag);
26544             
26545         }, this);
26546         
26547         
26548         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
26549             if (w.indexOf(tag) > -1) {
26550                 return;
26551             }
26552             this.black.push(tag);
26553             
26554         }, this);
26555         
26556         Roo.each(b, function(tag) {
26557             if (w.indexOf(tag) > -1) {
26558                 return;
26559             }
26560             if (this.black.indexOf(tag) > -1) {
26561                 return;
26562             }
26563             this.black.push(tag);
26564             
26565         }, this);
26566         
26567         
26568         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
26569         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
26570         
26571         this.cwhite = [];
26572         this.cblack = [];
26573         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
26574             if (b.indexOf(tag) > -1) {
26575                 return;
26576             }
26577             this.cwhite.push(tag);
26578             
26579         }, this);
26580         
26581         Roo.each(w, function(tag) {
26582             if (b.indexOf(tag) > -1) {
26583                 return;
26584             }
26585             if (this.cwhite.indexOf(tag) > -1) {
26586                 return;
26587             }
26588             this.cwhite.push(tag);
26589             
26590         }, this);
26591         
26592         
26593         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
26594             if (w.indexOf(tag) > -1) {
26595                 return;
26596             }
26597             this.cblack.push(tag);
26598             
26599         }, this);
26600         
26601         Roo.each(b, function(tag) {
26602             if (w.indexOf(tag) > -1) {
26603                 return;
26604             }
26605             if (this.cblack.indexOf(tag) > -1) {
26606                 return;
26607             }
26608             this.cblack.push(tag);
26609             
26610         }, this);
26611     },
26612     
26613     setStylesheets : function(stylesheets)
26614     {
26615         if(typeof(stylesheets) == 'string'){
26616             Roo.get(this.iframe.contentDocument.head).createChild({
26617                 tag : 'link',
26618                 rel : 'stylesheet',
26619                 type : 'text/css',
26620                 href : stylesheets
26621             });
26622             
26623             return;
26624         }
26625         var _this = this;
26626      
26627         Roo.each(stylesheets, function(s) {
26628             if(!s.length){
26629                 return;
26630             }
26631             
26632             Roo.get(_this.iframe.contentDocument.head).createChild({
26633                 tag : 'link',
26634                 rel : 'stylesheet',
26635                 type : 'text/css',
26636                 href : s
26637             });
26638         });
26639
26640         
26641     },
26642     
26643     removeStylesheets : function()
26644     {
26645         var _this = this;
26646         
26647         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26648             s.remove();
26649         });
26650     },
26651     
26652     setStyle : function(style)
26653     {
26654         Roo.get(this.iframe.contentDocument.head).createChild({
26655             tag : 'style',
26656             type : 'text/css',
26657             html : style
26658         });
26659
26660         return;
26661     }
26662     
26663     // hide stuff that is not compatible
26664     /**
26665      * @event blur
26666      * @hide
26667      */
26668     /**
26669      * @event change
26670      * @hide
26671      */
26672     /**
26673      * @event focus
26674      * @hide
26675      */
26676     /**
26677      * @event specialkey
26678      * @hide
26679      */
26680     /**
26681      * @cfg {String} fieldClass @hide
26682      */
26683     /**
26684      * @cfg {String} focusClass @hide
26685      */
26686     /**
26687      * @cfg {String} autoCreate @hide
26688      */
26689     /**
26690      * @cfg {String} inputType @hide
26691      */
26692     /**
26693      * @cfg {String} invalidClass @hide
26694      */
26695     /**
26696      * @cfg {String} invalidText @hide
26697      */
26698     /**
26699      * @cfg {String} msgFx @hide
26700      */
26701     /**
26702      * @cfg {String} validateOnBlur @hide
26703      */
26704 });
26705
26706 Roo.HtmlEditorCore.white = [
26707         'area', 'br', 'img', 'input', 'hr', 'wbr',
26708         
26709        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26710        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26711        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26712        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26713        'table',   'ul',         'xmp', 
26714        
26715        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26716       'thead',   'tr', 
26717      
26718       'dir', 'menu', 'ol', 'ul', 'dl',
26719        
26720       'embed',  'object'
26721 ];
26722
26723
26724 Roo.HtmlEditorCore.black = [
26725     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26726         'applet', // 
26727         'base',   'basefont', 'bgsound', 'blink',  'body', 
26728         'frame',  'frameset', 'head',    'html',   'ilayer', 
26729         'iframe', 'layer',  'link',     'meta',    'object',   
26730         'script', 'style' ,'title',  'xml' // clean later..
26731 ];
26732 Roo.HtmlEditorCore.clean = [
26733     'script', 'style', 'title', 'xml'
26734 ];
26735 Roo.HtmlEditorCore.remove = [
26736     'font'
26737 ];
26738 // attributes..
26739
26740 Roo.HtmlEditorCore.ablack = [
26741     'on'
26742 ];
26743     
26744 Roo.HtmlEditorCore.aclean = [ 
26745     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26746 ];
26747
26748 // protocols..
26749 Roo.HtmlEditorCore.pwhite= [
26750         'http',  'https',  'mailto'
26751 ];
26752
26753 // white listed style attributes.
26754 Roo.HtmlEditorCore.cwhite= [
26755       //  'text-align', /// default is to allow most things..
26756       
26757          
26758 //        'font-size'//??
26759 ];
26760
26761 // black listed style attributes.
26762 Roo.HtmlEditorCore.cblack= [
26763       //  'font-size' -- this can be set by the project 
26764 ];
26765
26766
26767 Roo.HtmlEditorCore.swapCodes   =[ 
26768     [    8211, "&#8211;" ], 
26769     [    8212, "&#8212;" ], 
26770     [    8216,  "'" ],  
26771     [    8217, "'" ],  
26772     [    8220, '"' ],  
26773     [    8221, '"' ],  
26774     [    8226, "*" ],  
26775     [    8230, "..." ]
26776 ]; 
26777
26778     /*
26779  * - LGPL
26780  *
26781  * HtmlEditor
26782  * 
26783  */
26784
26785 /**
26786  * @class Roo.bootstrap.HtmlEditor
26787  * @extends Roo.bootstrap.TextArea
26788  * Bootstrap HtmlEditor class
26789
26790  * @constructor
26791  * Create a new HtmlEditor
26792  * @param {Object} config The config object
26793  */
26794
26795 Roo.bootstrap.HtmlEditor = function(config){
26796     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26797     if (!this.toolbars) {
26798         this.toolbars = [];
26799     }
26800     
26801     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26802     this.addEvents({
26803             /**
26804              * @event initialize
26805              * Fires when the editor is fully initialized (including the iframe)
26806              * @param {HtmlEditor} this
26807              */
26808             initialize: true,
26809             /**
26810              * @event activate
26811              * Fires when the editor is first receives the focus. Any insertion must wait
26812              * until after this event.
26813              * @param {HtmlEditor} this
26814              */
26815             activate: true,
26816              /**
26817              * @event beforesync
26818              * Fires before the textarea is updated with content from the editor iframe. Return false
26819              * to cancel the sync.
26820              * @param {HtmlEditor} this
26821              * @param {String} html
26822              */
26823             beforesync: true,
26824              /**
26825              * @event beforepush
26826              * Fires before the iframe editor is updated with content from the textarea. Return false
26827              * to cancel the push.
26828              * @param {HtmlEditor} this
26829              * @param {String} html
26830              */
26831             beforepush: true,
26832              /**
26833              * @event sync
26834              * Fires when the textarea is updated with content from the editor iframe.
26835              * @param {HtmlEditor} this
26836              * @param {String} html
26837              */
26838             sync: true,
26839              /**
26840              * @event push
26841              * Fires when the iframe editor is updated with content from the textarea.
26842              * @param {HtmlEditor} this
26843              * @param {String} html
26844              */
26845             push: true,
26846              /**
26847              * @event editmodechange
26848              * Fires when the editor switches edit modes
26849              * @param {HtmlEditor} this
26850              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26851              */
26852             editmodechange: true,
26853             /**
26854              * @event editorevent
26855              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26856              * @param {HtmlEditor} this
26857              */
26858             editorevent: true,
26859             /**
26860              * @event firstfocus
26861              * Fires when on first focus - needed by toolbars..
26862              * @param {HtmlEditor} this
26863              */
26864             firstfocus: true,
26865             /**
26866              * @event autosave
26867              * Auto save the htmlEditor value as a file into Events
26868              * @param {HtmlEditor} this
26869              */
26870             autosave: true,
26871             /**
26872              * @event savedpreview
26873              * preview the saved version of htmlEditor
26874              * @param {HtmlEditor} this
26875              */
26876             savedpreview: true
26877         });
26878 };
26879
26880
26881 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26882     
26883     
26884       /**
26885      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26886      */
26887     toolbars : false,
26888     
26889      /**
26890     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26891     */
26892     btns : [],
26893    
26894      /**
26895      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26896      *                        Roo.resizable.
26897      */
26898     resizable : false,
26899      /**
26900      * @cfg {Number} height (in pixels)
26901      */   
26902     height: 300,
26903    /**
26904      * @cfg {Number} width (in pixels)
26905      */   
26906     width: false,
26907     
26908     /**
26909      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26910      * 
26911      */
26912     stylesheets: false,
26913     
26914     // id of frame..
26915     frameId: false,
26916     
26917     // private properties
26918     validationEvent : false,
26919     deferHeight: true,
26920     initialized : false,
26921     activated : false,
26922     
26923     onFocus : Roo.emptyFn,
26924     iframePad:3,
26925     hideMode:'offsets',
26926     
26927     tbContainer : false,
26928     
26929     bodyCls : '',
26930     
26931     toolbarContainer :function() {
26932         return this.wrap.select('.x-html-editor-tb',true).first();
26933     },
26934
26935     /**
26936      * Protected method that will not generally be called directly. It
26937      * is called when the editor creates its toolbar. Override this method if you need to
26938      * add custom toolbar buttons.
26939      * @param {HtmlEditor} editor
26940      */
26941     createToolbar : function(){
26942         Roo.log('renewing');
26943         Roo.log("create toolbars");
26944         
26945         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26946         this.toolbars[0].render(this.toolbarContainer());
26947         
26948         return;
26949         
26950 //        if (!editor.toolbars || !editor.toolbars.length) {
26951 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26952 //        }
26953 //        
26954 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26955 //            editor.toolbars[i] = Roo.factory(
26956 //                    typeof(editor.toolbars[i]) == 'string' ?
26957 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26958 //                Roo.bootstrap.HtmlEditor);
26959 //            editor.toolbars[i].init(editor);
26960 //        }
26961     },
26962
26963      
26964     // private
26965     onRender : function(ct, position)
26966     {
26967        // Roo.log("Call onRender: " + this.xtype);
26968         var _t = this;
26969         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26970       
26971         this.wrap = this.inputEl().wrap({
26972             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26973         });
26974         
26975         this.editorcore.onRender(ct, position);
26976          
26977         if (this.resizable) {
26978             this.resizeEl = new Roo.Resizable(this.wrap, {
26979                 pinned : true,
26980                 wrap: true,
26981                 dynamic : true,
26982                 minHeight : this.height,
26983                 height: this.height,
26984                 handles : this.resizable,
26985                 width: this.width,
26986                 listeners : {
26987                     resize : function(r, w, h) {
26988                         _t.onResize(w,h); // -something
26989                     }
26990                 }
26991             });
26992             
26993         }
26994         this.createToolbar(this);
26995        
26996         
26997         if(!this.width && this.resizable){
26998             this.setSize(this.wrap.getSize());
26999         }
27000         if (this.resizeEl) {
27001             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
27002             // should trigger onReize..
27003         }
27004         
27005     },
27006
27007     // private
27008     onResize : function(w, h)
27009     {
27010         Roo.log('resize: ' +w + ',' + h );
27011         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
27012         var ew = false;
27013         var eh = false;
27014         
27015         if(this.inputEl() ){
27016             if(typeof w == 'number'){
27017                 var aw = w - this.wrap.getFrameWidth('lr');
27018                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
27019                 ew = aw;
27020             }
27021             if(typeof h == 'number'){
27022                  var tbh = -11;  // fixme it needs to tool bar size!
27023                 for (var i =0; i < this.toolbars.length;i++) {
27024                     // fixme - ask toolbars for heights?
27025                     tbh += this.toolbars[i].el.getHeight();
27026                     //if (this.toolbars[i].footer) {
27027                     //    tbh += this.toolbars[i].footer.el.getHeight();
27028                     //}
27029                 }
27030               
27031                 
27032                 
27033                 
27034                 
27035                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
27036                 ah -= 5; // knock a few pixes off for look..
27037                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
27038                 var eh = ah;
27039             }
27040         }
27041         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
27042         this.editorcore.onResize(ew,eh);
27043         
27044     },
27045
27046     /**
27047      * Toggles the editor between standard and source edit mode.
27048      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
27049      */
27050     toggleSourceEdit : function(sourceEditMode)
27051     {
27052         this.editorcore.toggleSourceEdit(sourceEditMode);
27053         
27054         if(this.editorcore.sourceEditMode){
27055             Roo.log('editor - showing textarea');
27056             
27057 //            Roo.log('in');
27058 //            Roo.log(this.syncValue());
27059             this.syncValue();
27060             this.inputEl().removeClass(['hide', 'x-hidden']);
27061             this.inputEl().dom.removeAttribute('tabIndex');
27062             this.inputEl().focus();
27063         }else{
27064             Roo.log('editor - hiding textarea');
27065 //            Roo.log('out')
27066 //            Roo.log(this.pushValue()); 
27067             this.pushValue();
27068             
27069             this.inputEl().addClass(['hide', 'x-hidden']);
27070             this.inputEl().dom.setAttribute('tabIndex', -1);
27071             //this.deferFocus();
27072         }
27073          
27074         if(this.resizable){
27075             this.setSize(this.wrap.getSize());
27076         }
27077         
27078         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
27079     },
27080  
27081     // private (for BoxComponent)
27082     adjustSize : Roo.BoxComponent.prototype.adjustSize,
27083
27084     // private (for BoxComponent)
27085     getResizeEl : function(){
27086         return this.wrap;
27087     },
27088
27089     // private (for BoxComponent)
27090     getPositionEl : function(){
27091         return this.wrap;
27092     },
27093
27094     // private
27095     initEvents : function(){
27096         this.originalValue = this.getValue();
27097     },
27098
27099 //    /**
27100 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27101 //     * @method
27102 //     */
27103 //    markInvalid : Roo.emptyFn,
27104 //    /**
27105 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
27106 //     * @method
27107 //     */
27108 //    clearInvalid : Roo.emptyFn,
27109
27110     setValue : function(v){
27111         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
27112         this.editorcore.pushValue();
27113     },
27114
27115      
27116     // private
27117     deferFocus : function(){
27118         this.focus.defer(10, this);
27119     },
27120
27121     // doc'ed in Field
27122     focus : function(){
27123         this.editorcore.focus();
27124         
27125     },
27126       
27127
27128     // private
27129     onDestroy : function(){
27130         
27131         
27132         
27133         if(this.rendered){
27134             
27135             for (var i =0; i < this.toolbars.length;i++) {
27136                 // fixme - ask toolbars for heights?
27137                 this.toolbars[i].onDestroy();
27138             }
27139             
27140             this.wrap.dom.innerHTML = '';
27141             this.wrap.remove();
27142         }
27143     },
27144
27145     // private
27146     onFirstFocus : function(){
27147         //Roo.log("onFirstFocus");
27148         this.editorcore.onFirstFocus();
27149          for (var i =0; i < this.toolbars.length;i++) {
27150             this.toolbars[i].onFirstFocus();
27151         }
27152         
27153     },
27154     
27155     // private
27156     syncValue : function()
27157     {   
27158         this.editorcore.syncValue();
27159     },
27160     
27161     pushValue : function()
27162     {   
27163         this.editorcore.pushValue();
27164     }
27165      
27166     
27167     // hide stuff that is not compatible
27168     /**
27169      * @event blur
27170      * @hide
27171      */
27172     /**
27173      * @event change
27174      * @hide
27175      */
27176     /**
27177      * @event focus
27178      * @hide
27179      */
27180     /**
27181      * @event specialkey
27182      * @hide
27183      */
27184     /**
27185      * @cfg {String} fieldClass @hide
27186      */
27187     /**
27188      * @cfg {String} focusClass @hide
27189      */
27190     /**
27191      * @cfg {String} autoCreate @hide
27192      */
27193     /**
27194      * @cfg {String} inputType @hide
27195      */
27196      
27197     /**
27198      * @cfg {String} invalidText @hide
27199      */
27200     /**
27201      * @cfg {String} msgFx @hide
27202      */
27203     /**
27204      * @cfg {String} validateOnBlur @hide
27205      */
27206 });
27207  
27208     
27209    
27210    
27211    
27212       
27213 Roo.namespace('Roo.bootstrap.htmleditor');
27214 /**
27215  * @class Roo.bootstrap.HtmlEditorToolbar1
27216  * Basic Toolbar
27217  * 
27218  * @example
27219  * Usage:
27220  *
27221  new Roo.bootstrap.HtmlEditor({
27222     ....
27223     toolbars : [
27224         new Roo.bootstrap.HtmlEditorToolbar1({
27225             disable : { fonts: 1 , format: 1, ..., ... , ...],
27226             btns : [ .... ]
27227         })
27228     }
27229      
27230  * 
27231  * @cfg {Object} disable List of elements to disable..
27232  * @cfg {Array} btns List of additional buttons.
27233  * 
27234  * 
27235  * NEEDS Extra CSS? 
27236  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
27237  */
27238  
27239 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
27240 {
27241     
27242     Roo.apply(this, config);
27243     
27244     // default disabled, based on 'good practice'..
27245     this.disable = this.disable || {};
27246     Roo.applyIf(this.disable, {
27247         fontSize : true,
27248         colors : true,
27249         specialElements : true
27250     });
27251     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
27252     
27253     this.editor = config.editor;
27254     this.editorcore = config.editor.editorcore;
27255     
27256     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
27257     
27258     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
27259     // dont call parent... till later.
27260 }
27261 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
27262      
27263     bar : true,
27264     
27265     editor : false,
27266     editorcore : false,
27267     
27268     
27269     formats : [
27270         "p" ,  
27271         "h1","h2","h3","h4","h5","h6", 
27272         "pre", "code", 
27273         "abbr", "acronym", "address", "cite", "samp", "var",
27274         'div','span'
27275     ],
27276     
27277     onRender : function(ct, position)
27278     {
27279        // Roo.log("Call onRender: " + this.xtype);
27280         
27281        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
27282        Roo.log(this.el);
27283        this.el.dom.style.marginBottom = '0';
27284        var _this = this;
27285        var editorcore = this.editorcore;
27286        var editor= this.editor;
27287        
27288        var children = [];
27289        var btn = function(id,cmd , toggle, handler, html){
27290        
27291             var  event = toggle ? 'toggle' : 'click';
27292        
27293             var a = {
27294                 size : 'sm',
27295                 xtype: 'Button',
27296                 xns: Roo.bootstrap,
27297                 //glyphicon : id,
27298                 fa: id,
27299                 cmd : id || cmd,
27300                 enableToggle:toggle !== false,
27301                 html : html || '',
27302                 pressed : toggle ? false : null,
27303                 listeners : {}
27304             };
27305             a.listeners[toggle ? 'toggle' : 'click'] = function() {
27306                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
27307             };
27308             children.push(a);
27309             return a;
27310        }
27311        
27312     //    var cb_box = function...
27313         
27314         var style = {
27315                 xtype: 'Button',
27316                 size : 'sm',
27317                 xns: Roo.bootstrap,
27318                 fa : 'font',
27319                 //html : 'submit'
27320                 menu : {
27321                     xtype: 'Menu',
27322                     xns: Roo.bootstrap,
27323                     items:  []
27324                 }
27325         };
27326         Roo.each(this.formats, function(f) {
27327             style.menu.items.push({
27328                 xtype :'MenuItem',
27329                 xns: Roo.bootstrap,
27330                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
27331                 tagname : f,
27332                 listeners : {
27333                     click : function()
27334                     {
27335                         editorcore.insertTag(this.tagname);
27336                         editor.focus();
27337                     }
27338                 }
27339                 
27340             });
27341         });
27342         children.push(style);   
27343         
27344         btn('bold',false,true);
27345         btn('italic',false,true);
27346         btn('align-left', 'justifyleft',true);
27347         btn('align-center', 'justifycenter',true);
27348         btn('align-right' , 'justifyright',true);
27349         btn('link', false, false, function(btn) {
27350             //Roo.log("create link?");
27351             var url = prompt(this.createLinkText, this.defaultLinkValue);
27352             if(url && url != 'http:/'+'/'){
27353                 this.editorcore.relayCmd('createlink', url);
27354             }
27355         }),
27356         btn('list','insertunorderedlist',true);
27357         btn('pencil', false,true, function(btn){
27358                 Roo.log(this);
27359                 this.toggleSourceEdit(btn.pressed);
27360         });
27361         
27362         if (this.editor.btns.length > 0) {
27363             for (var i = 0; i<this.editor.btns.length; i++) {
27364                 children.push(this.editor.btns[i]);
27365             }
27366         }
27367         
27368         /*
27369         var cog = {
27370                 xtype: 'Button',
27371                 size : 'sm',
27372                 xns: Roo.bootstrap,
27373                 glyphicon : 'cog',
27374                 //html : 'submit'
27375                 menu : {
27376                     xtype: 'Menu',
27377                     xns: Roo.bootstrap,
27378                     items:  []
27379                 }
27380         };
27381         
27382         cog.menu.items.push({
27383             xtype :'MenuItem',
27384             xns: Roo.bootstrap,
27385             html : Clean styles,
27386             tagname : f,
27387             listeners : {
27388                 click : function()
27389                 {
27390                     editorcore.insertTag(this.tagname);
27391                     editor.focus();
27392                 }
27393             }
27394             
27395         });
27396        */
27397         
27398          
27399        this.xtype = 'NavSimplebar';
27400         
27401         for(var i=0;i< children.length;i++) {
27402             
27403             this.buttons.add(this.addxtypeChild(children[i]));
27404             
27405         }
27406         
27407         editor.on('editorevent', this.updateToolbar, this);
27408     },
27409     onBtnClick : function(id)
27410     {
27411        this.editorcore.relayCmd(id);
27412        this.editorcore.focus();
27413     },
27414     
27415     /**
27416      * Protected method that will not generally be called directly. It triggers
27417      * a toolbar update by reading the markup state of the current selection in the editor.
27418      */
27419     updateToolbar: function(){
27420
27421         if(!this.editorcore.activated){
27422             this.editor.onFirstFocus(); // is this neeed?
27423             return;
27424         }
27425
27426         var btns = this.buttons; 
27427         var doc = this.editorcore.doc;
27428         btns.get('bold').setActive(doc.queryCommandState('bold'));
27429         btns.get('italic').setActive(doc.queryCommandState('italic'));
27430         //btns.get('underline').setActive(doc.queryCommandState('underline'));
27431         
27432         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
27433         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
27434         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
27435         
27436         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
27437         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
27438          /*
27439         
27440         var ans = this.editorcore.getAllAncestors();
27441         if (this.formatCombo) {
27442             
27443             
27444             var store = this.formatCombo.store;
27445             this.formatCombo.setValue("");
27446             for (var i =0; i < ans.length;i++) {
27447                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
27448                     // select it..
27449                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
27450                     break;
27451                 }
27452             }
27453         }
27454         
27455         
27456         
27457         // hides menus... - so this cant be on a menu...
27458         Roo.bootstrap.MenuMgr.hideAll();
27459         */
27460         Roo.bootstrap.MenuMgr.hideAll();
27461         //this.editorsyncValue();
27462     },
27463     onFirstFocus: function() {
27464         this.buttons.each(function(item){
27465            item.enable();
27466         });
27467     },
27468     toggleSourceEdit : function(sourceEditMode){
27469         
27470           
27471         if(sourceEditMode){
27472             Roo.log("disabling buttons");
27473            this.buttons.each( function(item){
27474                 if(item.cmd != 'pencil'){
27475                     item.disable();
27476                 }
27477             });
27478           
27479         }else{
27480             Roo.log("enabling buttons");
27481             if(this.editorcore.initialized){
27482                 this.buttons.each( function(item){
27483                     item.enable();
27484                 });
27485             }
27486             
27487         }
27488         Roo.log("calling toggole on editor");
27489         // tell the editor that it's been pressed..
27490         this.editor.toggleSourceEdit(sourceEditMode);
27491        
27492     }
27493 });
27494
27495
27496
27497
27498  
27499 /*
27500  * - LGPL
27501  */
27502
27503 /**
27504  * @class Roo.bootstrap.Markdown
27505  * @extends Roo.bootstrap.TextArea
27506  * Bootstrap Showdown editable area
27507  * @cfg {string} content
27508  * 
27509  * @constructor
27510  * Create a new Showdown
27511  */
27512
27513 Roo.bootstrap.Markdown = function(config){
27514     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
27515    
27516 };
27517
27518 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
27519     
27520     editing :false,
27521     
27522     initEvents : function()
27523     {
27524         
27525         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
27526         this.markdownEl = this.el.createChild({
27527             cls : 'roo-markdown-area'
27528         });
27529         this.inputEl().addClass('d-none');
27530         if (this.getValue() == '') {
27531             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27532             
27533         } else {
27534             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27535         }
27536         this.markdownEl.on('click', this.toggleTextEdit, this);
27537         this.on('blur', this.toggleTextEdit, this);
27538         this.on('specialkey', this.resizeTextArea, this);
27539     },
27540     
27541     toggleTextEdit : function()
27542     {
27543         var sh = this.markdownEl.getHeight();
27544         this.inputEl().addClass('d-none');
27545         this.markdownEl.addClass('d-none');
27546         if (!this.editing) {
27547             // show editor?
27548             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
27549             this.inputEl().removeClass('d-none');
27550             this.inputEl().focus();
27551             this.editing = true;
27552             return;
27553         }
27554         // show showdown...
27555         this.updateMarkdown();
27556         this.markdownEl.removeClass('d-none');
27557         this.editing = false;
27558         return;
27559     },
27560     updateMarkdown : function()
27561     {
27562         if (this.getValue() == '') {
27563             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
27564             return;
27565         }
27566  
27567         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
27568     },
27569     
27570     resizeTextArea: function () {
27571         
27572         var sh = 100;
27573         Roo.log([sh, this.getValue().split("\n").length * 30]);
27574         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
27575     },
27576     setValue : function(val)
27577     {
27578         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
27579         if (!this.editing) {
27580             this.updateMarkdown();
27581         }
27582         
27583     },
27584     focus : function()
27585     {
27586         if (!this.editing) {
27587             this.toggleTextEdit();
27588         }
27589         
27590     }
27591
27592
27593 });/*
27594  * Based on:
27595  * Ext JS Library 1.1.1
27596  * Copyright(c) 2006-2007, Ext JS, LLC.
27597  *
27598  * Originally Released Under LGPL - original licence link has changed is not relivant.
27599  *
27600  * Fork - LGPL
27601  * <script type="text/javascript">
27602  */
27603  
27604 /**
27605  * @class Roo.bootstrap.PagingToolbar
27606  * @extends Roo.bootstrap.NavSimplebar
27607  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27608  * @constructor
27609  * Create a new PagingToolbar
27610  * @param {Object} config The config object
27611  * @param {Roo.data.Store} store
27612  */
27613 Roo.bootstrap.PagingToolbar = function(config)
27614 {
27615     // old args format still supported... - xtype is prefered..
27616         // created from xtype...
27617     
27618     this.ds = config.dataSource;
27619     
27620     if (config.store && !this.ds) {
27621         this.store= Roo.factory(config.store, Roo.data);
27622         this.ds = this.store;
27623         this.ds.xmodule = this.xmodule || false;
27624     }
27625     
27626     this.toolbarItems = [];
27627     if (config.items) {
27628         this.toolbarItems = config.items;
27629     }
27630     
27631     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27632     
27633     this.cursor = 0;
27634     
27635     if (this.ds) { 
27636         this.bind(this.ds);
27637     }
27638     
27639     if (Roo.bootstrap.version == 4) {
27640         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27641     } else {
27642         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27643     }
27644     
27645 };
27646
27647 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27648     /**
27649      * @cfg {Roo.data.Store} dataSource
27650      * The underlying data store providing the paged data
27651      */
27652     /**
27653      * @cfg {String/HTMLElement/Element} container
27654      * container The id or element that will contain the toolbar
27655      */
27656     /**
27657      * @cfg {Boolean} displayInfo
27658      * True to display the displayMsg (defaults to false)
27659      */
27660     /**
27661      * @cfg {Number} pageSize
27662      * The number of records to display per page (defaults to 20)
27663      */
27664     pageSize: 20,
27665     /**
27666      * @cfg {String} displayMsg
27667      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27668      */
27669     displayMsg : 'Displaying {0} - {1} of {2}',
27670     /**
27671      * @cfg {String} emptyMsg
27672      * The message to display when no records are found (defaults to "No data to display")
27673      */
27674     emptyMsg : 'No data to display',
27675     /**
27676      * Customizable piece of the default paging text (defaults to "Page")
27677      * @type String
27678      */
27679     beforePageText : "Page",
27680     /**
27681      * Customizable piece of the default paging text (defaults to "of %0")
27682      * @type String
27683      */
27684     afterPageText : "of {0}",
27685     /**
27686      * Customizable piece of the default paging text (defaults to "First Page")
27687      * @type String
27688      */
27689     firstText : "First Page",
27690     /**
27691      * Customizable piece of the default paging text (defaults to "Previous Page")
27692      * @type String
27693      */
27694     prevText : "Previous Page",
27695     /**
27696      * Customizable piece of the default paging text (defaults to "Next Page")
27697      * @type String
27698      */
27699     nextText : "Next Page",
27700     /**
27701      * Customizable piece of the default paging text (defaults to "Last Page")
27702      * @type String
27703      */
27704     lastText : "Last Page",
27705     /**
27706      * Customizable piece of the default paging text (defaults to "Refresh")
27707      * @type String
27708      */
27709     refreshText : "Refresh",
27710
27711     buttons : false,
27712     // private
27713     onRender : function(ct, position) 
27714     {
27715         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27716         this.navgroup.parentId = this.id;
27717         this.navgroup.onRender(this.el, null);
27718         // add the buttons to the navgroup
27719         
27720         if(this.displayInfo){
27721             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27722             this.displayEl = this.el.select('.x-paging-info', true).first();
27723 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27724 //            this.displayEl = navel.el.select('span',true).first();
27725         }
27726         
27727         var _this = this;
27728         
27729         if(this.buttons){
27730             Roo.each(_this.buttons, function(e){ // this might need to use render????
27731                Roo.factory(e).render(_this.el);
27732             });
27733         }
27734             
27735         Roo.each(_this.toolbarItems, function(e) {
27736             _this.navgroup.addItem(e);
27737         });
27738         
27739         
27740         this.first = this.navgroup.addItem({
27741             tooltip: this.firstText,
27742             cls: "prev btn-outline-secondary",
27743             html : ' <i class="fa fa-step-backward"></i>',
27744             disabled: true,
27745             preventDefault: true,
27746             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27747         });
27748         
27749         this.prev =  this.navgroup.addItem({
27750             tooltip: this.prevText,
27751             cls: "prev btn-outline-secondary",
27752             html : ' <i class="fa fa-backward"></i>',
27753             disabled: true,
27754             preventDefault: true,
27755             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27756         });
27757     //this.addSeparator();
27758         
27759         
27760         var field = this.navgroup.addItem( {
27761             tagtype : 'span',
27762             cls : 'x-paging-position  btn-outline-secondary',
27763              disabled: true,
27764             html : this.beforePageText  +
27765                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27766                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27767          } ); //?? escaped?
27768         
27769         this.field = field.el.select('input', true).first();
27770         this.field.on("keydown", this.onPagingKeydown, this);
27771         this.field.on("focus", function(){this.dom.select();});
27772     
27773     
27774         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27775         //this.field.setHeight(18);
27776         //this.addSeparator();
27777         this.next = this.navgroup.addItem({
27778             tooltip: this.nextText,
27779             cls: "next btn-outline-secondary",
27780             html : ' <i class="fa fa-forward"></i>',
27781             disabled: true,
27782             preventDefault: true,
27783             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27784         });
27785         this.last = this.navgroup.addItem({
27786             tooltip: this.lastText,
27787             html : ' <i class="fa fa-step-forward"></i>',
27788             cls: "next btn-outline-secondary",
27789             disabled: true,
27790             preventDefault: true,
27791             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27792         });
27793     //this.addSeparator();
27794         this.loading = this.navgroup.addItem({
27795             tooltip: this.refreshText,
27796             cls: "btn-outline-secondary",
27797             html : ' <i class="fa fa-refresh"></i>',
27798             preventDefault: true,
27799             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27800         });
27801         
27802     },
27803
27804     // private
27805     updateInfo : function(){
27806         if(this.displayEl){
27807             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27808             var msg = count == 0 ?
27809                 this.emptyMsg :
27810                 String.format(
27811                     this.displayMsg,
27812                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27813                 );
27814             this.displayEl.update(msg);
27815         }
27816     },
27817
27818     // private
27819     onLoad : function(ds, r, o)
27820     {
27821         this.cursor = o.params && o.params.start ? o.params.start : 0;
27822         
27823         var d = this.getPageData(),
27824             ap = d.activePage,
27825             ps = d.pages;
27826         
27827         
27828         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27829         this.field.dom.value = ap;
27830         this.first.setDisabled(ap == 1);
27831         this.prev.setDisabled(ap == 1);
27832         this.next.setDisabled(ap == ps);
27833         this.last.setDisabled(ap == ps);
27834         this.loading.enable();
27835         this.updateInfo();
27836     },
27837
27838     // private
27839     getPageData : function(){
27840         var total = this.ds.getTotalCount();
27841         return {
27842             total : total,
27843             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27844             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27845         };
27846     },
27847
27848     // private
27849     onLoadError : function(){
27850         this.loading.enable();
27851     },
27852
27853     // private
27854     onPagingKeydown : function(e){
27855         var k = e.getKey();
27856         var d = this.getPageData();
27857         if(k == e.RETURN){
27858             var v = this.field.dom.value, pageNum;
27859             if(!v || isNaN(pageNum = parseInt(v, 10))){
27860                 this.field.dom.value = d.activePage;
27861                 return;
27862             }
27863             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27864             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27865             e.stopEvent();
27866         }
27867         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))
27868         {
27869           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27870           this.field.dom.value = pageNum;
27871           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27872           e.stopEvent();
27873         }
27874         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27875         {
27876           var v = this.field.dom.value, pageNum; 
27877           var increment = (e.shiftKey) ? 10 : 1;
27878           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27879                 increment *= -1;
27880           }
27881           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27882             this.field.dom.value = d.activePage;
27883             return;
27884           }
27885           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27886           {
27887             this.field.dom.value = parseInt(v, 10) + increment;
27888             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27889             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27890           }
27891           e.stopEvent();
27892         }
27893     },
27894
27895     // private
27896     beforeLoad : function(){
27897         if(this.loading){
27898             this.loading.disable();
27899         }
27900     },
27901
27902     // private
27903     onClick : function(which){
27904         
27905         var ds = this.ds;
27906         if (!ds) {
27907             return;
27908         }
27909         
27910         switch(which){
27911             case "first":
27912                 ds.load({params:{start: 0, limit: this.pageSize}});
27913             break;
27914             case "prev":
27915                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27916             break;
27917             case "next":
27918                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27919             break;
27920             case "last":
27921                 var total = ds.getTotalCount();
27922                 var extra = total % this.pageSize;
27923                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27924                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27925             break;
27926             case "refresh":
27927                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27928             break;
27929         }
27930     },
27931
27932     /**
27933      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27934      * @param {Roo.data.Store} store The data store to unbind
27935      */
27936     unbind : function(ds){
27937         ds.un("beforeload", this.beforeLoad, this);
27938         ds.un("load", this.onLoad, this);
27939         ds.un("loadexception", this.onLoadError, this);
27940         ds.un("remove", this.updateInfo, this);
27941         ds.un("add", this.updateInfo, this);
27942         this.ds = undefined;
27943     },
27944
27945     /**
27946      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27947      * @param {Roo.data.Store} store The data store to bind
27948      */
27949     bind : function(ds){
27950         ds.on("beforeload", this.beforeLoad, this);
27951         ds.on("load", this.onLoad, this);
27952         ds.on("loadexception", this.onLoadError, this);
27953         ds.on("remove", this.updateInfo, this);
27954         ds.on("add", this.updateInfo, this);
27955         this.ds = ds;
27956     }
27957 });/*
27958  * - LGPL
27959  *
27960  * element
27961  * 
27962  */
27963
27964 /**
27965  * @class Roo.bootstrap.MessageBar
27966  * @extends Roo.bootstrap.Component
27967  * Bootstrap MessageBar class
27968  * @cfg {String} html contents of the MessageBar
27969  * @cfg {String} weight (info | success | warning | danger) default info
27970  * @cfg {String} beforeClass insert the bar before the given class
27971  * @cfg {Boolean} closable (true | false) default false
27972  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27973  * 
27974  * @constructor
27975  * Create a new Element
27976  * @param {Object} config The config object
27977  */
27978
27979 Roo.bootstrap.MessageBar = function(config){
27980     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27981 };
27982
27983 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27984     
27985     html: '',
27986     weight: 'info',
27987     closable: false,
27988     fixed: false,
27989     beforeClass: 'bootstrap-sticky-wrap',
27990     
27991     getAutoCreate : function(){
27992         
27993         var cfg = {
27994             tag: 'div',
27995             cls: 'alert alert-dismissable alert-' + this.weight,
27996             cn: [
27997                 {
27998                     tag: 'span',
27999                     cls: 'message',
28000                     html: this.html || ''
28001                 }
28002             ]
28003         };
28004         
28005         if(this.fixed){
28006             cfg.cls += ' alert-messages-fixed';
28007         }
28008         
28009         if(this.closable){
28010             cfg.cn.push({
28011                 tag: 'button',
28012                 cls: 'close',
28013                 html: 'x'
28014             });
28015         }
28016         
28017         return cfg;
28018     },
28019     
28020     onRender : function(ct, position)
28021     {
28022         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
28023         
28024         if(!this.el){
28025             var cfg = Roo.apply({},  this.getAutoCreate());
28026             cfg.id = Roo.id();
28027             
28028             if (this.cls) {
28029                 cfg.cls += ' ' + this.cls;
28030             }
28031             if (this.style) {
28032                 cfg.style = this.style;
28033             }
28034             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
28035             
28036             this.el.setVisibilityMode(Roo.Element.DISPLAY);
28037         }
28038         
28039         this.el.select('>button.close').on('click', this.hide, this);
28040         
28041     },
28042     
28043     show : function()
28044     {
28045         if (!this.rendered) {
28046             this.render();
28047         }
28048         
28049         this.el.show();
28050         
28051         this.fireEvent('show', this);
28052         
28053     },
28054     
28055     hide : function()
28056     {
28057         if (!this.rendered) {
28058             this.render();
28059         }
28060         
28061         this.el.hide();
28062         
28063         this.fireEvent('hide', this);
28064     },
28065     
28066     update : function()
28067     {
28068 //        var e = this.el.dom.firstChild;
28069 //        
28070 //        if(this.closable){
28071 //            e = e.nextSibling;
28072 //        }
28073 //        
28074 //        e.data = this.html || '';
28075
28076         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28077     }
28078    
28079 });
28080
28081  
28082
28083      /*
28084  * - LGPL
28085  *
28086  * Graph
28087  * 
28088  */
28089
28090
28091 /**
28092  * @class Roo.bootstrap.Graph
28093  * @extends Roo.bootstrap.Component
28094  * Bootstrap Graph class
28095 > Prameters
28096  -sm {number} sm 4
28097  -md {number} md 5
28098  @cfg {String} graphtype  bar | vbar | pie
28099  @cfg {number} g_x coodinator | centre x (pie)
28100  @cfg {number} g_y coodinator | centre y (pie)
28101  @cfg {number} g_r radius (pie)
28102  @cfg {number} g_height height of the chart (respected by all elements in the set)
28103  @cfg {number} g_width width of the chart (respected by all elements in the set)
28104  @cfg {Object} title The title of the chart
28105     
28106  -{Array}  values
28107  -opts (object) options for the chart 
28108      o {
28109      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28110      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28111      o vgutter (number)
28112      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.
28113      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28114      o to
28115      o stretch (boolean)
28116      o }
28117  -opts (object) options for the pie
28118      o{
28119      o cut
28120      o startAngle (number)
28121      o endAngle (number)
28122      } 
28123  *
28124  * @constructor
28125  * Create a new Input
28126  * @param {Object} config The config object
28127  */
28128
28129 Roo.bootstrap.Graph = function(config){
28130     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28131     
28132     this.addEvents({
28133         // img events
28134         /**
28135          * @event click
28136          * The img click event for the img.
28137          * @param {Roo.EventObject} e
28138          */
28139         "click" : true
28140     });
28141 };
28142
28143 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28144     
28145     sm: 4,
28146     md: 5,
28147     graphtype: 'bar',
28148     g_height: 250,
28149     g_width: 400,
28150     g_x: 50,
28151     g_y: 50,
28152     g_r: 30,
28153     opts:{
28154         //g_colors: this.colors,
28155         g_type: 'soft',
28156         g_gutter: '20%'
28157
28158     },
28159     title : false,
28160
28161     getAutoCreate : function(){
28162         
28163         var cfg = {
28164             tag: 'div',
28165             html : null
28166         };
28167         
28168         
28169         return  cfg;
28170     },
28171
28172     onRender : function(ct,position){
28173         
28174         
28175         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28176         
28177         if (typeof(Raphael) == 'undefined') {
28178             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28179             return;
28180         }
28181         
28182         this.raphael = Raphael(this.el.dom);
28183         
28184                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28185                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28186                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28187                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28188                 /*
28189                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28190                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28191                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28192                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28193                 
28194                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28195                 r.barchart(330, 10, 300, 220, data1);
28196                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28197                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28198                 */
28199                 
28200                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28201                 // r.barchart(30, 30, 560, 250,  xdata, {
28202                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28203                 //     axis : "0 0 1 1",
28204                 //     axisxlabels :  xdata
28205                 //     //yvalues : cols,
28206                    
28207                 // });
28208 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28209 //        
28210 //        this.load(null,xdata,{
28211 //                axis : "0 0 1 1",
28212 //                axisxlabels :  xdata
28213 //                });
28214
28215     },
28216
28217     load : function(graphtype,xdata,opts)
28218     {
28219         this.raphael.clear();
28220         if(!graphtype) {
28221             graphtype = this.graphtype;
28222         }
28223         if(!opts){
28224             opts = this.opts;
28225         }
28226         var r = this.raphael,
28227             fin = function () {
28228                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28229             },
28230             fout = function () {
28231                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28232             },
28233             pfin = function() {
28234                 this.sector.stop();
28235                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28236
28237                 if (this.label) {
28238                     this.label[0].stop();
28239                     this.label[0].attr({ r: 7.5 });
28240                     this.label[1].attr({ "font-weight": 800 });
28241                 }
28242             },
28243             pfout = function() {
28244                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28245
28246                 if (this.label) {
28247                     this.label[0].animate({ r: 5 }, 500, "bounce");
28248                     this.label[1].attr({ "font-weight": 400 });
28249                 }
28250             };
28251
28252         switch(graphtype){
28253             case 'bar':
28254                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28255                 break;
28256             case 'hbar':
28257                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28258                 break;
28259             case 'pie':
28260 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28261 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28262 //            
28263                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28264                 
28265                 break;
28266
28267         }
28268         
28269         if(this.title){
28270             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28271         }
28272         
28273     },
28274     
28275     setTitle: function(o)
28276     {
28277         this.title = o;
28278     },
28279     
28280     initEvents: function() {
28281         
28282         if(!this.href){
28283             this.el.on('click', this.onClick, this);
28284         }
28285     },
28286     
28287     onClick : function(e)
28288     {
28289         Roo.log('img onclick');
28290         this.fireEvent('click', this, e);
28291     }
28292    
28293 });
28294
28295  
28296 /*
28297  * - LGPL
28298  *
28299  * numberBox
28300  * 
28301  */
28302 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28303
28304 /**
28305  * @class Roo.bootstrap.dash.NumberBox
28306  * @extends Roo.bootstrap.Component
28307  * Bootstrap NumberBox class
28308  * @cfg {String} headline Box headline
28309  * @cfg {String} content Box content
28310  * @cfg {String} icon Box icon
28311  * @cfg {String} footer Footer text
28312  * @cfg {String} fhref Footer href
28313  * 
28314  * @constructor
28315  * Create a new NumberBox
28316  * @param {Object} config The config object
28317  */
28318
28319
28320 Roo.bootstrap.dash.NumberBox = function(config){
28321     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28322     
28323 };
28324
28325 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28326     
28327     headline : '',
28328     content : '',
28329     icon : '',
28330     footer : '',
28331     fhref : '',
28332     ficon : '',
28333     
28334     getAutoCreate : function(){
28335         
28336         var cfg = {
28337             tag : 'div',
28338             cls : 'small-box ',
28339             cn : [
28340                 {
28341                     tag : 'div',
28342                     cls : 'inner',
28343                     cn :[
28344                         {
28345                             tag : 'h3',
28346                             cls : 'roo-headline',
28347                             html : this.headline
28348                         },
28349                         {
28350                             tag : 'p',
28351                             cls : 'roo-content',
28352                             html : this.content
28353                         }
28354                     ]
28355                 }
28356             ]
28357         };
28358         
28359         if(this.icon){
28360             cfg.cn.push({
28361                 tag : 'div',
28362                 cls : 'icon',
28363                 cn :[
28364                     {
28365                         tag : 'i',
28366                         cls : 'ion ' + this.icon
28367                     }
28368                 ]
28369             });
28370         }
28371         
28372         if(this.footer){
28373             var footer = {
28374                 tag : 'a',
28375                 cls : 'small-box-footer',
28376                 href : this.fhref || '#',
28377                 html : this.footer
28378             };
28379             
28380             cfg.cn.push(footer);
28381             
28382         }
28383         
28384         return  cfg;
28385     },
28386
28387     onRender : function(ct,position){
28388         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28389
28390
28391        
28392                 
28393     },
28394
28395     setHeadline: function (value)
28396     {
28397         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28398     },
28399     
28400     setFooter: function (value, href)
28401     {
28402         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28403         
28404         if(href){
28405             this.el.select('a.small-box-footer',true).first().attr('href', href);
28406         }
28407         
28408     },
28409
28410     setContent: function (value)
28411     {
28412         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28413     },
28414
28415     initEvents: function() 
28416     {   
28417         
28418     }
28419     
28420 });
28421
28422  
28423 /*
28424  * - LGPL
28425  *
28426  * TabBox
28427  * 
28428  */
28429 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28430
28431 /**
28432  * @class Roo.bootstrap.dash.TabBox
28433  * @extends Roo.bootstrap.Component
28434  * Bootstrap TabBox class
28435  * @cfg {String} title Title of the TabBox
28436  * @cfg {String} icon Icon of the TabBox
28437  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28438  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28439  * 
28440  * @constructor
28441  * Create a new TabBox
28442  * @param {Object} config The config object
28443  */
28444
28445
28446 Roo.bootstrap.dash.TabBox = function(config){
28447     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28448     this.addEvents({
28449         // raw events
28450         /**
28451          * @event addpane
28452          * When a pane is added
28453          * @param {Roo.bootstrap.dash.TabPane} pane
28454          */
28455         "addpane" : true,
28456         /**
28457          * @event activatepane
28458          * When a pane is activated
28459          * @param {Roo.bootstrap.dash.TabPane} pane
28460          */
28461         "activatepane" : true
28462         
28463          
28464     });
28465     
28466     this.panes = [];
28467 };
28468
28469 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28470
28471     title : '',
28472     icon : false,
28473     showtabs : true,
28474     tabScrollable : false,
28475     
28476     getChildContainer : function()
28477     {
28478         return this.el.select('.tab-content', true).first();
28479     },
28480     
28481     getAutoCreate : function(){
28482         
28483         var header = {
28484             tag: 'li',
28485             cls: 'pull-left header',
28486             html: this.title,
28487             cn : []
28488         };
28489         
28490         if(this.icon){
28491             header.cn.push({
28492                 tag: 'i',
28493                 cls: 'fa ' + this.icon
28494             });
28495         }
28496         
28497         var h = {
28498             tag: 'ul',
28499             cls: 'nav nav-tabs pull-right',
28500             cn: [
28501                 header
28502             ]
28503         };
28504         
28505         if(this.tabScrollable){
28506             h = {
28507                 tag: 'div',
28508                 cls: 'tab-header',
28509                 cn: [
28510                     {
28511                         tag: 'ul',
28512                         cls: 'nav nav-tabs pull-right',
28513                         cn: [
28514                             header
28515                         ]
28516                     }
28517                 ]
28518             };
28519         }
28520         
28521         var cfg = {
28522             tag: 'div',
28523             cls: 'nav-tabs-custom',
28524             cn: [
28525                 h,
28526                 {
28527                     tag: 'div',
28528                     cls: 'tab-content no-padding',
28529                     cn: []
28530                 }
28531             ]
28532         };
28533
28534         return  cfg;
28535     },
28536     initEvents : function()
28537     {
28538         //Roo.log('add add pane handler');
28539         this.on('addpane', this.onAddPane, this);
28540     },
28541      /**
28542      * Updates the box title
28543      * @param {String} html to set the title to.
28544      */
28545     setTitle : function(value)
28546     {
28547         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28548     },
28549     onAddPane : function(pane)
28550     {
28551         this.panes.push(pane);
28552         //Roo.log('addpane');
28553         //Roo.log(pane);
28554         // tabs are rendere left to right..
28555         if(!this.showtabs){
28556             return;
28557         }
28558         
28559         var ctr = this.el.select('.nav-tabs', true).first();
28560          
28561          
28562         var existing = ctr.select('.nav-tab',true);
28563         var qty = existing.getCount();;
28564         
28565         
28566         var tab = ctr.createChild({
28567             tag : 'li',
28568             cls : 'nav-tab' + (qty ? '' : ' active'),
28569             cn : [
28570                 {
28571                     tag : 'a',
28572                     href:'#',
28573                     html : pane.title
28574                 }
28575             ]
28576         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28577         pane.tab = tab;
28578         
28579         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28580         if (!qty) {
28581             pane.el.addClass('active');
28582         }
28583         
28584                 
28585     },
28586     onTabClick : function(ev,un,ob,pane)
28587     {
28588         //Roo.log('tab - prev default');
28589         ev.preventDefault();
28590         
28591         
28592         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28593         pane.tab.addClass('active');
28594         //Roo.log(pane.title);
28595         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28596         // technically we should have a deactivate event.. but maybe add later.
28597         // and it should not de-activate the selected tab...
28598         this.fireEvent('activatepane', pane);
28599         pane.el.addClass('active');
28600         pane.fireEvent('activate');
28601         
28602         
28603     },
28604     
28605     getActivePane : function()
28606     {
28607         var r = false;
28608         Roo.each(this.panes, function(p) {
28609             if(p.el.hasClass('active')){
28610                 r = p;
28611                 return false;
28612             }
28613             
28614             return;
28615         });
28616         
28617         return r;
28618     }
28619     
28620     
28621 });
28622
28623  
28624 /*
28625  * - LGPL
28626  *
28627  * Tab pane
28628  * 
28629  */
28630 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28631 /**
28632  * @class Roo.bootstrap.TabPane
28633  * @extends Roo.bootstrap.Component
28634  * Bootstrap TabPane class
28635  * @cfg {Boolean} active (false | true) Default false
28636  * @cfg {String} title title of panel
28637
28638  * 
28639  * @constructor
28640  * Create a new TabPane
28641  * @param {Object} config The config object
28642  */
28643
28644 Roo.bootstrap.dash.TabPane = function(config){
28645     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28646     
28647     this.addEvents({
28648         // raw events
28649         /**
28650          * @event activate
28651          * When a pane is activated
28652          * @param {Roo.bootstrap.dash.TabPane} pane
28653          */
28654         "activate" : true
28655          
28656     });
28657 };
28658
28659 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28660     
28661     active : false,
28662     title : '',
28663     
28664     // the tabBox that this is attached to.
28665     tab : false,
28666      
28667     getAutoCreate : function() 
28668     {
28669         var cfg = {
28670             tag: 'div',
28671             cls: 'tab-pane'
28672         };
28673         
28674         if(this.active){
28675             cfg.cls += ' active';
28676         }
28677         
28678         return cfg;
28679     },
28680     initEvents  : function()
28681     {
28682         //Roo.log('trigger add pane handler');
28683         this.parent().fireEvent('addpane', this)
28684     },
28685     
28686      /**
28687      * Updates the tab title 
28688      * @param {String} html to set the title to.
28689      */
28690     setTitle: function(str)
28691     {
28692         if (!this.tab) {
28693             return;
28694         }
28695         this.title = str;
28696         this.tab.select('a', true).first().dom.innerHTML = str;
28697         
28698     }
28699     
28700     
28701     
28702 });
28703
28704  
28705
28706
28707  /*
28708  * - LGPL
28709  *
28710  * menu
28711  * 
28712  */
28713 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28714
28715 /**
28716  * @class Roo.bootstrap.menu.Menu
28717  * @extends Roo.bootstrap.Component
28718  * Bootstrap Menu class - container for Menu
28719  * @cfg {String} html Text of the menu
28720  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28721  * @cfg {String} icon Font awesome icon
28722  * @cfg {String} pos Menu align to (top | bottom) default bottom
28723  * 
28724  * 
28725  * @constructor
28726  * Create a new Menu
28727  * @param {Object} config The config object
28728  */
28729
28730
28731 Roo.bootstrap.menu.Menu = function(config){
28732     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28733     
28734     this.addEvents({
28735         /**
28736          * @event beforeshow
28737          * Fires before this menu is displayed
28738          * @param {Roo.bootstrap.menu.Menu} this
28739          */
28740         beforeshow : true,
28741         /**
28742          * @event beforehide
28743          * Fires before this menu is hidden
28744          * @param {Roo.bootstrap.menu.Menu} this
28745          */
28746         beforehide : true,
28747         /**
28748          * @event show
28749          * Fires after this menu is displayed
28750          * @param {Roo.bootstrap.menu.Menu} this
28751          */
28752         show : true,
28753         /**
28754          * @event hide
28755          * Fires after this menu is hidden
28756          * @param {Roo.bootstrap.menu.Menu} this
28757          */
28758         hide : true,
28759         /**
28760          * @event click
28761          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28762          * @param {Roo.bootstrap.menu.Menu} this
28763          * @param {Roo.EventObject} e
28764          */
28765         click : true
28766     });
28767     
28768 };
28769
28770 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28771     
28772     submenu : false,
28773     html : '',
28774     weight : 'default',
28775     icon : false,
28776     pos : 'bottom',
28777     
28778     
28779     getChildContainer : function() {
28780         if(this.isSubMenu){
28781             return this.el;
28782         }
28783         
28784         return this.el.select('ul.dropdown-menu', true).first();  
28785     },
28786     
28787     getAutoCreate : function()
28788     {
28789         var text = [
28790             {
28791                 tag : 'span',
28792                 cls : 'roo-menu-text',
28793                 html : this.html
28794             }
28795         ];
28796         
28797         if(this.icon){
28798             text.unshift({
28799                 tag : 'i',
28800                 cls : 'fa ' + this.icon
28801             })
28802         }
28803         
28804         
28805         var cfg = {
28806             tag : 'div',
28807             cls : 'btn-group',
28808             cn : [
28809                 {
28810                     tag : 'button',
28811                     cls : 'dropdown-button btn btn-' + this.weight,
28812                     cn : text
28813                 },
28814                 {
28815                     tag : 'button',
28816                     cls : 'dropdown-toggle btn btn-' + this.weight,
28817                     cn : [
28818                         {
28819                             tag : 'span',
28820                             cls : 'caret'
28821                         }
28822                     ]
28823                 },
28824                 {
28825                     tag : 'ul',
28826                     cls : 'dropdown-menu'
28827                 }
28828             ]
28829             
28830         };
28831         
28832         if(this.pos == 'top'){
28833             cfg.cls += ' dropup';
28834         }
28835         
28836         if(this.isSubMenu){
28837             cfg = {
28838                 tag : 'ul',
28839                 cls : 'dropdown-menu'
28840             }
28841         }
28842         
28843         return cfg;
28844     },
28845     
28846     onRender : function(ct, position)
28847     {
28848         this.isSubMenu = ct.hasClass('dropdown-submenu');
28849         
28850         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28851     },
28852     
28853     initEvents : function() 
28854     {
28855         if(this.isSubMenu){
28856             return;
28857         }
28858         
28859         this.hidden = true;
28860         
28861         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28862         this.triggerEl.on('click', this.onTriggerPress, this);
28863         
28864         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28865         this.buttonEl.on('click', this.onClick, this);
28866         
28867     },
28868     
28869     list : function()
28870     {
28871         if(this.isSubMenu){
28872             return this.el;
28873         }
28874         
28875         return this.el.select('ul.dropdown-menu', true).first();
28876     },
28877     
28878     onClick : function(e)
28879     {
28880         this.fireEvent("click", this, e);
28881     },
28882     
28883     onTriggerPress  : function(e)
28884     {   
28885         if (this.isVisible()) {
28886             this.hide();
28887         } else {
28888             this.show();
28889         }
28890     },
28891     
28892     isVisible : function(){
28893         return !this.hidden;
28894     },
28895     
28896     show : function()
28897     {
28898         this.fireEvent("beforeshow", this);
28899         
28900         this.hidden = false;
28901         this.el.addClass('open');
28902         
28903         Roo.get(document).on("mouseup", this.onMouseUp, this);
28904         
28905         this.fireEvent("show", this);
28906         
28907         
28908     },
28909     
28910     hide : function()
28911     {
28912         this.fireEvent("beforehide", this);
28913         
28914         this.hidden = true;
28915         this.el.removeClass('open');
28916         
28917         Roo.get(document).un("mouseup", this.onMouseUp);
28918         
28919         this.fireEvent("hide", this);
28920     },
28921     
28922     onMouseUp : function()
28923     {
28924         this.hide();
28925     }
28926     
28927 });
28928
28929  
28930  /*
28931  * - LGPL
28932  *
28933  * menu item
28934  * 
28935  */
28936 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28937
28938 /**
28939  * @class Roo.bootstrap.menu.Item
28940  * @extends Roo.bootstrap.Component
28941  * Bootstrap MenuItem class
28942  * @cfg {Boolean} submenu (true | false) default false
28943  * @cfg {String} html text of the item
28944  * @cfg {String} href the link
28945  * @cfg {Boolean} disable (true | false) default false
28946  * @cfg {Boolean} preventDefault (true | false) default true
28947  * @cfg {String} icon Font awesome icon
28948  * @cfg {String} pos Submenu align to (left | right) default right 
28949  * 
28950  * 
28951  * @constructor
28952  * Create a new Item
28953  * @param {Object} config The config object
28954  */
28955
28956
28957 Roo.bootstrap.menu.Item = function(config){
28958     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28959     this.addEvents({
28960         /**
28961          * @event mouseover
28962          * Fires when the mouse is hovering over this menu
28963          * @param {Roo.bootstrap.menu.Item} this
28964          * @param {Roo.EventObject} e
28965          */
28966         mouseover : true,
28967         /**
28968          * @event mouseout
28969          * Fires when the mouse exits this menu
28970          * @param {Roo.bootstrap.menu.Item} this
28971          * @param {Roo.EventObject} e
28972          */
28973         mouseout : true,
28974         // raw events
28975         /**
28976          * @event click
28977          * The raw click event for the entire grid.
28978          * @param {Roo.EventObject} e
28979          */
28980         click : true
28981     });
28982 };
28983
28984 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28985     
28986     submenu : false,
28987     href : '',
28988     html : '',
28989     preventDefault: true,
28990     disable : false,
28991     icon : false,
28992     pos : 'right',
28993     
28994     getAutoCreate : function()
28995     {
28996         var text = [
28997             {
28998                 tag : 'span',
28999                 cls : 'roo-menu-item-text',
29000                 html : this.html
29001             }
29002         ];
29003         
29004         if(this.icon){
29005             text.unshift({
29006                 tag : 'i',
29007                 cls : 'fa ' + this.icon
29008             })
29009         }
29010         
29011         var cfg = {
29012             tag : 'li',
29013             cn : [
29014                 {
29015                     tag : 'a',
29016                     href : this.href || '#',
29017                     cn : text
29018                 }
29019             ]
29020         };
29021         
29022         if(this.disable){
29023             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
29024         }
29025         
29026         if(this.submenu){
29027             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
29028             
29029             if(this.pos == 'left'){
29030                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
29031             }
29032         }
29033         
29034         return cfg;
29035     },
29036     
29037     initEvents : function() 
29038     {
29039         this.el.on('mouseover', this.onMouseOver, this);
29040         this.el.on('mouseout', this.onMouseOut, this);
29041         
29042         this.el.select('a', true).first().on('click', this.onClick, this);
29043         
29044     },
29045     
29046     onClick : function(e)
29047     {
29048         if(this.preventDefault){
29049             e.preventDefault();
29050         }
29051         
29052         this.fireEvent("click", this, e);
29053     },
29054     
29055     onMouseOver : function(e)
29056     {
29057         if(this.submenu && this.pos == 'left'){
29058             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29059         }
29060         
29061         this.fireEvent("mouseover", this, e);
29062     },
29063     
29064     onMouseOut : function(e)
29065     {
29066         this.fireEvent("mouseout", this, e);
29067     }
29068 });
29069
29070  
29071
29072  /*
29073  * - LGPL
29074  *
29075  * menu separator
29076  * 
29077  */
29078 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29079
29080 /**
29081  * @class Roo.bootstrap.menu.Separator
29082  * @extends Roo.bootstrap.Component
29083  * Bootstrap Separator class
29084  * 
29085  * @constructor
29086  * Create a new Separator
29087  * @param {Object} config The config object
29088  */
29089
29090
29091 Roo.bootstrap.menu.Separator = function(config){
29092     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29093 };
29094
29095 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29096     
29097     getAutoCreate : function(){
29098         var cfg = {
29099             tag : 'li',
29100             cls: 'dropdown-divider divider'
29101         };
29102         
29103         return cfg;
29104     }
29105    
29106 });
29107
29108  
29109
29110  /*
29111  * - LGPL
29112  *
29113  * Tooltip
29114  * 
29115  */
29116
29117 /**
29118  * @class Roo.bootstrap.Tooltip
29119  * Bootstrap Tooltip class
29120  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29121  * to determine which dom element triggers the tooltip.
29122  * 
29123  * It needs to add support for additional attributes like tooltip-position
29124  * 
29125  * @constructor
29126  * Create a new Toolti
29127  * @param {Object} config The config object
29128  */
29129
29130 Roo.bootstrap.Tooltip = function(config){
29131     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29132     
29133     this.alignment = Roo.bootstrap.Tooltip.alignment;
29134     
29135     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29136         this.alignment = config.alignment;
29137     }
29138     
29139 };
29140
29141 Roo.apply(Roo.bootstrap.Tooltip, {
29142     /**
29143      * @function init initialize tooltip monitoring.
29144      * @static
29145      */
29146     currentEl : false,
29147     currentTip : false,
29148     currentRegion : false,
29149     
29150     //  init : delay?
29151     
29152     init : function()
29153     {
29154         Roo.get(document).on('mouseover', this.enter ,this);
29155         Roo.get(document).on('mouseout', this.leave, this);
29156          
29157         
29158         this.currentTip = new Roo.bootstrap.Tooltip();
29159     },
29160     
29161     enter : function(ev)
29162     {
29163         var dom = ev.getTarget();
29164         
29165         //Roo.log(['enter',dom]);
29166         var el = Roo.fly(dom);
29167         if (this.currentEl) {
29168             //Roo.log(dom);
29169             //Roo.log(this.currentEl);
29170             //Roo.log(this.currentEl.contains(dom));
29171             if (this.currentEl == el) {
29172                 return;
29173             }
29174             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29175                 return;
29176             }
29177
29178         }
29179         
29180         if (this.currentTip.el) {
29181             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29182         }    
29183         //Roo.log(ev);
29184         
29185         if(!el || el.dom == document){
29186             return;
29187         }
29188         
29189         var bindEl = el; 
29190         var pel = false;
29191         if (!el.attr('tooltip')) {
29192             pel = el.findParent("[tooltip]");
29193             if (pel) {
29194                 bindEl = Roo.get(pel);
29195             }
29196         }
29197         
29198        
29199         
29200         // you can not look for children, as if el is the body.. then everythign is the child..
29201         if (!pel && !el.attr('tooltip')) { //
29202             if (!el.select("[tooltip]").elements.length) {
29203                 return;
29204             }
29205             // is the mouse over this child...?
29206             bindEl = el.select("[tooltip]").first();
29207             var xy = ev.getXY();
29208             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29209                 //Roo.log("not in region.");
29210                 return;
29211             }
29212             //Roo.log("child element over..");
29213             
29214         }
29215         this.currentEl = el;
29216         this.currentTip.bind(bindEl);
29217         this.currentRegion = Roo.lib.Region.getRegion(dom);
29218         this.currentTip.enter();
29219         
29220     },
29221     leave : function(ev)
29222     {
29223         var dom = ev.getTarget();
29224         //Roo.log(['leave',dom]);
29225         if (!this.currentEl) {
29226             return;
29227         }
29228         
29229         
29230         if (dom != this.currentEl.dom) {
29231             return;
29232         }
29233         var xy = ev.getXY();
29234         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29235             return;
29236         }
29237         // only activate leave if mouse cursor is outside... bounding box..
29238         
29239         
29240         
29241         
29242         if (this.currentTip) {
29243             this.currentTip.leave();
29244         }
29245         //Roo.log('clear currentEl');
29246         this.currentEl = false;
29247         
29248         
29249     },
29250     alignment : {
29251         'left' : ['r-l', [-2,0], 'right'],
29252         'right' : ['l-r', [2,0], 'left'],
29253         'bottom' : ['t-b', [0,2], 'top'],
29254         'top' : [ 'b-t', [0,-2], 'bottom']
29255     }
29256     
29257 });
29258
29259
29260 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29261     
29262     
29263     bindEl : false,
29264     
29265     delay : null, // can be { show : 300 , hide: 500}
29266     
29267     timeout : null,
29268     
29269     hoverState : null, //???
29270     
29271     placement : 'bottom', 
29272     
29273     alignment : false,
29274     
29275     getAutoCreate : function(){
29276     
29277         var cfg = {
29278            cls : 'tooltip',   
29279            role : 'tooltip',
29280            cn : [
29281                 {
29282                     cls : 'tooltip-arrow arrow'
29283                 },
29284                 {
29285                     cls : 'tooltip-inner'
29286                 }
29287            ]
29288         };
29289         
29290         return cfg;
29291     },
29292     bind : function(el)
29293     {
29294         this.bindEl = el;
29295     },
29296     
29297     initEvents : function()
29298     {
29299         this.arrowEl = this.el.select('.arrow', true).first();
29300         this.innerEl = this.el.select('.tooltip-inner', true).first();
29301     },
29302     
29303     enter : function () {
29304        
29305         if (this.timeout != null) {
29306             clearTimeout(this.timeout);
29307         }
29308         
29309         this.hoverState = 'in';
29310          //Roo.log("enter - show");
29311         if (!this.delay || !this.delay.show) {
29312             this.show();
29313             return;
29314         }
29315         var _t = this;
29316         this.timeout = setTimeout(function () {
29317             if (_t.hoverState == 'in') {
29318                 _t.show();
29319             }
29320         }, this.delay.show);
29321     },
29322     leave : function()
29323     {
29324         clearTimeout(this.timeout);
29325     
29326         this.hoverState = 'out';
29327          if (!this.delay || !this.delay.hide) {
29328             this.hide();
29329             return;
29330         }
29331        
29332         var _t = this;
29333         this.timeout = setTimeout(function () {
29334             //Roo.log("leave - timeout");
29335             
29336             if (_t.hoverState == 'out') {
29337                 _t.hide();
29338                 Roo.bootstrap.Tooltip.currentEl = false;
29339             }
29340         }, delay);
29341     },
29342     
29343     show : function (msg)
29344     {
29345         if (!this.el) {
29346             this.render(document.body);
29347         }
29348         // set content.
29349         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29350         
29351         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29352         
29353         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29354         
29355         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29356                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29357         
29358         var placement = typeof this.placement == 'function' ?
29359             this.placement.call(this, this.el, on_el) :
29360             this.placement;
29361             
29362         var autoToken = /\s?auto?\s?/i;
29363         var autoPlace = autoToken.test(placement);
29364         if (autoPlace) {
29365             placement = placement.replace(autoToken, '') || 'top';
29366         }
29367         
29368         //this.el.detach()
29369         //this.el.setXY([0,0]);
29370         this.el.show();
29371         //this.el.dom.style.display='block';
29372         
29373         //this.el.appendTo(on_el);
29374         
29375         var p = this.getPosition();
29376         var box = this.el.getBox();
29377         
29378         if (autoPlace) {
29379             // fixme..
29380         }
29381         
29382         var align = this.alignment[placement];
29383         
29384         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29385         
29386         if(placement == 'top' || placement == 'bottom'){
29387             if(xy[0] < 0){
29388                 placement = 'right';
29389             }
29390             
29391             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29392                 placement = 'left';
29393             }
29394             
29395             var scroll = Roo.select('body', true).first().getScroll();
29396             
29397             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29398                 placement = 'top';
29399             }
29400             
29401             align = this.alignment[placement];
29402             
29403             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29404             
29405         }
29406         
29407         var elems = document.getElementsByTagName('div');
29408         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29409         for (var i = 0; i < elems.length; i++) {
29410           var zindex = Number.parseInt(
29411                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29412                 10
29413           );
29414           if (zindex > highest) {
29415             highest = zindex;
29416           }
29417         }
29418         
29419         
29420         
29421         this.el.dom.style.zIndex = highest;
29422         
29423         this.el.alignTo(this.bindEl, align[0],align[1]);
29424         //var arrow = this.el.select('.arrow',true).first();
29425         //arrow.set(align[2], 
29426         
29427         this.el.addClass(placement);
29428         this.el.addClass("bs-tooltip-"+ placement);
29429         
29430         this.el.addClass('in fade show');
29431         
29432         this.hoverState = null;
29433         
29434         if (this.el.hasClass('fade')) {
29435             // fade it?
29436         }
29437         
29438         
29439         
29440         
29441         
29442     },
29443     hide : function()
29444     {
29445          
29446         if (!this.el) {
29447             return;
29448         }
29449         //this.el.setXY([0,0]);
29450         this.el.removeClass(['show', 'in']);
29451         //this.el.hide();
29452         
29453     }
29454     
29455 });
29456  
29457
29458  /*
29459  * - LGPL
29460  *
29461  * Location Picker
29462  * 
29463  */
29464
29465 /**
29466  * @class Roo.bootstrap.LocationPicker
29467  * @extends Roo.bootstrap.Component
29468  * Bootstrap LocationPicker class
29469  * @cfg {Number} latitude Position when init default 0
29470  * @cfg {Number} longitude Position when init default 0
29471  * @cfg {Number} zoom default 15
29472  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29473  * @cfg {Boolean} mapTypeControl default false
29474  * @cfg {Boolean} disableDoubleClickZoom default false
29475  * @cfg {Boolean} scrollwheel default true
29476  * @cfg {Boolean} streetViewControl default false
29477  * @cfg {Number} radius default 0
29478  * @cfg {String} locationName
29479  * @cfg {Boolean} draggable default true
29480  * @cfg {Boolean} enableAutocomplete default false
29481  * @cfg {Boolean} enableReverseGeocode default true
29482  * @cfg {String} markerTitle
29483  * 
29484  * @constructor
29485  * Create a new LocationPicker
29486  * @param {Object} config The config object
29487  */
29488
29489
29490 Roo.bootstrap.LocationPicker = function(config){
29491     
29492     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29493     
29494     this.addEvents({
29495         /**
29496          * @event initial
29497          * Fires when the picker initialized.
29498          * @param {Roo.bootstrap.LocationPicker} this
29499          * @param {Google Location} location
29500          */
29501         initial : true,
29502         /**
29503          * @event positionchanged
29504          * Fires when the picker position changed.
29505          * @param {Roo.bootstrap.LocationPicker} this
29506          * @param {Google Location} location
29507          */
29508         positionchanged : true,
29509         /**
29510          * @event resize
29511          * Fires when the map resize.
29512          * @param {Roo.bootstrap.LocationPicker} this
29513          */
29514         resize : true,
29515         /**
29516          * @event show
29517          * Fires when the map show.
29518          * @param {Roo.bootstrap.LocationPicker} this
29519          */
29520         show : true,
29521         /**
29522          * @event hide
29523          * Fires when the map hide.
29524          * @param {Roo.bootstrap.LocationPicker} this
29525          */
29526         hide : true,
29527         /**
29528          * @event mapClick
29529          * Fires when click the map.
29530          * @param {Roo.bootstrap.LocationPicker} this
29531          * @param {Map event} e
29532          */
29533         mapClick : true,
29534         /**
29535          * @event mapRightClick
29536          * Fires when right click the map.
29537          * @param {Roo.bootstrap.LocationPicker} this
29538          * @param {Map event} e
29539          */
29540         mapRightClick : true,
29541         /**
29542          * @event markerClick
29543          * Fires when click the marker.
29544          * @param {Roo.bootstrap.LocationPicker} this
29545          * @param {Map event} e
29546          */
29547         markerClick : true,
29548         /**
29549          * @event markerRightClick
29550          * Fires when right click the marker.
29551          * @param {Roo.bootstrap.LocationPicker} this
29552          * @param {Map event} e
29553          */
29554         markerRightClick : true,
29555         /**
29556          * @event OverlayViewDraw
29557          * Fires when OverlayView Draw
29558          * @param {Roo.bootstrap.LocationPicker} this
29559          */
29560         OverlayViewDraw : true,
29561         /**
29562          * @event OverlayViewOnAdd
29563          * Fires when OverlayView Draw
29564          * @param {Roo.bootstrap.LocationPicker} this
29565          */
29566         OverlayViewOnAdd : true,
29567         /**
29568          * @event OverlayViewOnRemove
29569          * Fires when OverlayView Draw
29570          * @param {Roo.bootstrap.LocationPicker} this
29571          */
29572         OverlayViewOnRemove : true,
29573         /**
29574          * @event OverlayViewShow
29575          * Fires when OverlayView Draw
29576          * @param {Roo.bootstrap.LocationPicker} this
29577          * @param {Pixel} cpx
29578          */
29579         OverlayViewShow : true,
29580         /**
29581          * @event OverlayViewHide
29582          * Fires when OverlayView Draw
29583          * @param {Roo.bootstrap.LocationPicker} this
29584          */
29585         OverlayViewHide : true,
29586         /**
29587          * @event loadexception
29588          * Fires when load google lib failed.
29589          * @param {Roo.bootstrap.LocationPicker} this
29590          */
29591         loadexception : true
29592     });
29593         
29594 };
29595
29596 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29597     
29598     gMapContext: false,
29599     
29600     latitude: 0,
29601     longitude: 0,
29602     zoom: 15,
29603     mapTypeId: false,
29604     mapTypeControl: false,
29605     disableDoubleClickZoom: false,
29606     scrollwheel: true,
29607     streetViewControl: false,
29608     radius: 0,
29609     locationName: '',
29610     draggable: true,
29611     enableAutocomplete: false,
29612     enableReverseGeocode: true,
29613     markerTitle: '',
29614     
29615     getAutoCreate: function()
29616     {
29617
29618         var cfg = {
29619             tag: 'div',
29620             cls: 'roo-location-picker'
29621         };
29622         
29623         return cfg
29624     },
29625     
29626     initEvents: function(ct, position)
29627     {       
29628         if(!this.el.getWidth() || this.isApplied()){
29629             return;
29630         }
29631         
29632         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29633         
29634         this.initial();
29635     },
29636     
29637     initial: function()
29638     {
29639         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29640             this.fireEvent('loadexception', this);
29641             return;
29642         }
29643         
29644         if(!this.mapTypeId){
29645             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29646         }
29647         
29648         this.gMapContext = this.GMapContext();
29649         
29650         this.initOverlayView();
29651         
29652         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29653         
29654         var _this = this;
29655                 
29656         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29657             _this.setPosition(_this.gMapContext.marker.position);
29658         });
29659         
29660         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29661             _this.fireEvent('mapClick', this, event);
29662             
29663         });
29664
29665         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29666             _this.fireEvent('mapRightClick', this, event);
29667             
29668         });
29669         
29670         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29671             _this.fireEvent('markerClick', this, event);
29672             
29673         });
29674
29675         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29676             _this.fireEvent('markerRightClick', this, event);
29677             
29678         });
29679         
29680         this.setPosition(this.gMapContext.location);
29681         
29682         this.fireEvent('initial', this, this.gMapContext.location);
29683     },
29684     
29685     initOverlayView: function()
29686     {
29687         var _this = this;
29688         
29689         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29690             
29691             draw: function()
29692             {
29693                 _this.fireEvent('OverlayViewDraw', _this);
29694             },
29695             
29696             onAdd: function()
29697             {
29698                 _this.fireEvent('OverlayViewOnAdd', _this);
29699             },
29700             
29701             onRemove: function()
29702             {
29703                 _this.fireEvent('OverlayViewOnRemove', _this);
29704             },
29705             
29706             show: function(cpx)
29707             {
29708                 _this.fireEvent('OverlayViewShow', _this, cpx);
29709             },
29710             
29711             hide: function()
29712             {
29713                 _this.fireEvent('OverlayViewHide', _this);
29714             }
29715             
29716         });
29717     },
29718     
29719     fromLatLngToContainerPixel: function(event)
29720     {
29721         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29722     },
29723     
29724     isApplied: function() 
29725     {
29726         return this.getGmapContext() == false ? false : true;
29727     },
29728     
29729     getGmapContext: function() 
29730     {
29731         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29732     },
29733     
29734     GMapContext: function() 
29735     {
29736         var position = new google.maps.LatLng(this.latitude, this.longitude);
29737         
29738         var _map = new google.maps.Map(this.el.dom, {
29739             center: position,
29740             zoom: this.zoom,
29741             mapTypeId: this.mapTypeId,
29742             mapTypeControl: this.mapTypeControl,
29743             disableDoubleClickZoom: this.disableDoubleClickZoom,
29744             scrollwheel: this.scrollwheel,
29745             streetViewControl: this.streetViewControl,
29746             locationName: this.locationName,
29747             draggable: this.draggable,
29748             enableAutocomplete: this.enableAutocomplete,
29749             enableReverseGeocode: this.enableReverseGeocode
29750         });
29751         
29752         var _marker = new google.maps.Marker({
29753             position: position,
29754             map: _map,
29755             title: this.markerTitle,
29756             draggable: this.draggable
29757         });
29758         
29759         return {
29760             map: _map,
29761             marker: _marker,
29762             circle: null,
29763             location: position,
29764             radius: this.radius,
29765             locationName: this.locationName,
29766             addressComponents: {
29767                 formatted_address: null,
29768                 addressLine1: null,
29769                 addressLine2: null,
29770                 streetName: null,
29771                 streetNumber: null,
29772                 city: null,
29773                 district: null,
29774                 state: null,
29775                 stateOrProvince: null
29776             },
29777             settings: this,
29778             domContainer: this.el.dom,
29779             geodecoder: new google.maps.Geocoder()
29780         };
29781     },
29782     
29783     drawCircle: function(center, radius, options) 
29784     {
29785         if (this.gMapContext.circle != null) {
29786             this.gMapContext.circle.setMap(null);
29787         }
29788         if (radius > 0) {
29789             radius *= 1;
29790             options = Roo.apply({}, options, {
29791                 strokeColor: "#0000FF",
29792                 strokeOpacity: .35,
29793                 strokeWeight: 2,
29794                 fillColor: "#0000FF",
29795                 fillOpacity: .2
29796             });
29797             
29798             options.map = this.gMapContext.map;
29799             options.radius = radius;
29800             options.center = center;
29801             this.gMapContext.circle = new google.maps.Circle(options);
29802             return this.gMapContext.circle;
29803         }
29804         
29805         return null;
29806     },
29807     
29808     setPosition: function(location) 
29809     {
29810         this.gMapContext.location = location;
29811         this.gMapContext.marker.setPosition(location);
29812         this.gMapContext.map.panTo(location);
29813         this.drawCircle(location, this.gMapContext.radius, {});
29814         
29815         var _this = this;
29816         
29817         if (this.gMapContext.settings.enableReverseGeocode) {
29818             this.gMapContext.geodecoder.geocode({
29819                 latLng: this.gMapContext.location
29820             }, function(results, status) {
29821                 
29822                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29823                     _this.gMapContext.locationName = results[0].formatted_address;
29824                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29825                     
29826                     _this.fireEvent('positionchanged', this, location);
29827                 }
29828             });
29829             
29830             return;
29831         }
29832         
29833         this.fireEvent('positionchanged', this, location);
29834     },
29835     
29836     resize: function()
29837     {
29838         google.maps.event.trigger(this.gMapContext.map, "resize");
29839         
29840         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29841         
29842         this.fireEvent('resize', this);
29843     },
29844     
29845     setPositionByLatLng: function(latitude, longitude)
29846     {
29847         this.setPosition(new google.maps.LatLng(latitude, longitude));
29848     },
29849     
29850     getCurrentPosition: function() 
29851     {
29852         return {
29853             latitude: this.gMapContext.location.lat(),
29854             longitude: this.gMapContext.location.lng()
29855         };
29856     },
29857     
29858     getAddressName: function() 
29859     {
29860         return this.gMapContext.locationName;
29861     },
29862     
29863     getAddressComponents: function() 
29864     {
29865         return this.gMapContext.addressComponents;
29866     },
29867     
29868     address_component_from_google_geocode: function(address_components) 
29869     {
29870         var result = {};
29871         
29872         for (var i = 0; i < address_components.length; i++) {
29873             var component = address_components[i];
29874             if (component.types.indexOf("postal_code") >= 0) {
29875                 result.postalCode = component.short_name;
29876             } else if (component.types.indexOf("street_number") >= 0) {
29877                 result.streetNumber = component.short_name;
29878             } else if (component.types.indexOf("route") >= 0) {
29879                 result.streetName = component.short_name;
29880             } else if (component.types.indexOf("neighborhood") >= 0) {
29881                 result.city = component.short_name;
29882             } else if (component.types.indexOf("locality") >= 0) {
29883                 result.city = component.short_name;
29884             } else if (component.types.indexOf("sublocality") >= 0) {
29885                 result.district = component.short_name;
29886             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29887                 result.stateOrProvince = component.short_name;
29888             } else if (component.types.indexOf("country") >= 0) {
29889                 result.country = component.short_name;
29890             }
29891         }
29892         
29893         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29894         result.addressLine2 = "";
29895         return result;
29896     },
29897     
29898     setZoomLevel: function(zoom)
29899     {
29900         this.gMapContext.map.setZoom(zoom);
29901     },
29902     
29903     show: function()
29904     {
29905         if(!this.el){
29906             return;
29907         }
29908         
29909         this.el.show();
29910         
29911         this.resize();
29912         
29913         this.fireEvent('show', this);
29914     },
29915     
29916     hide: function()
29917     {
29918         if(!this.el){
29919             return;
29920         }
29921         
29922         this.el.hide();
29923         
29924         this.fireEvent('hide', this);
29925     }
29926     
29927 });
29928
29929 Roo.apply(Roo.bootstrap.LocationPicker, {
29930     
29931     OverlayView : function(map, options)
29932     {
29933         options = options || {};
29934         
29935         this.setMap(map);
29936     }
29937     
29938     
29939 });/**
29940  * @class Roo.bootstrap.Alert
29941  * @extends Roo.bootstrap.Component
29942  * Bootstrap Alert class - shows an alert area box
29943  * eg
29944  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29945   Enter a valid email address
29946 </div>
29947  * @licence LGPL
29948  * @cfg {String} title The title of alert
29949  * @cfg {String} html The content of alert
29950  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29951  * @cfg {String} fa font-awesomeicon
29952  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29953  * @cfg {Boolean} close true to show a x closer
29954  * 
29955  * 
29956  * @constructor
29957  * Create a new alert
29958  * @param {Object} config The config object
29959  */
29960
29961
29962 Roo.bootstrap.Alert = function(config){
29963     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29964     
29965 };
29966
29967 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29968     
29969     title: '',
29970     html: '',
29971     weight: false,
29972     fa: false,
29973     faicon: false, // BC
29974     close : false,
29975     
29976     
29977     getAutoCreate : function()
29978     {
29979         
29980         var cfg = {
29981             tag : 'div',
29982             cls : 'alert',
29983             cn : [
29984                 {
29985                     tag: 'button',
29986                     type :  "button",
29987                     cls: "close",
29988                     html : '×',
29989                     style : this.close ? '' : 'display:none'
29990                 },
29991                 {
29992                     tag : 'i',
29993                     cls : 'roo-alert-icon'
29994                     
29995                 },
29996                 {
29997                     tag : 'b',
29998                     cls : 'roo-alert-title',
29999                     html : this.title
30000                 },
30001                 {
30002                     tag : 'span',
30003                     cls : 'roo-alert-text',
30004                     html : this.html
30005                 }
30006             ]
30007         };
30008         
30009         if(this.faicon){
30010             cfg.cn[0].cls += ' fa ' + this.faicon;
30011         }
30012         if(this.fa){
30013             cfg.cn[0].cls += ' fa ' + this.fa;
30014         }
30015         
30016         if(this.weight){
30017             cfg.cls += ' alert-' + this.weight;
30018         }
30019         
30020         return cfg;
30021     },
30022     
30023     initEvents: function() 
30024     {
30025         this.el.setVisibilityMode(Roo.Element.DISPLAY);
30026         this.titleEl =  this.el.select('.roo-alert-title',true).first();
30027         this.iconEl = this.el.select('.roo-alert-icon',true).first();
30028         this.htmlEl = this.el.select('.roo-alert-text',true).first();
30029         if (this.seconds > 0) {
30030             this.hide.defer(this.seconds, this);
30031         }
30032     },
30033     /**
30034      * Set the Title Message HTML
30035      * @param {String} html
30036      */
30037     setTitle : function(str)
30038     {
30039         this.titleEl.dom.innerHTML = str;
30040     },
30041      
30042      /**
30043      * Set the Body Message HTML
30044      * @param {String} html
30045      */
30046     setHtml : function(str)
30047     {
30048         this.htmlEl.dom.innerHTML = str;
30049     },
30050     /**
30051      * Set the Weight of the alert
30052      * @param {String} (success|info|warning|danger) weight
30053      */
30054     
30055     setWeight : function(weight)
30056     {
30057         if(this.weight){
30058             this.el.removeClass('alert-' + this.weight);
30059         }
30060         
30061         this.weight = weight;
30062         
30063         this.el.addClass('alert-' + this.weight);
30064     },
30065       /**
30066      * Set the Icon of the alert
30067      * @param {String} see fontawsome names (name without the 'fa-' bit)
30068      */
30069     setIcon : function(icon)
30070     {
30071         if(this.faicon){
30072             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30073         }
30074         
30075         this.faicon = icon;
30076         
30077         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30078     },
30079     /**
30080      * Hide the Alert
30081      */
30082     hide: function() 
30083     {
30084         this.el.hide();   
30085     },
30086     /**
30087      * Show the Alert
30088      */
30089     show: function() 
30090     {  
30091         this.el.show();   
30092     }
30093     
30094 });
30095
30096  
30097 /*
30098 * Licence: LGPL
30099 */
30100
30101 /**
30102  * @class Roo.bootstrap.UploadCropbox
30103  * @extends Roo.bootstrap.Component
30104  * Bootstrap UploadCropbox class
30105  * @cfg {String} emptyText show when image has been loaded
30106  * @cfg {String} rotateNotify show when image too small to rotate
30107  * @cfg {Number} errorTimeout default 3000
30108  * @cfg {Number} minWidth default 300
30109  * @cfg {Number} minHeight default 300
30110  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30111  * @cfg {Boolean} isDocument (true|false) default false
30112  * @cfg {String} url action url
30113  * @cfg {String} paramName default 'imageUpload'
30114  * @cfg {String} method default POST
30115  * @cfg {Boolean} loadMask (true|false) default true
30116  * @cfg {Boolean} loadingText default 'Loading...'
30117  * 
30118  * @constructor
30119  * Create a new UploadCropbox
30120  * @param {Object} config The config object
30121  */
30122
30123 Roo.bootstrap.UploadCropbox = function(config){
30124     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30125     
30126     this.addEvents({
30127         /**
30128          * @event beforeselectfile
30129          * Fire before select file
30130          * @param {Roo.bootstrap.UploadCropbox} this
30131          */
30132         "beforeselectfile" : true,
30133         /**
30134          * @event initial
30135          * Fire after initEvent
30136          * @param {Roo.bootstrap.UploadCropbox} this
30137          */
30138         "initial" : true,
30139         /**
30140          * @event crop
30141          * Fire after initEvent
30142          * @param {Roo.bootstrap.UploadCropbox} this
30143          * @param {String} data
30144          */
30145         "crop" : true,
30146         /**
30147          * @event prepare
30148          * Fire when preparing the file data
30149          * @param {Roo.bootstrap.UploadCropbox} this
30150          * @param {Object} file
30151          */
30152         "prepare" : true,
30153         /**
30154          * @event exception
30155          * Fire when get exception
30156          * @param {Roo.bootstrap.UploadCropbox} this
30157          * @param {XMLHttpRequest} xhr
30158          */
30159         "exception" : true,
30160         /**
30161          * @event beforeloadcanvas
30162          * Fire before load the canvas
30163          * @param {Roo.bootstrap.UploadCropbox} this
30164          * @param {String} src
30165          */
30166         "beforeloadcanvas" : true,
30167         /**
30168          * @event trash
30169          * Fire when trash image
30170          * @param {Roo.bootstrap.UploadCropbox} this
30171          */
30172         "trash" : true,
30173         /**
30174          * @event download
30175          * Fire when download the image
30176          * @param {Roo.bootstrap.UploadCropbox} this
30177          */
30178         "download" : true,
30179         /**
30180          * @event footerbuttonclick
30181          * Fire when footerbuttonclick
30182          * @param {Roo.bootstrap.UploadCropbox} this
30183          * @param {String} type
30184          */
30185         "footerbuttonclick" : true,
30186         /**
30187          * @event resize
30188          * Fire when resize
30189          * @param {Roo.bootstrap.UploadCropbox} this
30190          */
30191         "resize" : true,
30192         /**
30193          * @event rotate
30194          * Fire when rotate the image
30195          * @param {Roo.bootstrap.UploadCropbox} this
30196          * @param {String} pos
30197          */
30198         "rotate" : true,
30199         /**
30200          * @event inspect
30201          * Fire when inspect the file
30202          * @param {Roo.bootstrap.UploadCropbox} this
30203          * @param {Object} file
30204          */
30205         "inspect" : true,
30206         /**
30207          * @event upload
30208          * Fire when xhr upload the file
30209          * @param {Roo.bootstrap.UploadCropbox} this
30210          * @param {Object} data
30211          */
30212         "upload" : true,
30213         /**
30214          * @event arrange
30215          * Fire when arrange the file data
30216          * @param {Roo.bootstrap.UploadCropbox} this
30217          * @param {Object} formData
30218          */
30219         "arrange" : true
30220     });
30221     
30222     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30223 };
30224
30225 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30226     
30227     emptyText : 'Click to upload image',
30228     rotateNotify : 'Image is too small to rotate',
30229     errorTimeout : 3000,
30230     scale : 0,
30231     baseScale : 1,
30232     rotate : 0,
30233     dragable : false,
30234     pinching : false,
30235     mouseX : 0,
30236     mouseY : 0,
30237     cropData : false,
30238     minWidth : 300,
30239     minHeight : 300,
30240     file : false,
30241     exif : {},
30242     baseRotate : 1,
30243     cropType : 'image/jpeg',
30244     buttons : false,
30245     canvasLoaded : false,
30246     isDocument : false,
30247     method : 'POST',
30248     paramName : 'imageUpload',
30249     loadMask : true,
30250     loadingText : 'Loading...',
30251     maskEl : false,
30252     
30253     getAutoCreate : function()
30254     {
30255         var cfg = {
30256             tag : 'div',
30257             cls : 'roo-upload-cropbox',
30258             cn : [
30259                 {
30260                     tag : 'input',
30261                     cls : 'roo-upload-cropbox-selector',
30262                     type : 'file'
30263                 },
30264                 {
30265                     tag : 'div',
30266                     cls : 'roo-upload-cropbox-body',
30267                     style : 'cursor:pointer',
30268                     cn : [
30269                         {
30270                             tag : 'div',
30271                             cls : 'roo-upload-cropbox-preview'
30272                         },
30273                         {
30274                             tag : 'div',
30275                             cls : 'roo-upload-cropbox-thumb'
30276                         },
30277                         {
30278                             tag : 'div',
30279                             cls : 'roo-upload-cropbox-empty-notify',
30280                             html : this.emptyText
30281                         },
30282                         {
30283                             tag : 'div',
30284                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30285                             html : this.rotateNotify
30286                         }
30287                     ]
30288                 },
30289                 {
30290                     tag : 'div',
30291                     cls : 'roo-upload-cropbox-footer',
30292                     cn : {
30293                         tag : 'div',
30294                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30295                         cn : []
30296                     }
30297                 }
30298             ]
30299         };
30300         
30301         return cfg;
30302     },
30303     
30304     onRender : function(ct, position)
30305     {
30306         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30307         
30308         if (this.buttons.length) {
30309             
30310             Roo.each(this.buttons, function(bb) {
30311                 
30312                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30313                 
30314                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30315                 
30316             }, this);
30317         }
30318         
30319         if(this.loadMask){
30320             this.maskEl = this.el;
30321         }
30322     },
30323     
30324     initEvents : function()
30325     {
30326         this.urlAPI = (window.createObjectURL && window) || 
30327                                 (window.URL && URL.revokeObjectURL && URL) || 
30328                                 (window.webkitURL && webkitURL);
30329                         
30330         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30331         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30332         
30333         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30334         this.selectorEl.hide();
30335         
30336         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30337         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30338         
30339         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30340         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30341         this.thumbEl.hide();
30342         
30343         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30344         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30345         
30346         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30347         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30348         this.errorEl.hide();
30349         
30350         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30351         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30352         this.footerEl.hide();
30353         
30354         this.setThumbBoxSize();
30355         
30356         this.bind();
30357         
30358         this.resize();
30359         
30360         this.fireEvent('initial', this);
30361     },
30362
30363     bind : function()
30364     {
30365         var _this = this;
30366         
30367         window.addEventListener("resize", function() { _this.resize(); } );
30368         
30369         this.bodyEl.on('click', this.beforeSelectFile, this);
30370         
30371         if(Roo.isTouch){
30372             this.bodyEl.on('touchstart', this.onTouchStart, this);
30373             this.bodyEl.on('touchmove', this.onTouchMove, this);
30374             this.bodyEl.on('touchend', this.onTouchEnd, this);
30375         }
30376         
30377         if(!Roo.isTouch){
30378             this.bodyEl.on('mousedown', this.onMouseDown, this);
30379             this.bodyEl.on('mousemove', this.onMouseMove, this);
30380             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30381             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30382             Roo.get(document).on('mouseup', this.onMouseUp, this);
30383         }
30384         
30385         this.selectorEl.on('change', this.onFileSelected, this);
30386     },
30387     
30388     reset : function()
30389     {    
30390         this.scale = 0;
30391         this.baseScale = 1;
30392         this.rotate = 0;
30393         this.baseRotate = 1;
30394         this.dragable = false;
30395         this.pinching = false;
30396         this.mouseX = 0;
30397         this.mouseY = 0;
30398         this.cropData = false;
30399         this.notifyEl.dom.innerHTML = this.emptyText;
30400         
30401         this.selectorEl.dom.value = '';
30402         
30403     },
30404     
30405     resize : function()
30406     {
30407         if(this.fireEvent('resize', this) != false){
30408             this.setThumbBoxPosition();
30409             this.setCanvasPosition();
30410         }
30411     },
30412     
30413     onFooterButtonClick : function(e, el, o, type)
30414     {
30415         switch (type) {
30416             case 'rotate-left' :
30417                 this.onRotateLeft(e);
30418                 break;
30419             case 'rotate-right' :
30420                 this.onRotateRight(e);
30421                 break;
30422             case 'picture' :
30423                 this.beforeSelectFile(e);
30424                 break;
30425             case 'trash' :
30426                 this.trash(e);
30427                 break;
30428             case 'crop' :
30429                 this.crop(e);
30430                 break;
30431             case 'download' :
30432                 this.download(e);
30433                 break;
30434             default :
30435                 break;
30436         }
30437         
30438         this.fireEvent('footerbuttonclick', this, type);
30439     },
30440     
30441     beforeSelectFile : function(e)
30442     {
30443         e.preventDefault();
30444         
30445         if(this.fireEvent('beforeselectfile', this) != false){
30446             this.selectorEl.dom.click();
30447         }
30448     },
30449     
30450     onFileSelected : function(e)
30451     {
30452         e.preventDefault();
30453         
30454         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30455             return;
30456         }
30457         
30458         var file = this.selectorEl.dom.files[0];
30459         
30460         if(this.fireEvent('inspect', this, file) != false){
30461             this.prepare(file);
30462         }
30463         
30464     },
30465     
30466     trash : function(e)
30467     {
30468         this.fireEvent('trash', this);
30469     },
30470     
30471     download : function(e)
30472     {
30473         this.fireEvent('download', this);
30474     },
30475     
30476     loadCanvas : function(src)
30477     {   
30478         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30479             
30480             this.reset();
30481             
30482             this.imageEl = document.createElement('img');
30483             
30484             var _this = this;
30485             
30486             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30487             
30488             this.imageEl.src = src;
30489         }
30490     },
30491     
30492     onLoadCanvas : function()
30493     {   
30494         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30495         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30496         
30497         this.bodyEl.un('click', this.beforeSelectFile, this);
30498         
30499         this.notifyEl.hide();
30500         this.thumbEl.show();
30501         this.footerEl.show();
30502         
30503         this.baseRotateLevel();
30504         
30505         if(this.isDocument){
30506             this.setThumbBoxSize();
30507         }
30508         
30509         this.setThumbBoxPosition();
30510         
30511         this.baseScaleLevel();
30512         
30513         this.draw();
30514         
30515         this.resize();
30516         
30517         this.canvasLoaded = true;
30518         
30519         if(this.loadMask){
30520             this.maskEl.unmask();
30521         }
30522         
30523     },
30524     
30525     setCanvasPosition : function()
30526     {   
30527         if(!this.canvasEl){
30528             return;
30529         }
30530         
30531         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30532         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30533         
30534         this.previewEl.setLeft(pw);
30535         this.previewEl.setTop(ph);
30536         
30537     },
30538     
30539     onMouseDown : function(e)
30540     {   
30541         e.stopEvent();
30542         
30543         this.dragable = true;
30544         this.pinching = false;
30545         
30546         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30547             this.dragable = false;
30548             return;
30549         }
30550         
30551         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30552         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30553         
30554     },
30555     
30556     onMouseMove : function(e)
30557     {   
30558         e.stopEvent();
30559         
30560         if(!this.canvasLoaded){
30561             return;
30562         }
30563         
30564         if (!this.dragable){
30565             return;
30566         }
30567         
30568         var minX = Math.ceil(this.thumbEl.getLeft(true));
30569         var minY = Math.ceil(this.thumbEl.getTop(true));
30570         
30571         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30572         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30573         
30574         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30575         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30576         
30577         x = x - this.mouseX;
30578         y = y - this.mouseY;
30579         
30580         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30581         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30582         
30583         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30584         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30585         
30586         this.previewEl.setLeft(bgX);
30587         this.previewEl.setTop(bgY);
30588         
30589         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30590         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30591     },
30592     
30593     onMouseUp : function(e)
30594     {   
30595         e.stopEvent();
30596         
30597         this.dragable = false;
30598     },
30599     
30600     onMouseWheel : function(e)
30601     {   
30602         e.stopEvent();
30603         
30604         this.startScale = this.scale;
30605         
30606         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30607         
30608         if(!this.zoomable()){
30609             this.scale = this.startScale;
30610             return;
30611         }
30612         
30613         this.draw();
30614         
30615         return;
30616     },
30617     
30618     zoomable : function()
30619     {
30620         var minScale = this.thumbEl.getWidth() / this.minWidth;
30621         
30622         if(this.minWidth < this.minHeight){
30623             minScale = this.thumbEl.getHeight() / this.minHeight;
30624         }
30625         
30626         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30627         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30628         
30629         if(
30630                 this.isDocument &&
30631                 (this.rotate == 0 || this.rotate == 180) && 
30632                 (
30633                     width > this.imageEl.OriginWidth || 
30634                     height > this.imageEl.OriginHeight ||
30635                     (width < this.minWidth && height < this.minHeight)
30636                 )
30637         ){
30638             return false;
30639         }
30640         
30641         if(
30642                 this.isDocument &&
30643                 (this.rotate == 90 || this.rotate == 270) && 
30644                 (
30645                     width > this.imageEl.OriginWidth || 
30646                     height > this.imageEl.OriginHeight ||
30647                     (width < this.minHeight && height < this.minWidth)
30648                 )
30649         ){
30650             return false;
30651         }
30652         
30653         if(
30654                 !this.isDocument &&
30655                 (this.rotate == 0 || this.rotate == 180) && 
30656                 (
30657                     width < this.minWidth || 
30658                     width > this.imageEl.OriginWidth || 
30659                     height < this.minHeight || 
30660                     height > this.imageEl.OriginHeight
30661                 )
30662         ){
30663             return false;
30664         }
30665         
30666         if(
30667                 !this.isDocument &&
30668                 (this.rotate == 90 || this.rotate == 270) && 
30669                 (
30670                     width < this.minHeight || 
30671                     width > this.imageEl.OriginWidth || 
30672                     height < this.minWidth || 
30673                     height > this.imageEl.OriginHeight
30674                 )
30675         ){
30676             return false;
30677         }
30678         
30679         return true;
30680         
30681     },
30682     
30683     onRotateLeft : function(e)
30684     {   
30685         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30686             
30687             var minScale = this.thumbEl.getWidth() / this.minWidth;
30688             
30689             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30690             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30691             
30692             this.startScale = this.scale;
30693             
30694             while (this.getScaleLevel() < minScale){
30695             
30696                 this.scale = this.scale + 1;
30697                 
30698                 if(!this.zoomable()){
30699                     break;
30700                 }
30701                 
30702                 if(
30703                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30704                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30705                 ){
30706                     continue;
30707                 }
30708                 
30709                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30710
30711                 this.draw();
30712                 
30713                 return;
30714             }
30715             
30716             this.scale = this.startScale;
30717             
30718             this.onRotateFail();
30719             
30720             return false;
30721         }
30722         
30723         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30724
30725         if(this.isDocument){
30726             this.setThumbBoxSize();
30727             this.setThumbBoxPosition();
30728             this.setCanvasPosition();
30729         }
30730         
30731         this.draw();
30732         
30733         this.fireEvent('rotate', this, 'left');
30734         
30735     },
30736     
30737     onRotateRight : function(e)
30738     {
30739         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30740             
30741             var minScale = this.thumbEl.getWidth() / this.minWidth;
30742         
30743             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30744             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30745             
30746             this.startScale = this.scale;
30747             
30748             while (this.getScaleLevel() < minScale){
30749             
30750                 this.scale = this.scale + 1;
30751                 
30752                 if(!this.zoomable()){
30753                     break;
30754                 }
30755                 
30756                 if(
30757                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30758                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30759                 ){
30760                     continue;
30761                 }
30762                 
30763                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30764
30765                 this.draw();
30766                 
30767                 return;
30768             }
30769             
30770             this.scale = this.startScale;
30771             
30772             this.onRotateFail();
30773             
30774             return false;
30775         }
30776         
30777         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30778
30779         if(this.isDocument){
30780             this.setThumbBoxSize();
30781             this.setThumbBoxPosition();
30782             this.setCanvasPosition();
30783         }
30784         
30785         this.draw();
30786         
30787         this.fireEvent('rotate', this, 'right');
30788     },
30789     
30790     onRotateFail : function()
30791     {
30792         this.errorEl.show(true);
30793         
30794         var _this = this;
30795         
30796         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30797     },
30798     
30799     draw : function()
30800     {
30801         this.previewEl.dom.innerHTML = '';
30802         
30803         var canvasEl = document.createElement("canvas");
30804         
30805         var contextEl = canvasEl.getContext("2d");
30806         
30807         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30808         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30809         var center = this.imageEl.OriginWidth / 2;
30810         
30811         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30812             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30813             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30814             center = this.imageEl.OriginHeight / 2;
30815         }
30816         
30817         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30818         
30819         contextEl.translate(center, center);
30820         contextEl.rotate(this.rotate * Math.PI / 180);
30821
30822         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30823         
30824         this.canvasEl = document.createElement("canvas");
30825         
30826         this.contextEl = this.canvasEl.getContext("2d");
30827         
30828         switch (this.rotate) {
30829             case 0 :
30830                 
30831                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30832                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30833                 
30834                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30835                 
30836                 break;
30837             case 90 : 
30838                 
30839                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30840                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30841                 
30842                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30843                     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);
30844                     break;
30845                 }
30846                 
30847                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30848                 
30849                 break;
30850             case 180 :
30851                 
30852                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30853                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30854                 
30855                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30856                     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);
30857                     break;
30858                 }
30859                 
30860                 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);
30861                 
30862                 break;
30863             case 270 :
30864                 
30865                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30866                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30867         
30868                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30869                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30870                     break;
30871                 }
30872                 
30873                 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);
30874                 
30875                 break;
30876             default : 
30877                 break;
30878         }
30879         
30880         this.previewEl.appendChild(this.canvasEl);
30881         
30882         this.setCanvasPosition();
30883     },
30884     
30885     crop : function()
30886     {
30887         if(!this.canvasLoaded){
30888             return;
30889         }
30890         
30891         var imageCanvas = document.createElement("canvas");
30892         
30893         var imageContext = imageCanvas.getContext("2d");
30894         
30895         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30896         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30897         
30898         var center = imageCanvas.width / 2;
30899         
30900         imageContext.translate(center, center);
30901         
30902         imageContext.rotate(this.rotate * Math.PI / 180);
30903         
30904         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30905         
30906         var canvas = document.createElement("canvas");
30907         
30908         var context = canvas.getContext("2d");
30909                 
30910         canvas.width = this.minWidth;
30911         canvas.height = this.minHeight;
30912
30913         switch (this.rotate) {
30914             case 0 :
30915                 
30916                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30917                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30918                 
30919                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30920                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30921                 
30922                 var targetWidth = this.minWidth - 2 * x;
30923                 var targetHeight = this.minHeight - 2 * y;
30924                 
30925                 var scale = 1;
30926                 
30927                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30928                     scale = targetWidth / width;
30929                 }
30930                 
30931                 if(x > 0 && y == 0){
30932                     scale = targetHeight / height;
30933                 }
30934                 
30935                 if(x > 0 && y > 0){
30936                     scale = targetWidth / width;
30937                     
30938                     if(width < height){
30939                         scale = targetHeight / height;
30940                     }
30941                 }
30942                 
30943                 context.scale(scale, scale);
30944                 
30945                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30946                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30947
30948                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30949                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30950
30951                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30952                 
30953                 break;
30954             case 90 : 
30955                 
30956                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30957                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30958                 
30959                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30960                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30961                 
30962                 var targetWidth = this.minWidth - 2 * x;
30963                 var targetHeight = this.minHeight - 2 * y;
30964                 
30965                 var scale = 1;
30966                 
30967                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30968                     scale = targetWidth / width;
30969                 }
30970                 
30971                 if(x > 0 && y == 0){
30972                     scale = targetHeight / height;
30973                 }
30974                 
30975                 if(x > 0 && y > 0){
30976                     scale = targetWidth / width;
30977                     
30978                     if(width < height){
30979                         scale = targetHeight / height;
30980                     }
30981                 }
30982                 
30983                 context.scale(scale, scale);
30984                 
30985                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30986                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30987
30988                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30989                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30990                 
30991                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30992                 
30993                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30994                 
30995                 break;
30996             case 180 :
30997                 
30998                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30999                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
31000                 
31001                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31002                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31003                 
31004                 var targetWidth = this.minWidth - 2 * x;
31005                 var targetHeight = this.minHeight - 2 * y;
31006                 
31007                 var scale = 1;
31008                 
31009                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31010                     scale = targetWidth / width;
31011                 }
31012                 
31013                 if(x > 0 && y == 0){
31014                     scale = targetHeight / height;
31015                 }
31016                 
31017                 if(x > 0 && y > 0){
31018                     scale = targetWidth / width;
31019                     
31020                     if(width < height){
31021                         scale = targetHeight / height;
31022                     }
31023                 }
31024                 
31025                 context.scale(scale, scale);
31026                 
31027                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31028                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31029
31030                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31031                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31032
31033                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31034                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
31035                 
31036                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31037                 
31038                 break;
31039             case 270 :
31040                 
31041                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
31042                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
31043                 
31044                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
31045                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
31046                 
31047                 var targetWidth = this.minWidth - 2 * x;
31048                 var targetHeight = this.minHeight - 2 * y;
31049                 
31050                 var scale = 1;
31051                 
31052                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
31053                     scale = targetWidth / width;
31054                 }
31055                 
31056                 if(x > 0 && y == 0){
31057                     scale = targetHeight / height;
31058                 }
31059                 
31060                 if(x > 0 && y > 0){
31061                     scale = targetWidth / width;
31062                     
31063                     if(width < height){
31064                         scale = targetHeight / height;
31065                     }
31066                 }
31067                 
31068                 context.scale(scale, scale);
31069                 
31070                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31071                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31072
31073                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31074                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31075                 
31076                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31077                 
31078                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31079                 
31080                 break;
31081             default : 
31082                 break;
31083         }
31084         
31085         this.cropData = canvas.toDataURL(this.cropType);
31086         
31087         if(this.fireEvent('crop', this, this.cropData) !== false){
31088             this.process(this.file, this.cropData);
31089         }
31090         
31091         return;
31092         
31093     },
31094     
31095     setThumbBoxSize : function()
31096     {
31097         var width, height;
31098         
31099         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31100             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31101             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31102             
31103             this.minWidth = width;
31104             this.minHeight = height;
31105             
31106             if(this.rotate == 90 || this.rotate == 270){
31107                 this.minWidth = height;
31108                 this.minHeight = width;
31109             }
31110         }
31111         
31112         height = 300;
31113         width = Math.ceil(this.minWidth * height / this.minHeight);
31114         
31115         if(this.minWidth > this.minHeight){
31116             width = 300;
31117             height = Math.ceil(this.minHeight * width / this.minWidth);
31118         }
31119         
31120         this.thumbEl.setStyle({
31121             width : width + 'px',
31122             height : height + 'px'
31123         });
31124
31125         return;
31126             
31127     },
31128     
31129     setThumbBoxPosition : function()
31130     {
31131         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31132         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31133         
31134         this.thumbEl.setLeft(x);
31135         this.thumbEl.setTop(y);
31136         
31137     },
31138     
31139     baseRotateLevel : function()
31140     {
31141         this.baseRotate = 1;
31142         
31143         if(
31144                 typeof(this.exif) != 'undefined' &&
31145                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31146                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31147         ){
31148             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31149         }
31150         
31151         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31152         
31153     },
31154     
31155     baseScaleLevel : function()
31156     {
31157         var width, height;
31158         
31159         if(this.isDocument){
31160             
31161             if(this.baseRotate == 6 || this.baseRotate == 8){
31162             
31163                 height = this.thumbEl.getHeight();
31164                 this.baseScale = height / this.imageEl.OriginWidth;
31165
31166                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31167                     width = this.thumbEl.getWidth();
31168                     this.baseScale = width / this.imageEl.OriginHeight;
31169                 }
31170
31171                 return;
31172             }
31173
31174             height = this.thumbEl.getHeight();
31175             this.baseScale = height / this.imageEl.OriginHeight;
31176
31177             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31178                 width = this.thumbEl.getWidth();
31179                 this.baseScale = width / this.imageEl.OriginWidth;
31180             }
31181
31182             return;
31183         }
31184         
31185         if(this.baseRotate == 6 || this.baseRotate == 8){
31186             
31187             width = this.thumbEl.getHeight();
31188             this.baseScale = width / this.imageEl.OriginHeight;
31189             
31190             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31191                 height = this.thumbEl.getWidth();
31192                 this.baseScale = height / this.imageEl.OriginHeight;
31193             }
31194             
31195             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31196                 height = this.thumbEl.getWidth();
31197                 this.baseScale = height / this.imageEl.OriginHeight;
31198                 
31199                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31200                     width = this.thumbEl.getHeight();
31201                     this.baseScale = width / this.imageEl.OriginWidth;
31202                 }
31203             }
31204             
31205             return;
31206         }
31207         
31208         width = this.thumbEl.getWidth();
31209         this.baseScale = width / this.imageEl.OriginWidth;
31210         
31211         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31212             height = this.thumbEl.getHeight();
31213             this.baseScale = height / this.imageEl.OriginHeight;
31214         }
31215         
31216         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31217             
31218             height = this.thumbEl.getHeight();
31219             this.baseScale = height / this.imageEl.OriginHeight;
31220             
31221             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31222                 width = this.thumbEl.getWidth();
31223                 this.baseScale = width / this.imageEl.OriginWidth;
31224             }
31225             
31226         }
31227         
31228         return;
31229     },
31230     
31231     getScaleLevel : function()
31232     {
31233         return this.baseScale * Math.pow(1.1, this.scale);
31234     },
31235     
31236     onTouchStart : function(e)
31237     {
31238         if(!this.canvasLoaded){
31239             this.beforeSelectFile(e);
31240             return;
31241         }
31242         
31243         var touches = e.browserEvent.touches;
31244         
31245         if(!touches){
31246             return;
31247         }
31248         
31249         if(touches.length == 1){
31250             this.onMouseDown(e);
31251             return;
31252         }
31253         
31254         if(touches.length != 2){
31255             return;
31256         }
31257         
31258         var coords = [];
31259         
31260         for(var i = 0, finger; finger = touches[i]; i++){
31261             coords.push(finger.pageX, finger.pageY);
31262         }
31263         
31264         var x = Math.pow(coords[0] - coords[2], 2);
31265         var y = Math.pow(coords[1] - coords[3], 2);
31266         
31267         this.startDistance = Math.sqrt(x + y);
31268         
31269         this.startScale = this.scale;
31270         
31271         this.pinching = true;
31272         this.dragable = false;
31273         
31274     },
31275     
31276     onTouchMove : function(e)
31277     {
31278         if(!this.pinching && !this.dragable){
31279             return;
31280         }
31281         
31282         var touches = e.browserEvent.touches;
31283         
31284         if(!touches){
31285             return;
31286         }
31287         
31288         if(this.dragable){
31289             this.onMouseMove(e);
31290             return;
31291         }
31292         
31293         var coords = [];
31294         
31295         for(var i = 0, finger; finger = touches[i]; i++){
31296             coords.push(finger.pageX, finger.pageY);
31297         }
31298         
31299         var x = Math.pow(coords[0] - coords[2], 2);
31300         var y = Math.pow(coords[1] - coords[3], 2);
31301         
31302         this.endDistance = Math.sqrt(x + y);
31303         
31304         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31305         
31306         if(!this.zoomable()){
31307             this.scale = this.startScale;
31308             return;
31309         }
31310         
31311         this.draw();
31312         
31313     },
31314     
31315     onTouchEnd : function(e)
31316     {
31317         this.pinching = false;
31318         this.dragable = false;
31319         
31320     },
31321     
31322     process : function(file, crop)
31323     {
31324         if(this.loadMask){
31325             this.maskEl.mask(this.loadingText);
31326         }
31327         
31328         this.xhr = new XMLHttpRequest();
31329         
31330         file.xhr = this.xhr;
31331
31332         this.xhr.open(this.method, this.url, true);
31333         
31334         var headers = {
31335             "Accept": "application/json",
31336             "Cache-Control": "no-cache",
31337             "X-Requested-With": "XMLHttpRequest"
31338         };
31339         
31340         for (var headerName in headers) {
31341             var headerValue = headers[headerName];
31342             if (headerValue) {
31343                 this.xhr.setRequestHeader(headerName, headerValue);
31344             }
31345         }
31346         
31347         var _this = this;
31348         
31349         this.xhr.onload = function()
31350         {
31351             _this.xhrOnLoad(_this.xhr);
31352         }
31353         
31354         this.xhr.onerror = function()
31355         {
31356             _this.xhrOnError(_this.xhr);
31357         }
31358         
31359         var formData = new FormData();
31360
31361         formData.append('returnHTML', 'NO');
31362         
31363         if(crop){
31364             formData.append('crop', crop);
31365         }
31366         
31367         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31368             formData.append(this.paramName, file, file.name);
31369         }
31370         
31371         if(typeof(file.filename) != 'undefined'){
31372             formData.append('filename', file.filename);
31373         }
31374         
31375         if(typeof(file.mimetype) != 'undefined'){
31376             formData.append('mimetype', file.mimetype);
31377         }
31378         
31379         if(this.fireEvent('arrange', this, formData) != false){
31380             this.xhr.send(formData);
31381         };
31382     },
31383     
31384     xhrOnLoad : function(xhr)
31385     {
31386         if(this.loadMask){
31387             this.maskEl.unmask();
31388         }
31389         
31390         if (xhr.readyState !== 4) {
31391             this.fireEvent('exception', this, xhr);
31392             return;
31393         }
31394
31395         var response = Roo.decode(xhr.responseText);
31396         
31397         if(!response.success){
31398             this.fireEvent('exception', this, xhr);
31399             return;
31400         }
31401         
31402         var response = Roo.decode(xhr.responseText);
31403         
31404         this.fireEvent('upload', this, response);
31405         
31406     },
31407     
31408     xhrOnError : function()
31409     {
31410         if(this.loadMask){
31411             this.maskEl.unmask();
31412         }
31413         
31414         Roo.log('xhr on error');
31415         
31416         var response = Roo.decode(xhr.responseText);
31417           
31418         Roo.log(response);
31419         
31420     },
31421     
31422     prepare : function(file)
31423     {   
31424         if(this.loadMask){
31425             this.maskEl.mask(this.loadingText);
31426         }
31427         
31428         this.file = false;
31429         this.exif = {};
31430         
31431         if(typeof(file) === 'string'){
31432             this.loadCanvas(file);
31433             return;
31434         }
31435         
31436         if(!file || !this.urlAPI){
31437             return;
31438         }
31439         
31440         this.file = file;
31441         this.cropType = file.type;
31442         
31443         var _this = this;
31444         
31445         if(this.fireEvent('prepare', this, this.file) != false){
31446             
31447             var reader = new FileReader();
31448             
31449             reader.onload = function (e) {
31450                 if (e.target.error) {
31451                     Roo.log(e.target.error);
31452                     return;
31453                 }
31454                 
31455                 var buffer = e.target.result,
31456                     dataView = new DataView(buffer),
31457                     offset = 2,
31458                     maxOffset = dataView.byteLength - 4,
31459                     markerBytes,
31460                     markerLength;
31461                 
31462                 if (dataView.getUint16(0) === 0xffd8) {
31463                     while (offset < maxOffset) {
31464                         markerBytes = dataView.getUint16(offset);
31465                         
31466                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31467                             markerLength = dataView.getUint16(offset + 2) + 2;
31468                             if (offset + markerLength > dataView.byteLength) {
31469                                 Roo.log('Invalid meta data: Invalid segment size.');
31470                                 break;
31471                             }
31472                             
31473                             if(markerBytes == 0xffe1){
31474                                 _this.parseExifData(
31475                                     dataView,
31476                                     offset,
31477                                     markerLength
31478                                 );
31479                             }
31480                             
31481                             offset += markerLength;
31482                             
31483                             continue;
31484                         }
31485                         
31486                         break;
31487                     }
31488                     
31489                 }
31490                 
31491                 var url = _this.urlAPI.createObjectURL(_this.file);
31492                 
31493                 _this.loadCanvas(url);
31494                 
31495                 return;
31496             }
31497             
31498             reader.readAsArrayBuffer(this.file);
31499             
31500         }
31501         
31502     },
31503     
31504     parseExifData : function(dataView, offset, length)
31505     {
31506         var tiffOffset = offset + 10,
31507             littleEndian,
31508             dirOffset;
31509     
31510         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31511             // No Exif data, might be XMP data instead
31512             return;
31513         }
31514         
31515         // Check for the ASCII code for "Exif" (0x45786966):
31516         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31517             // No Exif data, might be XMP data instead
31518             return;
31519         }
31520         if (tiffOffset + 8 > dataView.byteLength) {
31521             Roo.log('Invalid Exif data: Invalid segment size.');
31522             return;
31523         }
31524         // Check for the two null bytes:
31525         if (dataView.getUint16(offset + 8) !== 0x0000) {
31526             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31527             return;
31528         }
31529         // Check the byte alignment:
31530         switch (dataView.getUint16(tiffOffset)) {
31531         case 0x4949:
31532             littleEndian = true;
31533             break;
31534         case 0x4D4D:
31535             littleEndian = false;
31536             break;
31537         default:
31538             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31539             return;
31540         }
31541         // Check for the TIFF tag marker (0x002A):
31542         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31543             Roo.log('Invalid Exif data: Missing TIFF marker.');
31544             return;
31545         }
31546         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31547         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31548         
31549         this.parseExifTags(
31550             dataView,
31551             tiffOffset,
31552             tiffOffset + dirOffset,
31553             littleEndian
31554         );
31555     },
31556     
31557     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31558     {
31559         var tagsNumber,
31560             dirEndOffset,
31561             i;
31562         if (dirOffset + 6 > dataView.byteLength) {
31563             Roo.log('Invalid Exif data: Invalid directory offset.');
31564             return;
31565         }
31566         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31567         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31568         if (dirEndOffset + 4 > dataView.byteLength) {
31569             Roo.log('Invalid Exif data: Invalid directory size.');
31570             return;
31571         }
31572         for (i = 0; i < tagsNumber; i += 1) {
31573             this.parseExifTag(
31574                 dataView,
31575                 tiffOffset,
31576                 dirOffset + 2 + 12 * i, // tag offset
31577                 littleEndian
31578             );
31579         }
31580         // Return the offset to the next directory:
31581         return dataView.getUint32(dirEndOffset, littleEndian);
31582     },
31583     
31584     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31585     {
31586         var tag = dataView.getUint16(offset, littleEndian);
31587         
31588         this.exif[tag] = this.getExifValue(
31589             dataView,
31590             tiffOffset,
31591             offset,
31592             dataView.getUint16(offset + 2, littleEndian), // tag type
31593             dataView.getUint32(offset + 4, littleEndian), // tag length
31594             littleEndian
31595         );
31596     },
31597     
31598     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31599     {
31600         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31601             tagSize,
31602             dataOffset,
31603             values,
31604             i,
31605             str,
31606             c;
31607     
31608         if (!tagType) {
31609             Roo.log('Invalid Exif data: Invalid tag type.');
31610             return;
31611         }
31612         
31613         tagSize = tagType.size * length;
31614         // Determine if the value is contained in the dataOffset bytes,
31615         // or if the value at the dataOffset is a pointer to the actual data:
31616         dataOffset = tagSize > 4 ?
31617                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31618         if (dataOffset + tagSize > dataView.byteLength) {
31619             Roo.log('Invalid Exif data: Invalid data offset.');
31620             return;
31621         }
31622         if (length === 1) {
31623             return tagType.getValue(dataView, dataOffset, littleEndian);
31624         }
31625         values = [];
31626         for (i = 0; i < length; i += 1) {
31627             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31628         }
31629         
31630         if (tagType.ascii) {
31631             str = '';
31632             // Concatenate the chars:
31633             for (i = 0; i < values.length; i += 1) {
31634                 c = values[i];
31635                 // Ignore the terminating NULL byte(s):
31636                 if (c === '\u0000') {
31637                     break;
31638                 }
31639                 str += c;
31640             }
31641             return str;
31642         }
31643         return values;
31644     }
31645     
31646 });
31647
31648 Roo.apply(Roo.bootstrap.UploadCropbox, {
31649     tags : {
31650         'Orientation': 0x0112
31651     },
31652     
31653     Orientation: {
31654             1: 0, //'top-left',
31655 //            2: 'top-right',
31656             3: 180, //'bottom-right',
31657 //            4: 'bottom-left',
31658 //            5: 'left-top',
31659             6: 90, //'right-top',
31660 //            7: 'right-bottom',
31661             8: 270 //'left-bottom'
31662     },
31663     
31664     exifTagTypes : {
31665         // byte, 8-bit unsigned int:
31666         1: {
31667             getValue: function (dataView, dataOffset) {
31668                 return dataView.getUint8(dataOffset);
31669             },
31670             size: 1
31671         },
31672         // ascii, 8-bit byte:
31673         2: {
31674             getValue: function (dataView, dataOffset) {
31675                 return String.fromCharCode(dataView.getUint8(dataOffset));
31676             },
31677             size: 1,
31678             ascii: true
31679         },
31680         // short, 16 bit int:
31681         3: {
31682             getValue: function (dataView, dataOffset, littleEndian) {
31683                 return dataView.getUint16(dataOffset, littleEndian);
31684             },
31685             size: 2
31686         },
31687         // long, 32 bit int:
31688         4: {
31689             getValue: function (dataView, dataOffset, littleEndian) {
31690                 return dataView.getUint32(dataOffset, littleEndian);
31691             },
31692             size: 4
31693         },
31694         // rational = two long values, first is numerator, second is denominator:
31695         5: {
31696             getValue: function (dataView, dataOffset, littleEndian) {
31697                 return dataView.getUint32(dataOffset, littleEndian) /
31698                     dataView.getUint32(dataOffset + 4, littleEndian);
31699             },
31700             size: 8
31701         },
31702         // slong, 32 bit signed int:
31703         9: {
31704             getValue: function (dataView, dataOffset, littleEndian) {
31705                 return dataView.getInt32(dataOffset, littleEndian);
31706             },
31707             size: 4
31708         },
31709         // srational, two slongs, first is numerator, second is denominator:
31710         10: {
31711             getValue: function (dataView, dataOffset, littleEndian) {
31712                 return dataView.getInt32(dataOffset, littleEndian) /
31713                     dataView.getInt32(dataOffset + 4, littleEndian);
31714             },
31715             size: 8
31716         }
31717     },
31718     
31719     footer : {
31720         STANDARD : [
31721             {
31722                 tag : 'div',
31723                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31724                 action : 'rotate-left',
31725                 cn : [
31726                     {
31727                         tag : 'button',
31728                         cls : 'btn btn-default',
31729                         html : '<i class="fa fa-undo"></i>'
31730                     }
31731                 ]
31732             },
31733             {
31734                 tag : 'div',
31735                 cls : 'btn-group roo-upload-cropbox-picture',
31736                 action : 'picture',
31737                 cn : [
31738                     {
31739                         tag : 'button',
31740                         cls : 'btn btn-default',
31741                         html : '<i class="fa fa-picture-o"></i>'
31742                     }
31743                 ]
31744             },
31745             {
31746                 tag : 'div',
31747                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31748                 action : 'rotate-right',
31749                 cn : [
31750                     {
31751                         tag : 'button',
31752                         cls : 'btn btn-default',
31753                         html : '<i class="fa fa-repeat"></i>'
31754                     }
31755                 ]
31756             }
31757         ],
31758         DOCUMENT : [
31759             {
31760                 tag : 'div',
31761                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31762                 action : 'rotate-left',
31763                 cn : [
31764                     {
31765                         tag : 'button',
31766                         cls : 'btn btn-default',
31767                         html : '<i class="fa fa-undo"></i>'
31768                     }
31769                 ]
31770             },
31771             {
31772                 tag : 'div',
31773                 cls : 'btn-group roo-upload-cropbox-download',
31774                 action : 'download',
31775                 cn : [
31776                     {
31777                         tag : 'button',
31778                         cls : 'btn btn-default',
31779                         html : '<i class="fa fa-download"></i>'
31780                     }
31781                 ]
31782             },
31783             {
31784                 tag : 'div',
31785                 cls : 'btn-group roo-upload-cropbox-crop',
31786                 action : 'crop',
31787                 cn : [
31788                     {
31789                         tag : 'button',
31790                         cls : 'btn btn-default',
31791                         html : '<i class="fa fa-crop"></i>'
31792                     }
31793                 ]
31794             },
31795             {
31796                 tag : 'div',
31797                 cls : 'btn-group roo-upload-cropbox-trash',
31798                 action : 'trash',
31799                 cn : [
31800                     {
31801                         tag : 'button',
31802                         cls : 'btn btn-default',
31803                         html : '<i class="fa fa-trash"></i>'
31804                     }
31805                 ]
31806             },
31807             {
31808                 tag : 'div',
31809                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31810                 action : 'rotate-right',
31811                 cn : [
31812                     {
31813                         tag : 'button',
31814                         cls : 'btn btn-default',
31815                         html : '<i class="fa fa-repeat"></i>'
31816                     }
31817                 ]
31818             }
31819         ],
31820         ROTATOR : [
31821             {
31822                 tag : 'div',
31823                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31824                 action : 'rotate-left',
31825                 cn : [
31826                     {
31827                         tag : 'button',
31828                         cls : 'btn btn-default',
31829                         html : '<i class="fa fa-undo"></i>'
31830                     }
31831                 ]
31832             },
31833             {
31834                 tag : 'div',
31835                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31836                 action : 'rotate-right',
31837                 cn : [
31838                     {
31839                         tag : 'button',
31840                         cls : 'btn btn-default',
31841                         html : '<i class="fa fa-repeat"></i>'
31842                     }
31843                 ]
31844             }
31845         ]
31846     }
31847 });
31848
31849 /*
31850 * Licence: LGPL
31851 */
31852
31853 /**
31854  * @class Roo.bootstrap.DocumentManager
31855  * @extends Roo.bootstrap.Component
31856  * Bootstrap DocumentManager class
31857  * @cfg {String} paramName default 'imageUpload'
31858  * @cfg {String} toolTipName default 'filename'
31859  * @cfg {String} method default POST
31860  * @cfg {String} url action url
31861  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31862  * @cfg {Boolean} multiple multiple upload default true
31863  * @cfg {Number} thumbSize default 300
31864  * @cfg {String} fieldLabel
31865  * @cfg {Number} labelWidth default 4
31866  * @cfg {String} labelAlign (left|top) default left
31867  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31868 * @cfg {Number} labellg set the width of label (1-12)
31869  * @cfg {Number} labelmd set the width of label (1-12)
31870  * @cfg {Number} labelsm set the width of label (1-12)
31871  * @cfg {Number} labelxs set the width of label (1-12)
31872  * 
31873  * @constructor
31874  * Create a new DocumentManager
31875  * @param {Object} config The config object
31876  */
31877
31878 Roo.bootstrap.DocumentManager = function(config){
31879     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31880     
31881     this.files = [];
31882     this.delegates = [];
31883     
31884     this.addEvents({
31885         /**
31886          * @event initial
31887          * Fire when initial the DocumentManager
31888          * @param {Roo.bootstrap.DocumentManager} this
31889          */
31890         "initial" : true,
31891         /**
31892          * @event inspect
31893          * inspect selected file
31894          * @param {Roo.bootstrap.DocumentManager} this
31895          * @param {File} file
31896          */
31897         "inspect" : true,
31898         /**
31899          * @event exception
31900          * Fire when xhr load exception
31901          * @param {Roo.bootstrap.DocumentManager} this
31902          * @param {XMLHttpRequest} xhr
31903          */
31904         "exception" : true,
31905         /**
31906          * @event afterupload
31907          * Fire when xhr load exception
31908          * @param {Roo.bootstrap.DocumentManager} this
31909          * @param {XMLHttpRequest} xhr
31910          */
31911         "afterupload" : true,
31912         /**
31913          * @event prepare
31914          * prepare the form data
31915          * @param {Roo.bootstrap.DocumentManager} this
31916          * @param {Object} formData
31917          */
31918         "prepare" : true,
31919         /**
31920          * @event remove
31921          * Fire when remove the file
31922          * @param {Roo.bootstrap.DocumentManager} this
31923          * @param {Object} file
31924          */
31925         "remove" : true,
31926         /**
31927          * @event refresh
31928          * Fire after refresh the file
31929          * @param {Roo.bootstrap.DocumentManager} this
31930          */
31931         "refresh" : true,
31932         /**
31933          * @event click
31934          * Fire after click the image
31935          * @param {Roo.bootstrap.DocumentManager} this
31936          * @param {Object} file
31937          */
31938         "click" : true,
31939         /**
31940          * @event edit
31941          * Fire when upload a image and editable set to true
31942          * @param {Roo.bootstrap.DocumentManager} this
31943          * @param {Object} file
31944          */
31945         "edit" : true,
31946         /**
31947          * @event beforeselectfile
31948          * Fire before select file
31949          * @param {Roo.bootstrap.DocumentManager} this
31950          */
31951         "beforeselectfile" : true,
31952         /**
31953          * @event process
31954          * Fire before process file
31955          * @param {Roo.bootstrap.DocumentManager} this
31956          * @param {Object} file
31957          */
31958         "process" : true,
31959         /**
31960          * @event previewrendered
31961          * Fire when preview rendered
31962          * @param {Roo.bootstrap.DocumentManager} this
31963          * @param {Object} file
31964          */
31965         "previewrendered" : true,
31966         /**
31967          */
31968         "previewResize" : true
31969         
31970     });
31971 };
31972
31973 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31974     
31975     boxes : 0,
31976     inputName : '',
31977     thumbSize : 300,
31978     multiple : true,
31979     files : false,
31980     method : 'POST',
31981     url : '',
31982     paramName : 'imageUpload',
31983     toolTipName : 'filename',
31984     fieldLabel : '',
31985     labelWidth : 4,
31986     labelAlign : 'left',
31987     editable : true,
31988     delegates : false,
31989     xhr : false, 
31990     
31991     labellg : 0,
31992     labelmd : 0,
31993     labelsm : 0,
31994     labelxs : 0,
31995     
31996     getAutoCreate : function()
31997     {   
31998         var managerWidget = {
31999             tag : 'div',
32000             cls : 'roo-document-manager',
32001             cn : [
32002                 {
32003                     tag : 'input',
32004                     cls : 'roo-document-manager-selector',
32005                     type : 'file'
32006                 },
32007                 {
32008                     tag : 'div',
32009                     cls : 'roo-document-manager-uploader',
32010                     cn : [
32011                         {
32012                             tag : 'div',
32013                             cls : 'roo-document-manager-upload-btn',
32014                             html : '<i class="fa fa-plus"></i>'
32015                         }
32016                     ]
32017                     
32018                 }
32019             ]
32020         };
32021         
32022         var content = [
32023             {
32024                 tag : 'div',
32025                 cls : 'column col-md-12',
32026                 cn : managerWidget
32027             }
32028         ];
32029         
32030         if(this.fieldLabel.length){
32031             
32032             content = [
32033                 {
32034                     tag : 'div',
32035                     cls : 'column col-md-12',
32036                     html : this.fieldLabel
32037                 },
32038                 {
32039                     tag : 'div',
32040                     cls : 'column col-md-12',
32041                     cn : managerWidget
32042                 }
32043             ];
32044
32045             if(this.labelAlign == 'left'){
32046                 content = [
32047                     {
32048                         tag : 'div',
32049                         cls : 'column',
32050                         html : this.fieldLabel
32051                     },
32052                     {
32053                         tag : 'div',
32054                         cls : 'column',
32055                         cn : managerWidget
32056                     }
32057                 ];
32058                 
32059                 if(this.labelWidth > 12){
32060                     content[0].style = "width: " + this.labelWidth + 'px';
32061                 }
32062
32063                 if(this.labelWidth < 13 && this.labelmd == 0){
32064                     this.labelmd = this.labelWidth;
32065                 }
32066
32067                 if(this.labellg > 0){
32068                     content[0].cls += ' col-lg-' + this.labellg;
32069                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32070                 }
32071
32072                 if(this.labelmd > 0){
32073                     content[0].cls += ' col-md-' + this.labelmd;
32074                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32075                 }
32076
32077                 if(this.labelsm > 0){
32078                     content[0].cls += ' col-sm-' + this.labelsm;
32079                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32080                 }
32081
32082                 if(this.labelxs > 0){
32083                     content[0].cls += ' col-xs-' + this.labelxs;
32084                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32085                 }
32086                 
32087             }
32088         }
32089         
32090         var cfg = {
32091             tag : 'div',
32092             cls : 'row clearfix',
32093             cn : content
32094         };
32095         
32096         return cfg;
32097         
32098     },
32099     
32100     initEvents : function()
32101     {
32102         this.managerEl = this.el.select('.roo-document-manager', true).first();
32103         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32104         
32105         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32106         this.selectorEl.hide();
32107         
32108         if(this.multiple){
32109             this.selectorEl.attr('multiple', 'multiple');
32110         }
32111         
32112         this.selectorEl.on('change', this.onFileSelected, this);
32113         
32114         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32115         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32116         
32117         this.uploader.on('click', this.onUploaderClick, this);
32118         
32119         this.renderProgressDialog();
32120         
32121         var _this = this;
32122         
32123         window.addEventListener("resize", function() { _this.refresh(); } );
32124         
32125         this.fireEvent('initial', this);
32126     },
32127     
32128     renderProgressDialog : function()
32129     {
32130         var _this = this;
32131         
32132         this.progressDialog = new Roo.bootstrap.Modal({
32133             cls : 'roo-document-manager-progress-dialog',
32134             allow_close : false,
32135             animate : false,
32136             title : '',
32137             buttons : [
32138                 {
32139                     name  :'cancel',
32140                     weight : 'danger',
32141                     html : 'Cancel'
32142                 }
32143             ], 
32144             listeners : { 
32145                 btnclick : function() {
32146                     _this.uploadCancel();
32147                     this.hide();
32148                 }
32149             }
32150         });
32151          
32152         this.progressDialog.render(Roo.get(document.body));
32153          
32154         this.progress = new Roo.bootstrap.Progress({
32155             cls : 'roo-document-manager-progress',
32156             active : true,
32157             striped : true
32158         });
32159         
32160         this.progress.render(this.progressDialog.getChildContainer());
32161         
32162         this.progressBar = new Roo.bootstrap.ProgressBar({
32163             cls : 'roo-document-manager-progress-bar',
32164             aria_valuenow : 0,
32165             aria_valuemin : 0,
32166             aria_valuemax : 12,
32167             panel : 'success'
32168         });
32169         
32170         this.progressBar.render(this.progress.getChildContainer());
32171     },
32172     
32173     onUploaderClick : function(e)
32174     {
32175         e.preventDefault();
32176      
32177         if(this.fireEvent('beforeselectfile', this) != false){
32178             this.selectorEl.dom.click();
32179         }
32180         
32181     },
32182     
32183     onFileSelected : function(e)
32184     {
32185         e.preventDefault();
32186         
32187         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32188             return;
32189         }
32190         
32191         Roo.each(this.selectorEl.dom.files, function(file){
32192             if(this.fireEvent('inspect', this, file) != false){
32193                 this.files.push(file);
32194             }
32195         }, this);
32196         
32197         this.queue();
32198         
32199     },
32200     
32201     queue : function()
32202     {
32203         this.selectorEl.dom.value = '';
32204         
32205         if(!this.files || !this.files.length){
32206             return;
32207         }
32208         
32209         if(this.boxes > 0 && this.files.length > this.boxes){
32210             this.files = this.files.slice(0, this.boxes);
32211         }
32212         
32213         this.uploader.show();
32214         
32215         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32216             this.uploader.hide();
32217         }
32218         
32219         var _this = this;
32220         
32221         var files = [];
32222         
32223         var docs = [];
32224         
32225         Roo.each(this.files, function(file){
32226             
32227             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32228                 var f = this.renderPreview(file);
32229                 files.push(f);
32230                 return;
32231             }
32232             
32233             if(file.type.indexOf('image') != -1){
32234                 this.delegates.push(
32235                     (function(){
32236                         _this.process(file);
32237                     }).createDelegate(this)
32238                 );
32239         
32240                 return;
32241             }
32242             
32243             docs.push(
32244                 (function(){
32245                     _this.process(file);
32246                 }).createDelegate(this)
32247             );
32248             
32249         }, this);
32250         
32251         this.files = files;
32252         
32253         this.delegates = this.delegates.concat(docs);
32254         
32255         if(!this.delegates.length){
32256             this.refresh();
32257             return;
32258         }
32259         
32260         this.progressBar.aria_valuemax = this.delegates.length;
32261         
32262         this.arrange();
32263         
32264         return;
32265     },
32266     
32267     arrange : function()
32268     {
32269         if(!this.delegates.length){
32270             this.progressDialog.hide();
32271             this.refresh();
32272             return;
32273         }
32274         
32275         var delegate = this.delegates.shift();
32276         
32277         this.progressDialog.show();
32278         
32279         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32280         
32281         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32282         
32283         delegate();
32284     },
32285     
32286     refresh : function()
32287     {
32288         this.uploader.show();
32289         
32290         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32291             this.uploader.hide();
32292         }
32293         
32294         Roo.isTouch ? this.closable(false) : this.closable(true);
32295         
32296         this.fireEvent('refresh', this);
32297     },
32298     
32299     onRemove : function(e, el, o)
32300     {
32301         e.preventDefault();
32302         
32303         this.fireEvent('remove', this, o);
32304         
32305     },
32306     
32307     remove : function(o)
32308     {
32309         var files = [];
32310         
32311         Roo.each(this.files, function(file){
32312             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32313                 files.push(file);
32314                 return;
32315             }
32316
32317             o.target.remove();
32318
32319         }, this);
32320         
32321         this.files = files;
32322         
32323         this.refresh();
32324     },
32325     
32326     clear : function()
32327     {
32328         Roo.each(this.files, function(file){
32329             if(!file.target){
32330                 return;
32331             }
32332             
32333             file.target.remove();
32334
32335         }, this);
32336         
32337         this.files = [];
32338         
32339         this.refresh();
32340     },
32341     
32342     onClick : function(e, el, o)
32343     {
32344         e.preventDefault();
32345         
32346         this.fireEvent('click', this, o);
32347         
32348     },
32349     
32350     closable : function(closable)
32351     {
32352         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32353             
32354             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32355             
32356             if(closable){
32357                 el.show();
32358                 return;
32359             }
32360             
32361             el.hide();
32362             
32363         }, this);
32364     },
32365     
32366     xhrOnLoad : function(xhr)
32367     {
32368         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32369             el.remove();
32370         }, this);
32371         
32372         if (xhr.readyState !== 4) {
32373             this.arrange();
32374             this.fireEvent('exception', this, xhr);
32375             return;
32376         }
32377
32378         var response = Roo.decode(xhr.responseText);
32379         
32380         if(!response.success){
32381             this.arrange();
32382             this.fireEvent('exception', this, xhr);
32383             return;
32384         }
32385         
32386         var file = this.renderPreview(response.data);
32387         
32388         this.files.push(file);
32389         
32390         this.arrange();
32391         
32392         this.fireEvent('afterupload', this, xhr);
32393         
32394     },
32395     
32396     xhrOnError : function(xhr)
32397     {
32398         Roo.log('xhr on error');
32399         
32400         var response = Roo.decode(xhr.responseText);
32401           
32402         Roo.log(response);
32403         
32404         this.arrange();
32405     },
32406     
32407     process : function(file)
32408     {
32409         if(this.fireEvent('process', this, file) !== false){
32410             if(this.editable && file.type.indexOf('image') != -1){
32411                 this.fireEvent('edit', this, file);
32412                 return;
32413             }
32414
32415             this.uploadStart(file, false);
32416
32417             return;
32418         }
32419         
32420     },
32421     
32422     uploadStart : function(file, crop)
32423     {
32424         this.xhr = new XMLHttpRequest();
32425         
32426         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32427             this.arrange();
32428             return;
32429         }
32430         
32431         file.xhr = this.xhr;
32432             
32433         this.managerEl.createChild({
32434             tag : 'div',
32435             cls : 'roo-document-manager-loading',
32436             cn : [
32437                 {
32438                     tag : 'div',
32439                     tooltip : file.name,
32440                     cls : 'roo-document-manager-thumb',
32441                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32442                 }
32443             ]
32444
32445         });
32446
32447         this.xhr.open(this.method, this.url, true);
32448         
32449         var headers = {
32450             "Accept": "application/json",
32451             "Cache-Control": "no-cache",
32452             "X-Requested-With": "XMLHttpRequest"
32453         };
32454         
32455         for (var headerName in headers) {
32456             var headerValue = headers[headerName];
32457             if (headerValue) {
32458                 this.xhr.setRequestHeader(headerName, headerValue);
32459             }
32460         }
32461         
32462         var _this = this;
32463         
32464         this.xhr.onload = function()
32465         {
32466             _this.xhrOnLoad(_this.xhr);
32467         }
32468         
32469         this.xhr.onerror = function()
32470         {
32471             _this.xhrOnError(_this.xhr);
32472         }
32473         
32474         var formData = new FormData();
32475
32476         formData.append('returnHTML', 'NO');
32477         
32478         if(crop){
32479             formData.append('crop', crop);
32480         }
32481         
32482         formData.append(this.paramName, file, file.name);
32483         
32484         var options = {
32485             file : file, 
32486             manually : false
32487         };
32488         
32489         if(this.fireEvent('prepare', this, formData, options) != false){
32490             
32491             if(options.manually){
32492                 return;
32493             }
32494             
32495             this.xhr.send(formData);
32496             return;
32497         };
32498         
32499         this.uploadCancel();
32500     },
32501     
32502     uploadCancel : function()
32503     {
32504         if (this.xhr) {
32505             this.xhr.abort();
32506         }
32507         
32508         this.delegates = [];
32509         
32510         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32511             el.remove();
32512         }, this);
32513         
32514         this.arrange();
32515     },
32516     
32517     renderPreview : function(file)
32518     {
32519         if(typeof(file.target) != 'undefined' && file.target){
32520             return file;
32521         }
32522         
32523         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32524         
32525         var previewEl = this.managerEl.createChild({
32526             tag : 'div',
32527             cls : 'roo-document-manager-preview',
32528             cn : [
32529                 {
32530                     tag : 'div',
32531                     tooltip : file[this.toolTipName],
32532                     cls : 'roo-document-manager-thumb',
32533                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32534                 },
32535                 {
32536                     tag : 'button',
32537                     cls : 'close',
32538                     html : '<i class="fa fa-times-circle"></i>'
32539                 }
32540             ]
32541         });
32542
32543         var close = previewEl.select('button.close', true).first();
32544
32545         close.on('click', this.onRemove, this, file);
32546
32547         file.target = previewEl;
32548
32549         var image = previewEl.select('img', true).first();
32550         
32551         var _this = this;
32552         
32553         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32554         
32555         image.on('click', this.onClick, this, file);
32556         
32557         this.fireEvent('previewrendered', this, file);
32558         
32559         return file;
32560         
32561     },
32562     
32563     onPreviewLoad : function(file, image)
32564     {
32565         if(typeof(file.target) == 'undefined' || !file.target){
32566             return;
32567         }
32568         
32569         var width = image.dom.naturalWidth || image.dom.width;
32570         var height = image.dom.naturalHeight || image.dom.height;
32571         
32572         if(!this.previewResize) {
32573             return;
32574         }
32575         
32576         if(width > height){
32577             file.target.addClass('wide');
32578             return;
32579         }
32580         
32581         file.target.addClass('tall');
32582         return;
32583         
32584     },
32585     
32586     uploadFromSource : function(file, crop)
32587     {
32588         this.xhr = new XMLHttpRequest();
32589         
32590         this.managerEl.createChild({
32591             tag : 'div',
32592             cls : 'roo-document-manager-loading',
32593             cn : [
32594                 {
32595                     tag : 'div',
32596                     tooltip : file.name,
32597                     cls : 'roo-document-manager-thumb',
32598                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32599                 }
32600             ]
32601
32602         });
32603
32604         this.xhr.open(this.method, this.url, true);
32605         
32606         var headers = {
32607             "Accept": "application/json",
32608             "Cache-Control": "no-cache",
32609             "X-Requested-With": "XMLHttpRequest"
32610         };
32611         
32612         for (var headerName in headers) {
32613             var headerValue = headers[headerName];
32614             if (headerValue) {
32615                 this.xhr.setRequestHeader(headerName, headerValue);
32616             }
32617         }
32618         
32619         var _this = this;
32620         
32621         this.xhr.onload = function()
32622         {
32623             _this.xhrOnLoad(_this.xhr);
32624         }
32625         
32626         this.xhr.onerror = function()
32627         {
32628             _this.xhrOnError(_this.xhr);
32629         }
32630         
32631         var formData = new FormData();
32632
32633         formData.append('returnHTML', 'NO');
32634         
32635         formData.append('crop', crop);
32636         
32637         if(typeof(file.filename) != 'undefined'){
32638             formData.append('filename', file.filename);
32639         }
32640         
32641         if(typeof(file.mimetype) != 'undefined'){
32642             formData.append('mimetype', file.mimetype);
32643         }
32644         
32645         Roo.log(formData);
32646         
32647         if(this.fireEvent('prepare', this, formData) != false){
32648             this.xhr.send(formData);
32649         };
32650     }
32651 });
32652
32653 /*
32654 * Licence: LGPL
32655 */
32656
32657 /**
32658  * @class Roo.bootstrap.DocumentViewer
32659  * @extends Roo.bootstrap.Component
32660  * Bootstrap DocumentViewer class
32661  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32662  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32663  * 
32664  * @constructor
32665  * Create a new DocumentViewer
32666  * @param {Object} config The config object
32667  */
32668
32669 Roo.bootstrap.DocumentViewer = function(config){
32670     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32671     
32672     this.addEvents({
32673         /**
32674          * @event initial
32675          * Fire after initEvent
32676          * @param {Roo.bootstrap.DocumentViewer} this
32677          */
32678         "initial" : true,
32679         /**
32680          * @event click
32681          * Fire after click
32682          * @param {Roo.bootstrap.DocumentViewer} this
32683          */
32684         "click" : true,
32685         /**
32686          * @event download
32687          * Fire after download button
32688          * @param {Roo.bootstrap.DocumentViewer} this
32689          */
32690         "download" : true,
32691         /**
32692          * @event trash
32693          * Fire after trash button
32694          * @param {Roo.bootstrap.DocumentViewer} this
32695          */
32696         "trash" : true
32697         
32698     });
32699 };
32700
32701 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32702     
32703     showDownload : true,
32704     
32705     showTrash : true,
32706     
32707     getAutoCreate : function()
32708     {
32709         var cfg = {
32710             tag : 'div',
32711             cls : 'roo-document-viewer',
32712             cn : [
32713                 {
32714                     tag : 'div',
32715                     cls : 'roo-document-viewer-body',
32716                     cn : [
32717                         {
32718                             tag : 'div',
32719                             cls : 'roo-document-viewer-thumb',
32720                             cn : [
32721                                 {
32722                                     tag : 'img',
32723                                     cls : 'roo-document-viewer-image'
32724                                 }
32725                             ]
32726                         }
32727                     ]
32728                 },
32729                 {
32730                     tag : 'div',
32731                     cls : 'roo-document-viewer-footer',
32732                     cn : {
32733                         tag : 'div',
32734                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32735                         cn : [
32736                             {
32737                                 tag : 'div',
32738                                 cls : 'btn-group roo-document-viewer-download',
32739                                 cn : [
32740                                     {
32741                                         tag : 'button',
32742                                         cls : 'btn btn-default',
32743                                         html : '<i class="fa fa-download"></i>'
32744                                     }
32745                                 ]
32746                             },
32747                             {
32748                                 tag : 'div',
32749                                 cls : 'btn-group roo-document-viewer-trash',
32750                                 cn : [
32751                                     {
32752                                         tag : 'button',
32753                                         cls : 'btn btn-default',
32754                                         html : '<i class="fa fa-trash"></i>'
32755                                     }
32756                                 ]
32757                             }
32758                         ]
32759                     }
32760                 }
32761             ]
32762         };
32763         
32764         return cfg;
32765     },
32766     
32767     initEvents : function()
32768     {
32769         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32770         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32771         
32772         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32773         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32774         
32775         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32776         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32777         
32778         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32779         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32780         
32781         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32782         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32783         
32784         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32785         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32786         
32787         this.bodyEl.on('click', this.onClick, this);
32788         this.downloadBtn.on('click', this.onDownload, this);
32789         this.trashBtn.on('click', this.onTrash, this);
32790         
32791         this.downloadBtn.hide();
32792         this.trashBtn.hide();
32793         
32794         if(this.showDownload){
32795             this.downloadBtn.show();
32796         }
32797         
32798         if(this.showTrash){
32799             this.trashBtn.show();
32800         }
32801         
32802         if(!this.showDownload && !this.showTrash) {
32803             this.footerEl.hide();
32804         }
32805         
32806     },
32807     
32808     initial : function()
32809     {
32810         this.fireEvent('initial', this);
32811         
32812     },
32813     
32814     onClick : function(e)
32815     {
32816         e.preventDefault();
32817         
32818         this.fireEvent('click', this);
32819     },
32820     
32821     onDownload : function(e)
32822     {
32823         e.preventDefault();
32824         
32825         this.fireEvent('download', this);
32826     },
32827     
32828     onTrash : function(e)
32829     {
32830         e.preventDefault();
32831         
32832         this.fireEvent('trash', this);
32833     }
32834     
32835 });
32836 /*
32837  * - LGPL
32838  *
32839  * nav progress bar
32840  * 
32841  */
32842
32843 /**
32844  * @class Roo.bootstrap.NavProgressBar
32845  * @extends Roo.bootstrap.Component
32846  * Bootstrap NavProgressBar class
32847  * 
32848  * @constructor
32849  * Create a new nav progress bar
32850  * @param {Object} config The config object
32851  */
32852
32853 Roo.bootstrap.NavProgressBar = function(config){
32854     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32855
32856     this.bullets = this.bullets || [];
32857    
32858 //    Roo.bootstrap.NavProgressBar.register(this);
32859      this.addEvents({
32860         /**
32861              * @event changed
32862              * Fires when the active item changes
32863              * @param {Roo.bootstrap.NavProgressBar} this
32864              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32865              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32866          */
32867         'changed': true
32868      });
32869     
32870 };
32871
32872 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32873     
32874     bullets : [],
32875     barItems : [],
32876     
32877     getAutoCreate : function()
32878     {
32879         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32880         
32881         cfg = {
32882             tag : 'div',
32883             cls : 'roo-navigation-bar-group',
32884             cn : [
32885                 {
32886                     tag : 'div',
32887                     cls : 'roo-navigation-top-bar'
32888                 },
32889                 {
32890                     tag : 'div',
32891                     cls : 'roo-navigation-bullets-bar',
32892                     cn : [
32893                         {
32894                             tag : 'ul',
32895                             cls : 'roo-navigation-bar'
32896                         }
32897                     ]
32898                 },
32899                 
32900                 {
32901                     tag : 'div',
32902                     cls : 'roo-navigation-bottom-bar'
32903                 }
32904             ]
32905             
32906         };
32907         
32908         return cfg;
32909         
32910     },
32911     
32912     initEvents: function() 
32913     {
32914         
32915     },
32916     
32917     onRender : function(ct, position) 
32918     {
32919         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32920         
32921         if(this.bullets.length){
32922             Roo.each(this.bullets, function(b){
32923                this.addItem(b);
32924             }, this);
32925         }
32926         
32927         this.format();
32928         
32929     },
32930     
32931     addItem : function(cfg)
32932     {
32933         var item = new Roo.bootstrap.NavProgressItem(cfg);
32934         
32935         item.parentId = this.id;
32936         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32937         
32938         if(cfg.html){
32939             var top = new Roo.bootstrap.Element({
32940                 tag : 'div',
32941                 cls : 'roo-navigation-bar-text'
32942             });
32943             
32944             var bottom = new Roo.bootstrap.Element({
32945                 tag : 'div',
32946                 cls : 'roo-navigation-bar-text'
32947             });
32948             
32949             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32950             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32951             
32952             var topText = new Roo.bootstrap.Element({
32953                 tag : 'span',
32954                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32955             });
32956             
32957             var bottomText = new Roo.bootstrap.Element({
32958                 tag : 'span',
32959                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32960             });
32961             
32962             topText.onRender(top.el, null);
32963             bottomText.onRender(bottom.el, null);
32964             
32965             item.topEl = top;
32966             item.bottomEl = bottom;
32967         }
32968         
32969         this.barItems.push(item);
32970         
32971         return item;
32972     },
32973     
32974     getActive : function()
32975     {
32976         var active = false;
32977         
32978         Roo.each(this.barItems, function(v){
32979             
32980             if (!v.isActive()) {
32981                 return;
32982             }
32983             
32984             active = v;
32985             return false;
32986             
32987         });
32988         
32989         return active;
32990     },
32991     
32992     setActiveItem : function(item)
32993     {
32994         var prev = false;
32995         
32996         Roo.each(this.barItems, function(v){
32997             if (v.rid == item.rid) {
32998                 return ;
32999             }
33000             
33001             if (v.isActive()) {
33002                 v.setActive(false);
33003                 prev = v;
33004             }
33005         });
33006
33007         item.setActive(true);
33008         
33009         this.fireEvent('changed', this, item, prev);
33010     },
33011     
33012     getBarItem: function(rid)
33013     {
33014         var ret = false;
33015         
33016         Roo.each(this.barItems, function(e) {
33017             if (e.rid != rid) {
33018                 return;
33019             }
33020             
33021             ret =  e;
33022             return false;
33023         });
33024         
33025         return ret;
33026     },
33027     
33028     indexOfItem : function(item)
33029     {
33030         var index = false;
33031         
33032         Roo.each(this.barItems, function(v, i){
33033             
33034             if (v.rid != item.rid) {
33035                 return;
33036             }
33037             
33038             index = i;
33039             return false
33040         });
33041         
33042         return index;
33043     },
33044     
33045     setActiveNext : function()
33046     {
33047         var i = this.indexOfItem(this.getActive());
33048         
33049         if (i > this.barItems.length) {
33050             return;
33051         }
33052         
33053         this.setActiveItem(this.barItems[i+1]);
33054     },
33055     
33056     setActivePrev : function()
33057     {
33058         var i = this.indexOfItem(this.getActive());
33059         
33060         if (i  < 1) {
33061             return;
33062         }
33063         
33064         this.setActiveItem(this.barItems[i-1]);
33065     },
33066     
33067     format : function()
33068     {
33069         if(!this.barItems.length){
33070             return;
33071         }
33072      
33073         var width = 100 / this.barItems.length;
33074         
33075         Roo.each(this.barItems, function(i){
33076             i.el.setStyle('width', width + '%');
33077             i.topEl.el.setStyle('width', width + '%');
33078             i.bottomEl.el.setStyle('width', width + '%');
33079         }, this);
33080         
33081     }
33082     
33083 });
33084 /*
33085  * - LGPL
33086  *
33087  * Nav Progress Item
33088  * 
33089  */
33090
33091 /**
33092  * @class Roo.bootstrap.NavProgressItem
33093  * @extends Roo.bootstrap.Component
33094  * Bootstrap NavProgressItem class
33095  * @cfg {String} rid the reference id
33096  * @cfg {Boolean} active (true|false) Is item active default false
33097  * @cfg {Boolean} disabled (true|false) Is item active default false
33098  * @cfg {String} html
33099  * @cfg {String} position (top|bottom) text position default bottom
33100  * @cfg {String} icon show icon instead of number
33101  * 
33102  * @constructor
33103  * Create a new NavProgressItem
33104  * @param {Object} config The config object
33105  */
33106 Roo.bootstrap.NavProgressItem = function(config){
33107     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33108     this.addEvents({
33109         // raw events
33110         /**
33111          * @event click
33112          * The raw click event for the entire grid.
33113          * @param {Roo.bootstrap.NavProgressItem} this
33114          * @param {Roo.EventObject} e
33115          */
33116         "click" : true
33117     });
33118    
33119 };
33120
33121 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33122     
33123     rid : '',
33124     active : false,
33125     disabled : false,
33126     html : '',
33127     position : 'bottom',
33128     icon : false,
33129     
33130     getAutoCreate : function()
33131     {
33132         var iconCls = 'roo-navigation-bar-item-icon';
33133         
33134         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33135         
33136         var cfg = {
33137             tag: 'li',
33138             cls: 'roo-navigation-bar-item',
33139             cn : [
33140                 {
33141                     tag : 'i',
33142                     cls : iconCls
33143                 }
33144             ]
33145         };
33146         
33147         if(this.active){
33148             cfg.cls += ' active';
33149         }
33150         if(this.disabled){
33151             cfg.cls += ' disabled';
33152         }
33153         
33154         return cfg;
33155     },
33156     
33157     disable : function()
33158     {
33159         this.setDisabled(true);
33160     },
33161     
33162     enable : function()
33163     {
33164         this.setDisabled(false);
33165     },
33166     
33167     initEvents: function() 
33168     {
33169         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33170         
33171         this.iconEl.on('click', this.onClick, this);
33172     },
33173     
33174     onClick : function(e)
33175     {
33176         e.preventDefault();
33177         
33178         if(this.disabled){
33179             return;
33180         }
33181         
33182         if(this.fireEvent('click', this, e) === false){
33183             return;
33184         };
33185         
33186         this.parent().setActiveItem(this);
33187     },
33188     
33189     isActive: function () 
33190     {
33191         return this.active;
33192     },
33193     
33194     setActive : function(state)
33195     {
33196         if(this.active == state){
33197             return;
33198         }
33199         
33200         this.active = state;
33201         
33202         if (state) {
33203             this.el.addClass('active');
33204             return;
33205         }
33206         
33207         this.el.removeClass('active');
33208         
33209         return;
33210     },
33211     
33212     setDisabled : function(state)
33213     {
33214         if(this.disabled == state){
33215             return;
33216         }
33217         
33218         this.disabled = state;
33219         
33220         if (state) {
33221             this.el.addClass('disabled');
33222             return;
33223         }
33224         
33225         this.el.removeClass('disabled');
33226     },
33227     
33228     tooltipEl : function()
33229     {
33230         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33231     }
33232 });
33233  
33234
33235  /*
33236  * - LGPL
33237  *
33238  * FieldLabel
33239  * 
33240  */
33241
33242 /**
33243  * @class Roo.bootstrap.FieldLabel
33244  * @extends Roo.bootstrap.Component
33245  * Bootstrap FieldLabel class
33246  * @cfg {String} html contents of the element
33247  * @cfg {String} tag tag of the element default label
33248  * @cfg {String} cls class of the element
33249  * @cfg {String} target label target 
33250  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33251  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33252  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33253  * @cfg {String} iconTooltip default "This field is required"
33254  * @cfg {String} indicatorpos (left|right) default left
33255  * 
33256  * @constructor
33257  * Create a new FieldLabel
33258  * @param {Object} config The config object
33259  */
33260
33261 Roo.bootstrap.FieldLabel = function(config){
33262     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33263     
33264     this.addEvents({
33265             /**
33266              * @event invalid
33267              * Fires after the field has been marked as invalid.
33268              * @param {Roo.form.FieldLabel} this
33269              * @param {String} msg The validation message
33270              */
33271             invalid : true,
33272             /**
33273              * @event valid
33274              * Fires after the field has been validated with no errors.
33275              * @param {Roo.form.FieldLabel} this
33276              */
33277             valid : true
33278         });
33279 };
33280
33281 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33282     
33283     tag: 'label',
33284     cls: '',
33285     html: '',
33286     target: '',
33287     allowBlank : true,
33288     invalidClass : 'has-warning',
33289     validClass : 'has-success',
33290     iconTooltip : 'This field is required',
33291     indicatorpos : 'left',
33292     
33293     getAutoCreate : function(){
33294         
33295         var cls = "";
33296         if (!this.allowBlank) {
33297             cls  = "visible";
33298         }
33299         
33300         var cfg = {
33301             tag : this.tag,
33302             cls : 'roo-bootstrap-field-label ' + this.cls,
33303             for : this.target,
33304             cn : [
33305                 {
33306                     tag : 'i',
33307                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33308                     tooltip : this.iconTooltip
33309                 },
33310                 {
33311                     tag : 'span',
33312                     html : this.html
33313                 }
33314             ] 
33315         };
33316         
33317         if(this.indicatorpos == 'right'){
33318             var cfg = {
33319                 tag : this.tag,
33320                 cls : 'roo-bootstrap-field-label ' + this.cls,
33321                 for : this.target,
33322                 cn : [
33323                     {
33324                         tag : 'span',
33325                         html : this.html
33326                     },
33327                     {
33328                         tag : 'i',
33329                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33330                         tooltip : this.iconTooltip
33331                     }
33332                 ] 
33333             };
33334         }
33335         
33336         return cfg;
33337     },
33338     
33339     initEvents: function() 
33340     {
33341         Roo.bootstrap.Element.superclass.initEvents.call(this);
33342         
33343         this.indicator = this.indicatorEl();
33344         
33345         if(this.indicator){
33346             this.indicator.removeClass('visible');
33347             this.indicator.addClass('invisible');
33348         }
33349         
33350         Roo.bootstrap.FieldLabel.register(this);
33351     },
33352     
33353     indicatorEl : function()
33354     {
33355         var indicator = this.el.select('i.roo-required-indicator',true).first();
33356         
33357         if(!indicator){
33358             return false;
33359         }
33360         
33361         return indicator;
33362         
33363     },
33364     
33365     /**
33366      * Mark this field as valid
33367      */
33368     markValid : function()
33369     {
33370         if(this.indicator){
33371             this.indicator.removeClass('visible');
33372             this.indicator.addClass('invisible');
33373         }
33374         if (Roo.bootstrap.version == 3) {
33375             this.el.removeClass(this.invalidClass);
33376             this.el.addClass(this.validClass);
33377         } else {
33378             this.el.removeClass('is-invalid');
33379             this.el.addClass('is-valid');
33380         }
33381         
33382         
33383         this.fireEvent('valid', this);
33384     },
33385     
33386     /**
33387      * Mark this field as invalid
33388      * @param {String} msg The validation message
33389      */
33390     markInvalid : function(msg)
33391     {
33392         if(this.indicator){
33393             this.indicator.removeClass('invisible');
33394             this.indicator.addClass('visible');
33395         }
33396           if (Roo.bootstrap.version == 3) {
33397             this.el.removeClass(this.validClass);
33398             this.el.addClass(this.invalidClass);
33399         } else {
33400             this.el.removeClass('is-valid');
33401             this.el.addClass('is-invalid');
33402         }
33403         
33404         
33405         this.fireEvent('invalid', this, msg);
33406     }
33407     
33408    
33409 });
33410
33411 Roo.apply(Roo.bootstrap.FieldLabel, {
33412     
33413     groups: {},
33414     
33415      /**
33416     * register a FieldLabel Group
33417     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33418     */
33419     register : function(label)
33420     {
33421         if(this.groups.hasOwnProperty(label.target)){
33422             return;
33423         }
33424      
33425         this.groups[label.target] = label;
33426         
33427     },
33428     /**
33429     * fetch a FieldLabel Group based on the target
33430     * @param {string} target
33431     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33432     */
33433     get: function(target) {
33434         if (typeof(this.groups[target]) == 'undefined') {
33435             return false;
33436         }
33437         
33438         return this.groups[target] ;
33439     }
33440 });
33441
33442  
33443
33444  /*
33445  * - LGPL
33446  *
33447  * page DateSplitField.
33448  * 
33449  */
33450
33451
33452 /**
33453  * @class Roo.bootstrap.DateSplitField
33454  * @extends Roo.bootstrap.Component
33455  * Bootstrap DateSplitField class
33456  * @cfg {string} fieldLabel - the label associated
33457  * @cfg {Number} labelWidth set the width of label (0-12)
33458  * @cfg {String} labelAlign (top|left)
33459  * @cfg {Boolean} dayAllowBlank (true|false) default false
33460  * @cfg {Boolean} monthAllowBlank (true|false) default false
33461  * @cfg {Boolean} yearAllowBlank (true|false) default false
33462  * @cfg {string} dayPlaceholder 
33463  * @cfg {string} monthPlaceholder
33464  * @cfg {string} yearPlaceholder
33465  * @cfg {string} dayFormat default 'd'
33466  * @cfg {string} monthFormat default 'm'
33467  * @cfg {string} yearFormat default 'Y'
33468  * @cfg {Number} labellg set the width of label (1-12)
33469  * @cfg {Number} labelmd set the width of label (1-12)
33470  * @cfg {Number} labelsm set the width of label (1-12)
33471  * @cfg {Number} labelxs set the width of label (1-12)
33472
33473  *     
33474  * @constructor
33475  * Create a new DateSplitField
33476  * @param {Object} config The config object
33477  */
33478
33479 Roo.bootstrap.DateSplitField = function(config){
33480     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33481     
33482     this.addEvents({
33483         // raw events
33484          /**
33485          * @event years
33486          * getting the data of years
33487          * @param {Roo.bootstrap.DateSplitField} this
33488          * @param {Object} years
33489          */
33490         "years" : true,
33491         /**
33492          * @event days
33493          * getting the data of days
33494          * @param {Roo.bootstrap.DateSplitField} this
33495          * @param {Object} days
33496          */
33497         "days" : true,
33498         /**
33499          * @event invalid
33500          * Fires after the field has been marked as invalid.
33501          * @param {Roo.form.Field} this
33502          * @param {String} msg The validation message
33503          */
33504         invalid : true,
33505        /**
33506          * @event valid
33507          * Fires after the field has been validated with no errors.
33508          * @param {Roo.form.Field} this
33509          */
33510         valid : true
33511     });
33512 };
33513
33514 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33515     
33516     fieldLabel : '',
33517     labelAlign : 'top',
33518     labelWidth : 3,
33519     dayAllowBlank : false,
33520     monthAllowBlank : false,
33521     yearAllowBlank : false,
33522     dayPlaceholder : '',
33523     monthPlaceholder : '',
33524     yearPlaceholder : '',
33525     dayFormat : 'd',
33526     monthFormat : 'm',
33527     yearFormat : 'Y',
33528     isFormField : true,
33529     labellg : 0,
33530     labelmd : 0,
33531     labelsm : 0,
33532     labelxs : 0,
33533     
33534     getAutoCreate : function()
33535     {
33536         var cfg = {
33537             tag : 'div',
33538             cls : 'row roo-date-split-field-group',
33539             cn : [
33540                 {
33541                     tag : 'input',
33542                     type : 'hidden',
33543                     cls : 'form-hidden-field roo-date-split-field-group-value',
33544                     name : this.name
33545                 }
33546             ]
33547         };
33548         
33549         var labelCls = 'col-md-12';
33550         var contentCls = 'col-md-4';
33551         
33552         if(this.fieldLabel){
33553             
33554             var label = {
33555                 tag : 'div',
33556                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33557                 cn : [
33558                     {
33559                         tag : 'label',
33560                         html : this.fieldLabel
33561                     }
33562                 ]
33563             };
33564             
33565             if(this.labelAlign == 'left'){
33566             
33567                 if(this.labelWidth > 12){
33568                     label.style = "width: " + this.labelWidth + 'px';
33569                 }
33570
33571                 if(this.labelWidth < 13 && this.labelmd == 0){
33572                     this.labelmd = this.labelWidth;
33573                 }
33574
33575                 if(this.labellg > 0){
33576                     labelCls = ' col-lg-' + this.labellg;
33577                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33578                 }
33579
33580                 if(this.labelmd > 0){
33581                     labelCls = ' col-md-' + this.labelmd;
33582                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33583                 }
33584
33585                 if(this.labelsm > 0){
33586                     labelCls = ' col-sm-' + this.labelsm;
33587                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33588                 }
33589
33590                 if(this.labelxs > 0){
33591                     labelCls = ' col-xs-' + this.labelxs;
33592                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33593                 }
33594             }
33595             
33596             label.cls += ' ' + labelCls;
33597             
33598             cfg.cn.push(label);
33599         }
33600         
33601         Roo.each(['day', 'month', 'year'], function(t){
33602             cfg.cn.push({
33603                 tag : 'div',
33604                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33605             });
33606         }, this);
33607         
33608         return cfg;
33609     },
33610     
33611     inputEl: function ()
33612     {
33613         return this.el.select('.roo-date-split-field-group-value', true).first();
33614     },
33615     
33616     onRender : function(ct, position) 
33617     {
33618         var _this = this;
33619         
33620         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33621         
33622         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33623         
33624         this.dayField = new Roo.bootstrap.ComboBox({
33625             allowBlank : this.dayAllowBlank,
33626             alwaysQuery : true,
33627             displayField : 'value',
33628             editable : false,
33629             fieldLabel : '',
33630             forceSelection : true,
33631             mode : 'local',
33632             placeholder : this.dayPlaceholder,
33633             selectOnFocus : true,
33634             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33635             triggerAction : 'all',
33636             typeAhead : true,
33637             valueField : 'value',
33638             store : new Roo.data.SimpleStore({
33639                 data : (function() {    
33640                     var days = [];
33641                     _this.fireEvent('days', _this, days);
33642                     return days;
33643                 })(),
33644                 fields : [ 'value' ]
33645             }),
33646             listeners : {
33647                 select : function (_self, record, index)
33648                 {
33649                     _this.setValue(_this.getValue());
33650                 }
33651             }
33652         });
33653
33654         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33655         
33656         this.monthField = new Roo.bootstrap.MonthField({
33657             after : '<i class=\"fa fa-calendar\"></i>',
33658             allowBlank : this.monthAllowBlank,
33659             placeholder : this.monthPlaceholder,
33660             readOnly : true,
33661             listeners : {
33662                 render : function (_self)
33663                 {
33664                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33665                         e.preventDefault();
33666                         _self.focus();
33667                     });
33668                 },
33669                 select : function (_self, oldvalue, newvalue)
33670                 {
33671                     _this.setValue(_this.getValue());
33672                 }
33673             }
33674         });
33675         
33676         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33677         
33678         this.yearField = new Roo.bootstrap.ComboBox({
33679             allowBlank : this.yearAllowBlank,
33680             alwaysQuery : true,
33681             displayField : 'value',
33682             editable : false,
33683             fieldLabel : '',
33684             forceSelection : true,
33685             mode : 'local',
33686             placeholder : this.yearPlaceholder,
33687             selectOnFocus : true,
33688             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33689             triggerAction : 'all',
33690             typeAhead : true,
33691             valueField : 'value',
33692             store : new Roo.data.SimpleStore({
33693                 data : (function() {
33694                     var years = [];
33695                     _this.fireEvent('years', _this, years);
33696                     return years;
33697                 })(),
33698                 fields : [ 'value' ]
33699             }),
33700             listeners : {
33701                 select : function (_self, record, index)
33702                 {
33703                     _this.setValue(_this.getValue());
33704                 }
33705             }
33706         });
33707
33708         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33709     },
33710     
33711     setValue : function(v, format)
33712     {
33713         this.inputEl.dom.value = v;
33714         
33715         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33716         
33717         var d = Date.parseDate(v, f);
33718         
33719         if(!d){
33720             this.validate();
33721             return;
33722         }
33723         
33724         this.setDay(d.format(this.dayFormat));
33725         this.setMonth(d.format(this.monthFormat));
33726         this.setYear(d.format(this.yearFormat));
33727         
33728         this.validate();
33729         
33730         return;
33731     },
33732     
33733     setDay : function(v)
33734     {
33735         this.dayField.setValue(v);
33736         this.inputEl.dom.value = this.getValue();
33737         this.validate();
33738         return;
33739     },
33740     
33741     setMonth : function(v)
33742     {
33743         this.monthField.setValue(v, true);
33744         this.inputEl.dom.value = this.getValue();
33745         this.validate();
33746         return;
33747     },
33748     
33749     setYear : function(v)
33750     {
33751         this.yearField.setValue(v);
33752         this.inputEl.dom.value = this.getValue();
33753         this.validate();
33754         return;
33755     },
33756     
33757     getDay : function()
33758     {
33759         return this.dayField.getValue();
33760     },
33761     
33762     getMonth : function()
33763     {
33764         return this.monthField.getValue();
33765     },
33766     
33767     getYear : function()
33768     {
33769         return this.yearField.getValue();
33770     },
33771     
33772     getValue : function()
33773     {
33774         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33775         
33776         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33777         
33778         return date;
33779     },
33780     
33781     reset : function()
33782     {
33783         this.setDay('');
33784         this.setMonth('');
33785         this.setYear('');
33786         this.inputEl.dom.value = '';
33787         this.validate();
33788         return;
33789     },
33790     
33791     validate : function()
33792     {
33793         var d = this.dayField.validate();
33794         var m = this.monthField.validate();
33795         var y = this.yearField.validate();
33796         
33797         var valid = true;
33798         
33799         if(
33800                 (!this.dayAllowBlank && !d) ||
33801                 (!this.monthAllowBlank && !m) ||
33802                 (!this.yearAllowBlank && !y)
33803         ){
33804             valid = false;
33805         }
33806         
33807         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33808             return valid;
33809         }
33810         
33811         if(valid){
33812             this.markValid();
33813             return valid;
33814         }
33815         
33816         this.markInvalid();
33817         
33818         return valid;
33819     },
33820     
33821     markValid : function()
33822     {
33823         
33824         var label = this.el.select('label', true).first();
33825         var icon = this.el.select('i.fa-star', true).first();
33826
33827         if(label && icon){
33828             icon.remove();
33829         }
33830         
33831         this.fireEvent('valid', this);
33832     },
33833     
33834      /**
33835      * Mark this field as invalid
33836      * @param {String} msg The validation message
33837      */
33838     markInvalid : function(msg)
33839     {
33840         
33841         var label = this.el.select('label', true).first();
33842         var icon = this.el.select('i.fa-star', true).first();
33843
33844         if(label && !icon){
33845             this.el.select('.roo-date-split-field-label', true).createChild({
33846                 tag : 'i',
33847                 cls : 'text-danger fa fa-lg fa-star',
33848                 tooltip : 'This field is required',
33849                 style : 'margin-right:5px;'
33850             }, label, true);
33851         }
33852         
33853         this.fireEvent('invalid', this, msg);
33854     },
33855     
33856     clearInvalid : function()
33857     {
33858         var label = this.el.select('label', true).first();
33859         var icon = this.el.select('i.fa-star', true).first();
33860
33861         if(label && icon){
33862             icon.remove();
33863         }
33864         
33865         this.fireEvent('valid', this);
33866     },
33867     
33868     getName: function()
33869     {
33870         return this.name;
33871     }
33872     
33873 });
33874
33875  /**
33876  *
33877  * This is based on 
33878  * http://masonry.desandro.com
33879  *
33880  * The idea is to render all the bricks based on vertical width...
33881  *
33882  * The original code extends 'outlayer' - we might need to use that....
33883  * 
33884  */
33885
33886
33887 /**
33888  * @class Roo.bootstrap.LayoutMasonry
33889  * @extends Roo.bootstrap.Component
33890  * Bootstrap Layout Masonry class
33891  * 
33892  * @constructor
33893  * Create a new Element
33894  * @param {Object} config The config object
33895  */
33896
33897 Roo.bootstrap.LayoutMasonry = function(config){
33898     
33899     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33900     
33901     this.bricks = [];
33902     
33903     Roo.bootstrap.LayoutMasonry.register(this);
33904     
33905     this.addEvents({
33906         // raw events
33907         /**
33908          * @event layout
33909          * Fire after layout the items
33910          * @param {Roo.bootstrap.LayoutMasonry} this
33911          * @param {Roo.EventObject} e
33912          */
33913         "layout" : true
33914     });
33915     
33916 };
33917
33918 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33919     
33920     /**
33921      * @cfg {Boolean} isLayoutInstant = no animation?
33922      */   
33923     isLayoutInstant : false, // needed?
33924    
33925     /**
33926      * @cfg {Number} boxWidth  width of the columns
33927      */   
33928     boxWidth : 450,
33929     
33930       /**
33931      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33932      */   
33933     boxHeight : 0,
33934     
33935     /**
33936      * @cfg {Number} padWidth padding below box..
33937      */   
33938     padWidth : 10, 
33939     
33940     /**
33941      * @cfg {Number} gutter gutter width..
33942      */   
33943     gutter : 10,
33944     
33945      /**
33946      * @cfg {Number} maxCols maximum number of columns
33947      */   
33948     
33949     maxCols: 0,
33950     
33951     /**
33952      * @cfg {Boolean} isAutoInitial defalut true
33953      */   
33954     isAutoInitial : true, 
33955     
33956     containerWidth: 0,
33957     
33958     /**
33959      * @cfg {Boolean} isHorizontal defalut false
33960      */   
33961     isHorizontal : false, 
33962
33963     currentSize : null,
33964     
33965     tag: 'div',
33966     
33967     cls: '',
33968     
33969     bricks: null, //CompositeElement
33970     
33971     cols : 1,
33972     
33973     _isLayoutInited : false,
33974     
33975 //    isAlternative : false, // only use for vertical layout...
33976     
33977     /**
33978      * @cfg {Number} alternativePadWidth padding below box..
33979      */   
33980     alternativePadWidth : 50,
33981     
33982     selectedBrick : [],
33983     
33984     getAutoCreate : function(){
33985         
33986         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33987         
33988         var cfg = {
33989             tag: this.tag,
33990             cls: 'blog-masonary-wrapper ' + this.cls,
33991             cn : {
33992                 cls : 'mas-boxes masonary'
33993             }
33994         };
33995         
33996         return cfg;
33997     },
33998     
33999     getChildContainer: function( )
34000     {
34001         if (this.boxesEl) {
34002             return this.boxesEl;
34003         }
34004         
34005         this.boxesEl = this.el.select('.mas-boxes').first();
34006         
34007         return this.boxesEl;
34008     },
34009     
34010     
34011     initEvents : function()
34012     {
34013         var _this = this;
34014         
34015         if(this.isAutoInitial){
34016             Roo.log('hook children rendered');
34017             this.on('childrenrendered', function() {
34018                 Roo.log('children rendered');
34019                 _this.initial();
34020             } ,this);
34021         }
34022     },
34023     
34024     initial : function()
34025     {
34026         this.selectedBrick = [];
34027         
34028         this.currentSize = this.el.getBox(true);
34029         
34030         Roo.EventManager.onWindowResize(this.resize, this); 
34031
34032         if(!this.isAutoInitial){
34033             this.layout();
34034             return;
34035         }
34036         
34037         this.layout();
34038         
34039         return;
34040         //this.layout.defer(500,this);
34041         
34042     },
34043     
34044     resize : function()
34045     {
34046         var cs = this.el.getBox(true);
34047         
34048         if (
34049                 this.currentSize.width == cs.width && 
34050                 this.currentSize.x == cs.x && 
34051                 this.currentSize.height == cs.height && 
34052                 this.currentSize.y == cs.y 
34053         ) {
34054             Roo.log("no change in with or X or Y");
34055             return;
34056         }
34057         
34058         this.currentSize = cs;
34059         
34060         this.layout();
34061         
34062     },
34063     
34064     layout : function()
34065     {   
34066         this._resetLayout();
34067         
34068         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34069         
34070         this.layoutItems( isInstant );
34071       
34072         this._isLayoutInited = true;
34073         
34074         this.fireEvent('layout', this);
34075         
34076     },
34077     
34078     _resetLayout : function()
34079     {
34080         if(this.isHorizontal){
34081             this.horizontalMeasureColumns();
34082             return;
34083         }
34084         
34085         this.verticalMeasureColumns();
34086         
34087     },
34088     
34089     verticalMeasureColumns : function()
34090     {
34091         this.getContainerWidth();
34092         
34093 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34094 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34095 //            return;
34096 //        }
34097         
34098         var boxWidth = this.boxWidth + this.padWidth;
34099         
34100         if(this.containerWidth < this.boxWidth){
34101             boxWidth = this.containerWidth
34102         }
34103         
34104         var containerWidth = this.containerWidth;
34105         
34106         var cols = Math.floor(containerWidth / boxWidth);
34107         
34108         this.cols = Math.max( cols, 1 );
34109         
34110         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34111         
34112         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34113         
34114         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34115         
34116         this.colWidth = boxWidth + avail - this.padWidth;
34117         
34118         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34119         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34120     },
34121     
34122     horizontalMeasureColumns : function()
34123     {
34124         this.getContainerWidth();
34125         
34126         var boxWidth = this.boxWidth;
34127         
34128         if(this.containerWidth < boxWidth){
34129             boxWidth = this.containerWidth;
34130         }
34131         
34132         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34133         
34134         this.el.setHeight(boxWidth);
34135         
34136     },
34137     
34138     getContainerWidth : function()
34139     {
34140         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34141     },
34142     
34143     layoutItems : function( isInstant )
34144     {
34145         Roo.log(this.bricks);
34146         
34147         var items = Roo.apply([], this.bricks);
34148         
34149         if(this.isHorizontal){
34150             this._horizontalLayoutItems( items , isInstant );
34151             return;
34152         }
34153         
34154 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34155 //            this._verticalAlternativeLayoutItems( items , isInstant );
34156 //            return;
34157 //        }
34158         
34159         this._verticalLayoutItems( items , isInstant );
34160         
34161     },
34162     
34163     _verticalLayoutItems : function ( items , isInstant)
34164     {
34165         if ( !items || !items.length ) {
34166             return;
34167         }
34168         
34169         var standard = [
34170             ['xs', 'xs', 'xs', 'tall'],
34171             ['xs', 'xs', 'tall'],
34172             ['xs', 'xs', 'sm'],
34173             ['xs', 'xs', 'xs'],
34174             ['xs', 'tall'],
34175             ['xs', 'sm'],
34176             ['xs', 'xs'],
34177             ['xs'],
34178             
34179             ['sm', 'xs', 'xs'],
34180             ['sm', 'xs'],
34181             ['sm'],
34182             
34183             ['tall', 'xs', 'xs', 'xs'],
34184             ['tall', 'xs', 'xs'],
34185             ['tall', 'xs'],
34186             ['tall']
34187             
34188         ];
34189         
34190         var queue = [];
34191         
34192         var boxes = [];
34193         
34194         var box = [];
34195         
34196         Roo.each(items, function(item, k){
34197             
34198             switch (item.size) {
34199                 // these layouts take up a full box,
34200                 case 'md' :
34201                 case 'md-left' :
34202                 case 'md-right' :
34203                 case 'wide' :
34204                     
34205                     if(box.length){
34206                         boxes.push(box);
34207                         box = [];
34208                     }
34209                     
34210                     boxes.push([item]);
34211                     
34212                     break;
34213                     
34214                 case 'xs' :
34215                 case 'sm' :
34216                 case 'tall' :
34217                     
34218                     box.push(item);
34219                     
34220                     break;
34221                 default :
34222                     break;
34223                     
34224             }
34225             
34226         }, this);
34227         
34228         if(box.length){
34229             boxes.push(box);
34230             box = [];
34231         }
34232         
34233         var filterPattern = function(box, length)
34234         {
34235             if(!box.length){
34236                 return;
34237             }
34238             
34239             var match = false;
34240             
34241             var pattern = box.slice(0, length);
34242             
34243             var format = [];
34244             
34245             Roo.each(pattern, function(i){
34246                 format.push(i.size);
34247             }, this);
34248             
34249             Roo.each(standard, function(s){
34250                 
34251                 if(String(s) != String(format)){
34252                     return;
34253                 }
34254                 
34255                 match = true;
34256                 return false;
34257                 
34258             }, this);
34259             
34260             if(!match && length == 1){
34261                 return;
34262             }
34263             
34264             if(!match){
34265                 filterPattern(box, length - 1);
34266                 return;
34267             }
34268                 
34269             queue.push(pattern);
34270
34271             box = box.slice(length, box.length);
34272
34273             filterPattern(box, 4);
34274
34275             return;
34276             
34277         }
34278         
34279         Roo.each(boxes, function(box, k){
34280             
34281             if(!box.length){
34282                 return;
34283             }
34284             
34285             if(box.length == 1){
34286                 queue.push(box);
34287                 return;
34288             }
34289             
34290             filterPattern(box, 4);
34291             
34292         }, this);
34293         
34294         this._processVerticalLayoutQueue( queue, isInstant );
34295         
34296     },
34297     
34298 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34299 //    {
34300 //        if ( !items || !items.length ) {
34301 //            return;
34302 //        }
34303 //
34304 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34305 //        
34306 //    },
34307     
34308     _horizontalLayoutItems : function ( items , isInstant)
34309     {
34310         if ( !items || !items.length || items.length < 3) {
34311             return;
34312         }
34313         
34314         items.reverse();
34315         
34316         var eItems = items.slice(0, 3);
34317         
34318         items = items.slice(3, items.length);
34319         
34320         var standard = [
34321             ['xs', 'xs', 'xs', 'wide'],
34322             ['xs', 'xs', 'wide'],
34323             ['xs', 'xs', 'sm'],
34324             ['xs', 'xs', 'xs'],
34325             ['xs', 'wide'],
34326             ['xs', 'sm'],
34327             ['xs', 'xs'],
34328             ['xs'],
34329             
34330             ['sm', 'xs', 'xs'],
34331             ['sm', 'xs'],
34332             ['sm'],
34333             
34334             ['wide', 'xs', 'xs', 'xs'],
34335             ['wide', 'xs', 'xs'],
34336             ['wide', 'xs'],
34337             ['wide'],
34338             
34339             ['wide-thin']
34340         ];
34341         
34342         var queue = [];
34343         
34344         var boxes = [];
34345         
34346         var box = [];
34347         
34348         Roo.each(items, function(item, k){
34349             
34350             switch (item.size) {
34351                 case 'md' :
34352                 case 'md-left' :
34353                 case 'md-right' :
34354                 case 'tall' :
34355                     
34356                     if(box.length){
34357                         boxes.push(box);
34358                         box = [];
34359                     }
34360                     
34361                     boxes.push([item]);
34362                     
34363                     break;
34364                     
34365                 case 'xs' :
34366                 case 'sm' :
34367                 case 'wide' :
34368                 case 'wide-thin' :
34369                     
34370                     box.push(item);
34371                     
34372                     break;
34373                 default :
34374                     break;
34375                     
34376             }
34377             
34378         }, this);
34379         
34380         if(box.length){
34381             boxes.push(box);
34382             box = [];
34383         }
34384         
34385         var filterPattern = function(box, length)
34386         {
34387             if(!box.length){
34388                 return;
34389             }
34390             
34391             var match = false;
34392             
34393             var pattern = box.slice(0, length);
34394             
34395             var format = [];
34396             
34397             Roo.each(pattern, function(i){
34398                 format.push(i.size);
34399             }, this);
34400             
34401             Roo.each(standard, function(s){
34402                 
34403                 if(String(s) != String(format)){
34404                     return;
34405                 }
34406                 
34407                 match = true;
34408                 return false;
34409                 
34410             }, this);
34411             
34412             if(!match && length == 1){
34413                 return;
34414             }
34415             
34416             if(!match){
34417                 filterPattern(box, length - 1);
34418                 return;
34419             }
34420                 
34421             queue.push(pattern);
34422
34423             box = box.slice(length, box.length);
34424
34425             filterPattern(box, 4);
34426
34427             return;
34428             
34429         }
34430         
34431         Roo.each(boxes, function(box, k){
34432             
34433             if(!box.length){
34434                 return;
34435             }
34436             
34437             if(box.length == 1){
34438                 queue.push(box);
34439                 return;
34440             }
34441             
34442             filterPattern(box, 4);
34443             
34444         }, this);
34445         
34446         
34447         var prune = [];
34448         
34449         var pos = this.el.getBox(true);
34450         
34451         var minX = pos.x;
34452         
34453         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34454         
34455         var hit_end = false;
34456         
34457         Roo.each(queue, function(box){
34458             
34459             if(hit_end){
34460                 
34461                 Roo.each(box, function(b){
34462                 
34463                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34464                     b.el.hide();
34465
34466                 }, this);
34467
34468                 return;
34469             }
34470             
34471             var mx = 0;
34472             
34473             Roo.each(box, function(b){
34474                 
34475                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34476                 b.el.show();
34477
34478                 mx = Math.max(mx, b.x);
34479                 
34480             }, this);
34481             
34482             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34483             
34484             if(maxX < minX){
34485                 
34486                 Roo.each(box, function(b){
34487                 
34488                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34489                     b.el.hide();
34490                     
34491                 }, this);
34492                 
34493                 hit_end = true;
34494                 
34495                 return;
34496             }
34497             
34498             prune.push(box);
34499             
34500         }, this);
34501         
34502         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34503     },
34504     
34505     /** Sets position of item in DOM
34506     * @param {Element} item
34507     * @param {Number} x - horizontal position
34508     * @param {Number} y - vertical position
34509     * @param {Boolean} isInstant - disables transitions
34510     */
34511     _processVerticalLayoutQueue : function( queue, isInstant )
34512     {
34513         var pos = this.el.getBox(true);
34514         var x = pos.x;
34515         var y = pos.y;
34516         var maxY = [];
34517         
34518         for (var i = 0; i < this.cols; i++){
34519             maxY[i] = pos.y;
34520         }
34521         
34522         Roo.each(queue, function(box, k){
34523             
34524             var col = k % this.cols;
34525             
34526             Roo.each(box, function(b,kk){
34527                 
34528                 b.el.position('absolute');
34529                 
34530                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34531                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34532                 
34533                 if(b.size == 'md-left' || b.size == 'md-right'){
34534                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34535                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34536                 }
34537                 
34538                 b.el.setWidth(width);
34539                 b.el.setHeight(height);
34540                 // iframe?
34541                 b.el.select('iframe',true).setSize(width,height);
34542                 
34543             }, this);
34544             
34545             for (var i = 0; i < this.cols; i++){
34546                 
34547                 if(maxY[i] < maxY[col]){
34548                     col = i;
34549                     continue;
34550                 }
34551                 
34552                 col = Math.min(col, i);
34553                 
34554             }
34555             
34556             x = pos.x + col * (this.colWidth + this.padWidth);
34557             
34558             y = maxY[col];
34559             
34560             var positions = [];
34561             
34562             switch (box.length){
34563                 case 1 :
34564                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34565                     break;
34566                 case 2 :
34567                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34568                     break;
34569                 case 3 :
34570                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34571                     break;
34572                 case 4 :
34573                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34574                     break;
34575                 default :
34576                     break;
34577             }
34578             
34579             Roo.each(box, function(b,kk){
34580                 
34581                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34582                 
34583                 var sz = b.el.getSize();
34584                 
34585                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34586                 
34587             }, this);
34588             
34589         }, this);
34590         
34591         var mY = 0;
34592         
34593         for (var i = 0; i < this.cols; i++){
34594             mY = Math.max(mY, maxY[i]);
34595         }
34596         
34597         this.el.setHeight(mY - pos.y);
34598         
34599     },
34600     
34601 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34602 //    {
34603 //        var pos = this.el.getBox(true);
34604 //        var x = pos.x;
34605 //        var y = pos.y;
34606 //        var maxX = pos.right;
34607 //        
34608 //        var maxHeight = 0;
34609 //        
34610 //        Roo.each(items, function(item, k){
34611 //            
34612 //            var c = k % 2;
34613 //            
34614 //            item.el.position('absolute');
34615 //                
34616 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34617 //
34618 //            item.el.setWidth(width);
34619 //
34620 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34621 //
34622 //            item.el.setHeight(height);
34623 //            
34624 //            if(c == 0){
34625 //                item.el.setXY([x, y], isInstant ? false : true);
34626 //            } else {
34627 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34628 //            }
34629 //            
34630 //            y = y + height + this.alternativePadWidth;
34631 //            
34632 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34633 //            
34634 //        }, this);
34635 //        
34636 //        this.el.setHeight(maxHeight);
34637 //        
34638 //    },
34639     
34640     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34641     {
34642         var pos = this.el.getBox(true);
34643         
34644         var minX = pos.x;
34645         var minY = pos.y;
34646         
34647         var maxX = pos.right;
34648         
34649         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34650         
34651         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34652         
34653         Roo.each(queue, function(box, k){
34654             
34655             Roo.each(box, function(b, kk){
34656                 
34657                 b.el.position('absolute');
34658                 
34659                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34660                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34661                 
34662                 if(b.size == 'md-left' || b.size == 'md-right'){
34663                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34664                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34665                 }
34666                 
34667                 b.el.setWidth(width);
34668                 b.el.setHeight(height);
34669                 
34670             }, this);
34671             
34672             if(!box.length){
34673                 return;
34674             }
34675             
34676             var positions = [];
34677             
34678             switch (box.length){
34679                 case 1 :
34680                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34681                     break;
34682                 case 2 :
34683                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34684                     break;
34685                 case 3 :
34686                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34687                     break;
34688                 case 4 :
34689                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34690                     break;
34691                 default :
34692                     break;
34693             }
34694             
34695             Roo.each(box, function(b,kk){
34696                 
34697                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34698                 
34699                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34700                 
34701             }, this);
34702             
34703         }, this);
34704         
34705     },
34706     
34707     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34708     {
34709         Roo.each(eItems, function(b,k){
34710             
34711             b.size = (k == 0) ? 'sm' : 'xs';
34712             b.x = (k == 0) ? 2 : 1;
34713             b.y = (k == 0) ? 2 : 1;
34714             
34715             b.el.position('absolute');
34716             
34717             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34718                 
34719             b.el.setWidth(width);
34720             
34721             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34722             
34723             b.el.setHeight(height);
34724             
34725         }, this);
34726
34727         var positions = [];
34728         
34729         positions.push({
34730             x : maxX - this.unitWidth * 2 - this.gutter,
34731             y : minY
34732         });
34733         
34734         positions.push({
34735             x : maxX - this.unitWidth,
34736             y : minY + (this.unitWidth + this.gutter) * 2
34737         });
34738         
34739         positions.push({
34740             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34741             y : minY
34742         });
34743         
34744         Roo.each(eItems, function(b,k){
34745             
34746             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34747
34748         }, this);
34749         
34750     },
34751     
34752     getVerticalOneBoxColPositions : function(x, y, box)
34753     {
34754         var pos = [];
34755         
34756         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34757         
34758         if(box[0].size == 'md-left'){
34759             rand = 0;
34760         }
34761         
34762         if(box[0].size == 'md-right'){
34763             rand = 1;
34764         }
34765         
34766         pos.push({
34767             x : x + (this.unitWidth + this.gutter) * rand,
34768             y : y
34769         });
34770         
34771         return pos;
34772     },
34773     
34774     getVerticalTwoBoxColPositions : function(x, y, box)
34775     {
34776         var pos = [];
34777         
34778         if(box[0].size == 'xs'){
34779             
34780             pos.push({
34781                 x : x,
34782                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34783             });
34784
34785             pos.push({
34786                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34787                 y : y
34788             });
34789             
34790             return pos;
34791             
34792         }
34793         
34794         pos.push({
34795             x : x,
34796             y : y
34797         });
34798
34799         pos.push({
34800             x : x + (this.unitWidth + this.gutter) * 2,
34801             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34802         });
34803         
34804         return pos;
34805         
34806     },
34807     
34808     getVerticalThreeBoxColPositions : function(x, y, box)
34809     {
34810         var pos = [];
34811         
34812         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34813             
34814             pos.push({
34815                 x : x,
34816                 y : y
34817             });
34818
34819             pos.push({
34820                 x : x + (this.unitWidth + this.gutter) * 1,
34821                 y : y
34822             });
34823             
34824             pos.push({
34825                 x : x + (this.unitWidth + this.gutter) * 2,
34826                 y : y
34827             });
34828             
34829             return pos;
34830             
34831         }
34832         
34833         if(box[0].size == 'xs' && box[1].size == 'xs'){
34834             
34835             pos.push({
34836                 x : x,
34837                 y : y
34838             });
34839
34840             pos.push({
34841                 x : x,
34842                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34843             });
34844             
34845             pos.push({
34846                 x : x + (this.unitWidth + this.gutter) * 1,
34847                 y : y
34848             });
34849             
34850             return pos;
34851             
34852         }
34853         
34854         pos.push({
34855             x : x,
34856             y : y
34857         });
34858
34859         pos.push({
34860             x : x + (this.unitWidth + this.gutter) * 2,
34861             y : y
34862         });
34863
34864         pos.push({
34865             x : x + (this.unitWidth + this.gutter) * 2,
34866             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34867         });
34868             
34869         return pos;
34870         
34871     },
34872     
34873     getVerticalFourBoxColPositions : function(x, y, box)
34874     {
34875         var pos = [];
34876         
34877         if(box[0].size == 'xs'){
34878             
34879             pos.push({
34880                 x : x,
34881                 y : y
34882             });
34883
34884             pos.push({
34885                 x : x,
34886                 y : y + (this.unitHeight + this.gutter) * 1
34887             });
34888             
34889             pos.push({
34890                 x : x,
34891                 y : y + (this.unitHeight + this.gutter) * 2
34892             });
34893             
34894             pos.push({
34895                 x : x + (this.unitWidth + this.gutter) * 1,
34896                 y : y
34897             });
34898             
34899             return pos;
34900             
34901         }
34902         
34903         pos.push({
34904             x : x,
34905             y : y
34906         });
34907
34908         pos.push({
34909             x : x + (this.unitWidth + this.gutter) * 2,
34910             y : y
34911         });
34912
34913         pos.push({
34914             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34915             y : y + (this.unitHeight + this.gutter) * 1
34916         });
34917
34918         pos.push({
34919             x : x + (this.unitWidth + this.gutter) * 2,
34920             y : y + (this.unitWidth + this.gutter) * 2
34921         });
34922
34923         return pos;
34924         
34925     },
34926     
34927     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34928     {
34929         var pos = [];
34930         
34931         if(box[0].size == 'md-left'){
34932             pos.push({
34933                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34934                 y : minY
34935             });
34936             
34937             return pos;
34938         }
34939         
34940         if(box[0].size == 'md-right'){
34941             pos.push({
34942                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34943                 y : minY + (this.unitWidth + this.gutter) * 1
34944             });
34945             
34946             return pos;
34947         }
34948         
34949         var rand = Math.floor(Math.random() * (4 - box[0].y));
34950         
34951         pos.push({
34952             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34953             y : minY + (this.unitWidth + this.gutter) * rand
34954         });
34955         
34956         return pos;
34957         
34958     },
34959     
34960     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34961     {
34962         var pos = [];
34963         
34964         if(box[0].size == 'xs'){
34965             
34966             pos.push({
34967                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34968                 y : minY
34969             });
34970
34971             pos.push({
34972                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34973                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34974             });
34975             
34976             return pos;
34977             
34978         }
34979         
34980         pos.push({
34981             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34982             y : minY
34983         });
34984
34985         pos.push({
34986             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34987             y : minY + (this.unitWidth + this.gutter) * 2
34988         });
34989         
34990         return pos;
34991         
34992     },
34993     
34994     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34995     {
34996         var pos = [];
34997         
34998         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34999             
35000             pos.push({
35001                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35002                 y : minY
35003             });
35004
35005             pos.push({
35006                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35007                 y : minY + (this.unitWidth + this.gutter) * 1
35008             });
35009             
35010             pos.push({
35011                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35012                 y : minY + (this.unitWidth + this.gutter) * 2
35013             });
35014             
35015             return pos;
35016             
35017         }
35018         
35019         if(box[0].size == 'xs' && box[1].size == 'xs'){
35020             
35021             pos.push({
35022                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35023                 y : minY
35024             });
35025
35026             pos.push({
35027                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35028                 y : minY
35029             });
35030             
35031             pos.push({
35032                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35033                 y : minY + (this.unitWidth + this.gutter) * 1
35034             });
35035             
35036             return pos;
35037             
35038         }
35039         
35040         pos.push({
35041             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35042             y : minY
35043         });
35044
35045         pos.push({
35046             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35047             y : minY + (this.unitWidth + this.gutter) * 2
35048         });
35049
35050         pos.push({
35051             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35052             y : minY + (this.unitWidth + this.gutter) * 2
35053         });
35054             
35055         return pos;
35056         
35057     },
35058     
35059     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35060     {
35061         var pos = [];
35062         
35063         if(box[0].size == 'xs'){
35064             
35065             pos.push({
35066                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35067                 y : minY
35068             });
35069
35070             pos.push({
35071                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35072                 y : minY
35073             });
35074             
35075             pos.push({
35076                 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),
35077                 y : minY
35078             });
35079             
35080             pos.push({
35081                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35082                 y : minY + (this.unitWidth + this.gutter) * 1
35083             });
35084             
35085             return pos;
35086             
35087         }
35088         
35089         pos.push({
35090             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35091             y : minY
35092         });
35093         
35094         pos.push({
35095             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35096             y : minY + (this.unitWidth + this.gutter) * 2
35097         });
35098         
35099         pos.push({
35100             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35101             y : minY + (this.unitWidth + this.gutter) * 2
35102         });
35103         
35104         pos.push({
35105             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),
35106             y : minY + (this.unitWidth + this.gutter) * 2
35107         });
35108
35109         return pos;
35110         
35111     },
35112     
35113     /**
35114     * remove a Masonry Brick
35115     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35116     */
35117     removeBrick : function(brick_id)
35118     {
35119         if (!brick_id) {
35120             return;
35121         }
35122         
35123         for (var i = 0; i<this.bricks.length; i++) {
35124             if (this.bricks[i].id == brick_id) {
35125                 this.bricks.splice(i,1);
35126                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35127                 this.initial();
35128             }
35129         }
35130     },
35131     
35132     /**
35133     * adds a Masonry Brick
35134     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35135     */
35136     addBrick : function(cfg)
35137     {
35138         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35139         //this.register(cn);
35140         cn.parentId = this.id;
35141         cn.render(this.el);
35142         return cn;
35143     },
35144     
35145     /**
35146     * register a Masonry Brick
35147     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35148     */
35149     
35150     register : function(brick)
35151     {
35152         this.bricks.push(brick);
35153         brick.masonryId = this.id;
35154     },
35155     
35156     /**
35157     * clear all the Masonry Brick
35158     */
35159     clearAll : function()
35160     {
35161         this.bricks = [];
35162         //this.getChildContainer().dom.innerHTML = "";
35163         this.el.dom.innerHTML = '';
35164     },
35165     
35166     getSelected : function()
35167     {
35168         if (!this.selectedBrick) {
35169             return false;
35170         }
35171         
35172         return this.selectedBrick;
35173     }
35174 });
35175
35176 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35177     
35178     groups: {},
35179      /**
35180     * register a Masonry Layout
35181     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35182     */
35183     
35184     register : function(layout)
35185     {
35186         this.groups[layout.id] = layout;
35187     },
35188     /**
35189     * fetch a  Masonry Layout based on the masonry layout ID
35190     * @param {string} the masonry layout to add
35191     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35192     */
35193     
35194     get: function(layout_id) {
35195         if (typeof(this.groups[layout_id]) == 'undefined') {
35196             return false;
35197         }
35198         return this.groups[layout_id] ;
35199     }
35200     
35201     
35202     
35203 });
35204
35205  
35206
35207  /**
35208  *
35209  * This is based on 
35210  * http://masonry.desandro.com
35211  *
35212  * The idea is to render all the bricks based on vertical width...
35213  *
35214  * The original code extends 'outlayer' - we might need to use that....
35215  * 
35216  */
35217
35218
35219 /**
35220  * @class Roo.bootstrap.LayoutMasonryAuto
35221  * @extends Roo.bootstrap.Component
35222  * Bootstrap Layout Masonry class
35223  * 
35224  * @constructor
35225  * Create a new Element
35226  * @param {Object} config The config object
35227  */
35228
35229 Roo.bootstrap.LayoutMasonryAuto = function(config){
35230     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35231 };
35232
35233 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35234     
35235       /**
35236      * @cfg {Boolean} isFitWidth  - resize the width..
35237      */   
35238     isFitWidth : false,  // options..
35239     /**
35240      * @cfg {Boolean} isOriginLeft = left align?
35241      */   
35242     isOriginLeft : true,
35243     /**
35244      * @cfg {Boolean} isOriginTop = top align?
35245      */   
35246     isOriginTop : false,
35247     /**
35248      * @cfg {Boolean} isLayoutInstant = no animation?
35249      */   
35250     isLayoutInstant : false, // needed?
35251     /**
35252      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35253      */   
35254     isResizingContainer : true,
35255     /**
35256      * @cfg {Number} columnWidth  width of the columns 
35257      */   
35258     
35259     columnWidth : 0,
35260     
35261     /**
35262      * @cfg {Number} maxCols maximum number of columns
35263      */   
35264     
35265     maxCols: 0,
35266     /**
35267      * @cfg {Number} padHeight padding below box..
35268      */   
35269     
35270     padHeight : 10, 
35271     
35272     /**
35273      * @cfg {Boolean} isAutoInitial defalut true
35274      */   
35275     
35276     isAutoInitial : true, 
35277     
35278     // private?
35279     gutter : 0,
35280     
35281     containerWidth: 0,
35282     initialColumnWidth : 0,
35283     currentSize : null,
35284     
35285     colYs : null, // array.
35286     maxY : 0,
35287     padWidth: 10,
35288     
35289     
35290     tag: 'div',
35291     cls: '',
35292     bricks: null, //CompositeElement
35293     cols : 0, // array?
35294     // element : null, // wrapped now this.el
35295     _isLayoutInited : null, 
35296     
35297     
35298     getAutoCreate : function(){
35299         
35300         var cfg = {
35301             tag: this.tag,
35302             cls: 'blog-masonary-wrapper ' + this.cls,
35303             cn : {
35304                 cls : 'mas-boxes masonary'
35305             }
35306         };
35307         
35308         return cfg;
35309     },
35310     
35311     getChildContainer: function( )
35312     {
35313         if (this.boxesEl) {
35314             return this.boxesEl;
35315         }
35316         
35317         this.boxesEl = this.el.select('.mas-boxes').first();
35318         
35319         return this.boxesEl;
35320     },
35321     
35322     
35323     initEvents : function()
35324     {
35325         var _this = this;
35326         
35327         if(this.isAutoInitial){
35328             Roo.log('hook children rendered');
35329             this.on('childrenrendered', function() {
35330                 Roo.log('children rendered');
35331                 _this.initial();
35332             } ,this);
35333         }
35334         
35335     },
35336     
35337     initial : function()
35338     {
35339         this.reloadItems();
35340
35341         this.currentSize = this.el.getBox(true);
35342
35343         /// was window resize... - let's see if this works..
35344         Roo.EventManager.onWindowResize(this.resize, this); 
35345
35346         if(!this.isAutoInitial){
35347             this.layout();
35348             return;
35349         }
35350         
35351         this.layout.defer(500,this);
35352     },
35353     
35354     reloadItems: function()
35355     {
35356         this.bricks = this.el.select('.masonry-brick', true);
35357         
35358         this.bricks.each(function(b) {
35359             //Roo.log(b.getSize());
35360             if (!b.attr('originalwidth')) {
35361                 b.attr('originalwidth',  b.getSize().width);
35362             }
35363             
35364         });
35365         
35366         Roo.log(this.bricks.elements.length);
35367     },
35368     
35369     resize : function()
35370     {
35371         Roo.log('resize');
35372         var cs = this.el.getBox(true);
35373         
35374         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35375             Roo.log("no change in with or X");
35376             return;
35377         }
35378         this.currentSize = cs;
35379         this.layout();
35380     },
35381     
35382     layout : function()
35383     {
35384          Roo.log('layout');
35385         this._resetLayout();
35386         //this._manageStamps();
35387       
35388         // don't animate first layout
35389         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35390         this.layoutItems( isInstant );
35391       
35392         // flag for initalized
35393         this._isLayoutInited = true;
35394     },
35395     
35396     layoutItems : function( isInstant )
35397     {
35398         //var items = this._getItemsForLayout( this.items );
35399         // original code supports filtering layout items.. we just ignore it..
35400         
35401         this._layoutItems( this.bricks , isInstant );
35402       
35403         this._postLayout();
35404     },
35405     _layoutItems : function ( items , isInstant)
35406     {
35407        //this.fireEvent( 'layout', this, items );
35408     
35409
35410         if ( !items || !items.elements.length ) {
35411           // no items, emit event with empty array
35412             return;
35413         }
35414
35415         var queue = [];
35416         items.each(function(item) {
35417             Roo.log("layout item");
35418             Roo.log(item);
35419             // get x/y object from method
35420             var position = this._getItemLayoutPosition( item );
35421             // enqueue
35422             position.item = item;
35423             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35424             queue.push( position );
35425         }, this);
35426       
35427         this._processLayoutQueue( queue );
35428     },
35429     /** Sets position of item in DOM
35430     * @param {Element} item
35431     * @param {Number} x - horizontal position
35432     * @param {Number} y - vertical position
35433     * @param {Boolean} isInstant - disables transitions
35434     */
35435     _processLayoutQueue : function( queue )
35436     {
35437         for ( var i=0, len = queue.length; i < len; i++ ) {
35438             var obj = queue[i];
35439             obj.item.position('absolute');
35440             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35441         }
35442     },
35443       
35444     
35445     /**
35446     * Any logic you want to do after each layout,
35447     * i.e. size the container
35448     */
35449     _postLayout : function()
35450     {
35451         this.resizeContainer();
35452     },
35453     
35454     resizeContainer : function()
35455     {
35456         if ( !this.isResizingContainer ) {
35457             return;
35458         }
35459         var size = this._getContainerSize();
35460         if ( size ) {
35461             this.el.setSize(size.width,size.height);
35462             this.boxesEl.setSize(size.width,size.height);
35463         }
35464     },
35465     
35466     
35467     
35468     _resetLayout : function()
35469     {
35470         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35471         this.colWidth = this.el.getWidth();
35472         //this.gutter = this.el.getWidth(); 
35473         
35474         this.measureColumns();
35475
35476         // reset column Y
35477         var i = this.cols;
35478         this.colYs = [];
35479         while (i--) {
35480             this.colYs.push( 0 );
35481         }
35482     
35483         this.maxY = 0;
35484     },
35485
35486     measureColumns : function()
35487     {
35488         this.getContainerWidth();
35489       // if columnWidth is 0, default to outerWidth of first item
35490         if ( !this.columnWidth ) {
35491             var firstItem = this.bricks.first();
35492             Roo.log(firstItem);
35493             this.columnWidth  = this.containerWidth;
35494             if (firstItem && firstItem.attr('originalwidth') ) {
35495                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35496             }
35497             // columnWidth fall back to item of first element
35498             Roo.log("set column width?");
35499                         this.initialColumnWidth = this.columnWidth  ;
35500
35501             // if first elem has no width, default to size of container
35502             
35503         }
35504         
35505         
35506         if (this.initialColumnWidth) {
35507             this.columnWidth = this.initialColumnWidth;
35508         }
35509         
35510         
35511             
35512         // column width is fixed at the top - however if container width get's smaller we should
35513         // reduce it...
35514         
35515         // this bit calcs how man columns..
35516             
35517         var columnWidth = this.columnWidth += this.gutter;
35518       
35519         // calculate columns
35520         var containerWidth = this.containerWidth + this.gutter;
35521         
35522         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35523         // fix rounding errors, typically with gutters
35524         var excess = columnWidth - containerWidth % columnWidth;
35525         
35526         
35527         // if overshoot is less than a pixel, round up, otherwise floor it
35528         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35529         cols = Math[ mathMethod ]( cols );
35530         this.cols = Math.max( cols, 1 );
35531         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35532         
35533          // padding positioning..
35534         var totalColWidth = this.cols * this.columnWidth;
35535         var padavail = this.containerWidth - totalColWidth;
35536         // so for 2 columns - we need 3 'pads'
35537         
35538         var padNeeded = (1+this.cols) * this.padWidth;
35539         
35540         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35541         
35542         this.columnWidth += padExtra
35543         //this.padWidth = Math.floor(padavail /  ( this.cols));
35544         
35545         // adjust colum width so that padding is fixed??
35546         
35547         // we have 3 columns ... total = width * 3
35548         // we have X left over... that should be used by 
35549         
35550         //if (this.expandC) {
35551             
35552         //}
35553         
35554         
35555         
35556     },
35557     
35558     getContainerWidth : function()
35559     {
35560        /* // container is parent if fit width
35561         var container = this.isFitWidth ? this.element.parentNode : this.element;
35562         // check that this.size and size are there
35563         // IE8 triggers resize on body size change, so they might not be
35564         
35565         var size = getSize( container );  //FIXME
35566         this.containerWidth = size && size.innerWidth; //FIXME
35567         */
35568          
35569         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35570         
35571     },
35572     
35573     _getItemLayoutPosition : function( item )  // what is item?
35574     {
35575         // we resize the item to our columnWidth..
35576       
35577         item.setWidth(this.columnWidth);
35578         item.autoBoxAdjust  = false;
35579         
35580         var sz = item.getSize();
35581  
35582         // how many columns does this brick span
35583         var remainder = this.containerWidth % this.columnWidth;
35584         
35585         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35586         // round if off by 1 pixel, otherwise use ceil
35587         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35588         colSpan = Math.min( colSpan, this.cols );
35589         
35590         // normally this should be '1' as we dont' currently allow multi width columns..
35591         
35592         var colGroup = this._getColGroup( colSpan );
35593         // get the minimum Y value from the columns
35594         var minimumY = Math.min.apply( Math, colGroup );
35595         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35596         
35597         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35598          
35599         // position the brick
35600         var position = {
35601             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35602             y: this.currentSize.y + minimumY + this.padHeight
35603         };
35604         
35605         Roo.log(position);
35606         // apply setHeight to necessary columns
35607         var setHeight = minimumY + sz.height + this.padHeight;
35608         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35609         
35610         var setSpan = this.cols + 1 - colGroup.length;
35611         for ( var i = 0; i < setSpan; i++ ) {
35612           this.colYs[ shortColIndex + i ] = setHeight ;
35613         }
35614       
35615         return position;
35616     },
35617     
35618     /**
35619      * @param {Number} colSpan - number of columns the element spans
35620      * @returns {Array} colGroup
35621      */
35622     _getColGroup : function( colSpan )
35623     {
35624         if ( colSpan < 2 ) {
35625           // if brick spans only one column, use all the column Ys
35626           return this.colYs;
35627         }
35628       
35629         var colGroup = [];
35630         // how many different places could this brick fit horizontally
35631         var groupCount = this.cols + 1 - colSpan;
35632         // for each group potential horizontal position
35633         for ( var i = 0; i < groupCount; i++ ) {
35634           // make an array of colY values for that one group
35635           var groupColYs = this.colYs.slice( i, i + colSpan );
35636           // and get the max value of the array
35637           colGroup[i] = Math.max.apply( Math, groupColYs );
35638         }
35639         return colGroup;
35640     },
35641     /*
35642     _manageStamp : function( stamp )
35643     {
35644         var stampSize =  stamp.getSize();
35645         var offset = stamp.getBox();
35646         // get the columns that this stamp affects
35647         var firstX = this.isOriginLeft ? offset.x : offset.right;
35648         var lastX = firstX + stampSize.width;
35649         var firstCol = Math.floor( firstX / this.columnWidth );
35650         firstCol = Math.max( 0, firstCol );
35651         
35652         var lastCol = Math.floor( lastX / this.columnWidth );
35653         // lastCol should not go over if multiple of columnWidth #425
35654         lastCol -= lastX % this.columnWidth ? 0 : 1;
35655         lastCol = Math.min( this.cols - 1, lastCol );
35656         
35657         // set colYs to bottom of the stamp
35658         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35659             stampSize.height;
35660             
35661         for ( var i = firstCol; i <= lastCol; i++ ) {
35662           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35663         }
35664     },
35665     */
35666     
35667     _getContainerSize : function()
35668     {
35669         this.maxY = Math.max.apply( Math, this.colYs );
35670         var size = {
35671             height: this.maxY
35672         };
35673       
35674         if ( this.isFitWidth ) {
35675             size.width = this._getContainerFitWidth();
35676         }
35677       
35678         return size;
35679     },
35680     
35681     _getContainerFitWidth : function()
35682     {
35683         var unusedCols = 0;
35684         // count unused columns
35685         var i = this.cols;
35686         while ( --i ) {
35687           if ( this.colYs[i] !== 0 ) {
35688             break;
35689           }
35690           unusedCols++;
35691         }
35692         // fit container to columns that have been used
35693         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35694     },
35695     
35696     needsResizeLayout : function()
35697     {
35698         var previousWidth = this.containerWidth;
35699         this.getContainerWidth();
35700         return previousWidth !== this.containerWidth;
35701     }
35702  
35703 });
35704
35705  
35706
35707  /*
35708  * - LGPL
35709  *
35710  * element
35711  * 
35712  */
35713
35714 /**
35715  * @class Roo.bootstrap.MasonryBrick
35716  * @extends Roo.bootstrap.Component
35717  * Bootstrap MasonryBrick class
35718  * 
35719  * @constructor
35720  * Create a new MasonryBrick
35721  * @param {Object} config The config object
35722  */
35723
35724 Roo.bootstrap.MasonryBrick = function(config){
35725     
35726     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35727     
35728     Roo.bootstrap.MasonryBrick.register(this);
35729     
35730     this.addEvents({
35731         // raw events
35732         /**
35733          * @event click
35734          * When a MasonryBrick is clcik
35735          * @param {Roo.bootstrap.MasonryBrick} this
35736          * @param {Roo.EventObject} e
35737          */
35738         "click" : true
35739     });
35740 };
35741
35742 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35743     
35744     /**
35745      * @cfg {String} title
35746      */   
35747     title : '',
35748     /**
35749      * @cfg {String} html
35750      */   
35751     html : '',
35752     /**
35753      * @cfg {String} bgimage
35754      */   
35755     bgimage : '',
35756     /**
35757      * @cfg {String} videourl
35758      */   
35759     videourl : '',
35760     /**
35761      * @cfg {String} cls
35762      */   
35763     cls : '',
35764     /**
35765      * @cfg {String} href
35766      */   
35767     href : '',
35768     /**
35769      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35770      */   
35771     size : 'xs',
35772     
35773     /**
35774      * @cfg {String} placetitle (center|bottom)
35775      */   
35776     placetitle : '',
35777     
35778     /**
35779      * @cfg {Boolean} isFitContainer defalut true
35780      */   
35781     isFitContainer : true, 
35782     
35783     /**
35784      * @cfg {Boolean} preventDefault defalut false
35785      */   
35786     preventDefault : false, 
35787     
35788     /**
35789      * @cfg {Boolean} inverse defalut false
35790      */   
35791     maskInverse : false, 
35792     
35793     getAutoCreate : function()
35794     {
35795         if(!this.isFitContainer){
35796             return this.getSplitAutoCreate();
35797         }
35798         
35799         var cls = 'masonry-brick masonry-brick-full';
35800         
35801         if(this.href.length){
35802             cls += ' masonry-brick-link';
35803         }
35804         
35805         if(this.bgimage.length){
35806             cls += ' masonry-brick-image';
35807         }
35808         
35809         if(this.maskInverse){
35810             cls += ' mask-inverse';
35811         }
35812         
35813         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35814             cls += ' enable-mask';
35815         }
35816         
35817         if(this.size){
35818             cls += ' masonry-' + this.size + '-brick';
35819         }
35820         
35821         if(this.placetitle.length){
35822             
35823             switch (this.placetitle) {
35824                 case 'center' :
35825                     cls += ' masonry-center-title';
35826                     break;
35827                 case 'bottom' :
35828                     cls += ' masonry-bottom-title';
35829                     break;
35830                 default:
35831                     break;
35832             }
35833             
35834         } else {
35835             if(!this.html.length && !this.bgimage.length){
35836                 cls += ' masonry-center-title';
35837             }
35838
35839             if(!this.html.length && this.bgimage.length){
35840                 cls += ' masonry-bottom-title';
35841             }
35842         }
35843         
35844         if(this.cls){
35845             cls += ' ' + this.cls;
35846         }
35847         
35848         var cfg = {
35849             tag: (this.href.length) ? 'a' : 'div',
35850             cls: cls,
35851             cn: [
35852                 {
35853                     tag: 'div',
35854                     cls: 'masonry-brick-mask'
35855                 },
35856                 {
35857                     tag: 'div',
35858                     cls: 'masonry-brick-paragraph',
35859                     cn: []
35860                 }
35861             ]
35862         };
35863         
35864         if(this.href.length){
35865             cfg.href = this.href;
35866         }
35867         
35868         var cn = cfg.cn[1].cn;
35869         
35870         if(this.title.length){
35871             cn.push({
35872                 tag: 'h4',
35873                 cls: 'masonry-brick-title',
35874                 html: this.title
35875             });
35876         }
35877         
35878         if(this.html.length){
35879             cn.push({
35880                 tag: 'p',
35881                 cls: 'masonry-brick-text',
35882                 html: this.html
35883             });
35884         }
35885         
35886         if (!this.title.length && !this.html.length) {
35887             cfg.cn[1].cls += ' hide';
35888         }
35889         
35890         if(this.bgimage.length){
35891             cfg.cn.push({
35892                 tag: 'img',
35893                 cls: 'masonry-brick-image-view',
35894                 src: this.bgimage
35895             });
35896         }
35897         
35898         if(this.videourl.length){
35899             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35900             // youtube support only?
35901             cfg.cn.push({
35902                 tag: 'iframe',
35903                 cls: 'masonry-brick-image-view',
35904                 src: vurl,
35905                 frameborder : 0,
35906                 allowfullscreen : true
35907             });
35908         }
35909         
35910         return cfg;
35911         
35912     },
35913     
35914     getSplitAutoCreate : function()
35915     {
35916         var cls = 'masonry-brick masonry-brick-split';
35917         
35918         if(this.href.length){
35919             cls += ' masonry-brick-link';
35920         }
35921         
35922         if(this.bgimage.length){
35923             cls += ' masonry-brick-image';
35924         }
35925         
35926         if(this.size){
35927             cls += ' masonry-' + this.size + '-brick';
35928         }
35929         
35930         switch (this.placetitle) {
35931             case 'center' :
35932                 cls += ' masonry-center-title';
35933                 break;
35934             case 'bottom' :
35935                 cls += ' masonry-bottom-title';
35936                 break;
35937             default:
35938                 if(!this.bgimage.length){
35939                     cls += ' masonry-center-title';
35940                 }
35941
35942                 if(this.bgimage.length){
35943                     cls += ' masonry-bottom-title';
35944                 }
35945                 break;
35946         }
35947         
35948         if(this.cls){
35949             cls += ' ' + this.cls;
35950         }
35951         
35952         var cfg = {
35953             tag: (this.href.length) ? 'a' : 'div',
35954             cls: cls,
35955             cn: [
35956                 {
35957                     tag: 'div',
35958                     cls: 'masonry-brick-split-head',
35959                     cn: [
35960                         {
35961                             tag: 'div',
35962                             cls: 'masonry-brick-paragraph',
35963                             cn: []
35964                         }
35965                     ]
35966                 },
35967                 {
35968                     tag: 'div',
35969                     cls: 'masonry-brick-split-body',
35970                     cn: []
35971                 }
35972             ]
35973         };
35974         
35975         if(this.href.length){
35976             cfg.href = this.href;
35977         }
35978         
35979         if(this.title.length){
35980             cfg.cn[0].cn[0].cn.push({
35981                 tag: 'h4',
35982                 cls: 'masonry-brick-title',
35983                 html: this.title
35984             });
35985         }
35986         
35987         if(this.html.length){
35988             cfg.cn[1].cn.push({
35989                 tag: 'p',
35990                 cls: 'masonry-brick-text',
35991                 html: this.html
35992             });
35993         }
35994
35995         if(this.bgimage.length){
35996             cfg.cn[0].cn.push({
35997                 tag: 'img',
35998                 cls: 'masonry-brick-image-view',
35999                 src: this.bgimage
36000             });
36001         }
36002         
36003         if(this.videourl.length){
36004             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
36005             // youtube support only?
36006             cfg.cn[0].cn.cn.push({
36007                 tag: 'iframe',
36008                 cls: 'masonry-brick-image-view',
36009                 src: vurl,
36010                 frameborder : 0,
36011                 allowfullscreen : true
36012             });
36013         }
36014         
36015         return cfg;
36016     },
36017     
36018     initEvents: function() 
36019     {
36020         switch (this.size) {
36021             case 'xs' :
36022                 this.x = 1;
36023                 this.y = 1;
36024                 break;
36025             case 'sm' :
36026                 this.x = 2;
36027                 this.y = 2;
36028                 break;
36029             case 'md' :
36030             case 'md-left' :
36031             case 'md-right' :
36032                 this.x = 3;
36033                 this.y = 3;
36034                 break;
36035             case 'tall' :
36036                 this.x = 2;
36037                 this.y = 3;
36038                 break;
36039             case 'wide' :
36040                 this.x = 3;
36041                 this.y = 2;
36042                 break;
36043             case 'wide-thin' :
36044                 this.x = 3;
36045                 this.y = 1;
36046                 break;
36047                         
36048             default :
36049                 break;
36050         }
36051         
36052         if(Roo.isTouch){
36053             this.el.on('touchstart', this.onTouchStart, this);
36054             this.el.on('touchmove', this.onTouchMove, this);
36055             this.el.on('touchend', this.onTouchEnd, this);
36056             this.el.on('contextmenu', this.onContextMenu, this);
36057         } else {
36058             this.el.on('mouseenter'  ,this.enter, this);
36059             this.el.on('mouseleave', this.leave, this);
36060             this.el.on('click', this.onClick, this);
36061         }
36062         
36063         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36064             this.parent().bricks.push(this);   
36065         }
36066         
36067     },
36068     
36069     onClick: function(e, el)
36070     {
36071         var time = this.endTimer - this.startTimer;
36072         // Roo.log(e.preventDefault());
36073         if(Roo.isTouch){
36074             if(time > 1000){
36075                 e.preventDefault();
36076                 return;
36077             }
36078         }
36079         
36080         if(!this.preventDefault){
36081             return;
36082         }
36083         
36084         e.preventDefault();
36085         
36086         if (this.activeClass != '') {
36087             this.selectBrick();
36088         }
36089         
36090         this.fireEvent('click', this, e);
36091     },
36092     
36093     enter: function(e, el)
36094     {
36095         e.preventDefault();
36096         
36097         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36098             return;
36099         }
36100         
36101         if(this.bgimage.length && this.html.length){
36102             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36103         }
36104     },
36105     
36106     leave: function(e, el)
36107     {
36108         e.preventDefault();
36109         
36110         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36111             return;
36112         }
36113         
36114         if(this.bgimage.length && this.html.length){
36115             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36116         }
36117     },
36118     
36119     onTouchStart: function(e, el)
36120     {
36121 //        e.preventDefault();
36122         
36123         this.touchmoved = false;
36124         
36125         if(!this.isFitContainer){
36126             return;
36127         }
36128         
36129         if(!this.bgimage.length || !this.html.length){
36130             return;
36131         }
36132         
36133         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36134         
36135         this.timer = new Date().getTime();
36136         
36137     },
36138     
36139     onTouchMove: function(e, el)
36140     {
36141         this.touchmoved = true;
36142     },
36143     
36144     onContextMenu : function(e,el)
36145     {
36146         e.preventDefault();
36147         e.stopPropagation();
36148         return false;
36149     },
36150     
36151     onTouchEnd: function(e, el)
36152     {
36153 //        e.preventDefault();
36154         
36155         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36156         
36157             this.leave(e,el);
36158             
36159             return;
36160         }
36161         
36162         if(!this.bgimage.length || !this.html.length){
36163             
36164             if(this.href.length){
36165                 window.location.href = this.href;
36166             }
36167             
36168             return;
36169         }
36170         
36171         if(!this.isFitContainer){
36172             return;
36173         }
36174         
36175         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36176         
36177         window.location.href = this.href;
36178     },
36179     
36180     //selection on single brick only
36181     selectBrick : function() {
36182         
36183         if (!this.parentId) {
36184             return;
36185         }
36186         
36187         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36188         var index = m.selectedBrick.indexOf(this.id);
36189         
36190         if ( index > -1) {
36191             m.selectedBrick.splice(index,1);
36192             this.el.removeClass(this.activeClass);
36193             return;
36194         }
36195         
36196         for(var i = 0; i < m.selectedBrick.length; i++) {
36197             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36198             b.el.removeClass(b.activeClass);
36199         }
36200         
36201         m.selectedBrick = [];
36202         
36203         m.selectedBrick.push(this.id);
36204         this.el.addClass(this.activeClass);
36205         return;
36206     },
36207     
36208     isSelected : function(){
36209         return this.el.hasClass(this.activeClass);
36210         
36211     }
36212 });
36213
36214 Roo.apply(Roo.bootstrap.MasonryBrick, {
36215     
36216     //groups: {},
36217     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36218      /**
36219     * register a Masonry Brick
36220     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36221     */
36222     
36223     register : function(brick)
36224     {
36225         //this.groups[brick.id] = brick;
36226         this.groups.add(brick.id, brick);
36227     },
36228     /**
36229     * fetch a  masonry brick based on the masonry brick ID
36230     * @param {string} the masonry brick to add
36231     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36232     */
36233     
36234     get: function(brick_id) 
36235     {
36236         // if (typeof(this.groups[brick_id]) == 'undefined') {
36237         //     return false;
36238         // }
36239         // return this.groups[brick_id] ;
36240         
36241         if(this.groups.key(brick_id)) {
36242             return this.groups.key(brick_id);
36243         }
36244         
36245         return false;
36246     }
36247     
36248     
36249     
36250 });
36251
36252  /*
36253  * - LGPL
36254  *
36255  * element
36256  * 
36257  */
36258
36259 /**
36260  * @class Roo.bootstrap.Brick
36261  * @extends Roo.bootstrap.Component
36262  * Bootstrap Brick class
36263  * 
36264  * @constructor
36265  * Create a new Brick
36266  * @param {Object} config The config object
36267  */
36268
36269 Roo.bootstrap.Brick = function(config){
36270     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36271     
36272     this.addEvents({
36273         // raw events
36274         /**
36275          * @event click
36276          * When a Brick is click
36277          * @param {Roo.bootstrap.Brick} this
36278          * @param {Roo.EventObject} e
36279          */
36280         "click" : true
36281     });
36282 };
36283
36284 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36285     
36286     /**
36287      * @cfg {String} title
36288      */   
36289     title : '',
36290     /**
36291      * @cfg {String} html
36292      */   
36293     html : '',
36294     /**
36295      * @cfg {String} bgimage
36296      */   
36297     bgimage : '',
36298     /**
36299      * @cfg {String} cls
36300      */   
36301     cls : '',
36302     /**
36303      * @cfg {String} href
36304      */   
36305     href : '',
36306     /**
36307      * @cfg {String} video
36308      */   
36309     video : '',
36310     /**
36311      * @cfg {Boolean} square
36312      */   
36313     square : true,
36314     
36315     getAutoCreate : function()
36316     {
36317         var cls = 'roo-brick';
36318         
36319         if(this.href.length){
36320             cls += ' roo-brick-link';
36321         }
36322         
36323         if(this.bgimage.length){
36324             cls += ' roo-brick-image';
36325         }
36326         
36327         if(!this.html.length && !this.bgimage.length){
36328             cls += ' roo-brick-center-title';
36329         }
36330         
36331         if(!this.html.length && this.bgimage.length){
36332             cls += ' roo-brick-bottom-title';
36333         }
36334         
36335         if(this.cls){
36336             cls += ' ' + this.cls;
36337         }
36338         
36339         var cfg = {
36340             tag: (this.href.length) ? 'a' : 'div',
36341             cls: cls,
36342             cn: [
36343                 {
36344                     tag: 'div',
36345                     cls: 'roo-brick-paragraph',
36346                     cn: []
36347                 }
36348             ]
36349         };
36350         
36351         if(this.href.length){
36352             cfg.href = this.href;
36353         }
36354         
36355         var cn = cfg.cn[0].cn;
36356         
36357         if(this.title.length){
36358             cn.push({
36359                 tag: 'h4',
36360                 cls: 'roo-brick-title',
36361                 html: this.title
36362             });
36363         }
36364         
36365         if(this.html.length){
36366             cn.push({
36367                 tag: 'p',
36368                 cls: 'roo-brick-text',
36369                 html: this.html
36370             });
36371         } else {
36372             cn.cls += ' hide';
36373         }
36374         
36375         if(this.bgimage.length){
36376             cfg.cn.push({
36377                 tag: 'img',
36378                 cls: 'roo-brick-image-view',
36379                 src: this.bgimage
36380             });
36381         }
36382         
36383         return cfg;
36384     },
36385     
36386     initEvents: function() 
36387     {
36388         if(this.title.length || this.html.length){
36389             this.el.on('mouseenter'  ,this.enter, this);
36390             this.el.on('mouseleave', this.leave, this);
36391         }
36392         
36393         Roo.EventManager.onWindowResize(this.resize, this); 
36394         
36395         if(this.bgimage.length){
36396             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36397             this.imageEl.on('load', this.onImageLoad, this);
36398             return;
36399         }
36400         
36401         this.resize();
36402     },
36403     
36404     onImageLoad : function()
36405     {
36406         this.resize();
36407     },
36408     
36409     resize : function()
36410     {
36411         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36412         
36413         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36414         
36415         if(this.bgimage.length){
36416             var image = this.el.select('.roo-brick-image-view', true).first();
36417             
36418             image.setWidth(paragraph.getWidth());
36419             
36420             if(this.square){
36421                 image.setHeight(paragraph.getWidth());
36422             }
36423             
36424             this.el.setHeight(image.getHeight());
36425             paragraph.setHeight(image.getHeight());
36426             
36427         }
36428         
36429     },
36430     
36431     enter: function(e, el)
36432     {
36433         e.preventDefault();
36434         
36435         if(this.bgimage.length){
36436             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36437             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36438         }
36439     },
36440     
36441     leave: function(e, el)
36442     {
36443         e.preventDefault();
36444         
36445         if(this.bgimage.length){
36446             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36447             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36448         }
36449     }
36450     
36451 });
36452
36453  
36454
36455  /*
36456  * - LGPL
36457  *
36458  * Number field 
36459  */
36460
36461 /**
36462  * @class Roo.bootstrap.NumberField
36463  * @extends Roo.bootstrap.Input
36464  * Bootstrap NumberField class
36465  * 
36466  * 
36467  * 
36468  * 
36469  * @constructor
36470  * Create a new NumberField
36471  * @param {Object} config The config object
36472  */
36473
36474 Roo.bootstrap.NumberField = function(config){
36475     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36476 };
36477
36478 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36479     
36480     /**
36481      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36482      */
36483     allowDecimals : true,
36484     /**
36485      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36486      */
36487     decimalSeparator : ".",
36488     /**
36489      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36490      */
36491     decimalPrecision : 2,
36492     /**
36493      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36494      */
36495     allowNegative : true,
36496     
36497     /**
36498      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36499      */
36500     allowZero: true,
36501     /**
36502      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36503      */
36504     minValue : Number.NEGATIVE_INFINITY,
36505     /**
36506      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36507      */
36508     maxValue : Number.MAX_VALUE,
36509     /**
36510      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36511      */
36512     minText : "The minimum value for this field is {0}",
36513     /**
36514      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36515      */
36516     maxText : "The maximum value for this field is {0}",
36517     /**
36518      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36519      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36520      */
36521     nanText : "{0} is not a valid number",
36522     /**
36523      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36524      */
36525     thousandsDelimiter : false,
36526     /**
36527      * @cfg {String} valueAlign alignment of value
36528      */
36529     valueAlign : "left",
36530
36531     getAutoCreate : function()
36532     {
36533         var hiddenInput = {
36534             tag: 'input',
36535             type: 'hidden',
36536             id: Roo.id(),
36537             cls: 'hidden-number-input'
36538         };
36539         
36540         if (this.name) {
36541             hiddenInput.name = this.name;
36542         }
36543         
36544         this.name = '';
36545         
36546         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36547         
36548         this.name = hiddenInput.name;
36549         
36550         if(cfg.cn.length > 0) {
36551             cfg.cn.push(hiddenInput);
36552         }
36553         
36554         return cfg;
36555     },
36556
36557     // private
36558     initEvents : function()
36559     {   
36560         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36561         
36562         var allowed = "0123456789";
36563         
36564         if(this.allowDecimals){
36565             allowed += this.decimalSeparator;
36566         }
36567         
36568         if(this.allowNegative){
36569             allowed += "-";
36570         }
36571         
36572         if(this.thousandsDelimiter) {
36573             allowed += ",";
36574         }
36575         
36576         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36577         
36578         var keyPress = function(e){
36579             
36580             var k = e.getKey();
36581             
36582             var c = e.getCharCode();
36583             
36584             if(
36585                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36586                     allowed.indexOf(String.fromCharCode(c)) === -1
36587             ){
36588                 e.stopEvent();
36589                 return;
36590             }
36591             
36592             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36593                 return;
36594             }
36595             
36596             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36597                 e.stopEvent();
36598             }
36599         };
36600         
36601         this.el.on("keypress", keyPress, this);
36602     },
36603     
36604     validateValue : function(value)
36605     {
36606         
36607         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36608             return false;
36609         }
36610         
36611         var num = this.parseValue(value);
36612         
36613         if(isNaN(num)){
36614             this.markInvalid(String.format(this.nanText, value));
36615             return false;
36616         }
36617         
36618         if(num < this.minValue){
36619             this.markInvalid(String.format(this.minText, this.minValue));
36620             return false;
36621         }
36622         
36623         if(num > this.maxValue){
36624             this.markInvalid(String.format(this.maxText, this.maxValue));
36625             return false;
36626         }
36627         
36628         return true;
36629     },
36630
36631     getValue : function()
36632     {
36633         var v = this.hiddenEl().getValue();
36634         
36635         return this.fixPrecision(this.parseValue(v));
36636     },
36637
36638     parseValue : function(value)
36639     {
36640         if(this.thousandsDelimiter) {
36641             value += "";
36642             r = new RegExp(",", "g");
36643             value = value.replace(r, "");
36644         }
36645         
36646         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36647         return isNaN(value) ? '' : value;
36648     },
36649
36650     fixPrecision : function(value)
36651     {
36652         if(this.thousandsDelimiter) {
36653             value += "";
36654             r = new RegExp(",", "g");
36655             value = value.replace(r, "");
36656         }
36657         
36658         var nan = isNaN(value);
36659         
36660         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36661             return nan ? '' : value;
36662         }
36663         return parseFloat(value).toFixed(this.decimalPrecision);
36664     },
36665
36666     setValue : function(v)
36667     {
36668         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36669         
36670         this.value = v;
36671         
36672         if(this.rendered){
36673             
36674             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36675             
36676             this.inputEl().dom.value = (v == '') ? '' :
36677                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36678             
36679             if(!this.allowZero && v === '0') {
36680                 this.hiddenEl().dom.value = '';
36681                 this.inputEl().dom.value = '';
36682             }
36683             
36684             this.validate();
36685         }
36686     },
36687
36688     decimalPrecisionFcn : function(v)
36689     {
36690         return Math.floor(v);
36691     },
36692
36693     beforeBlur : function()
36694     {
36695         var v = this.parseValue(this.getRawValue());
36696         
36697         if(v || v === 0 || v === ''){
36698             this.setValue(v);
36699         }
36700     },
36701     
36702     hiddenEl : function()
36703     {
36704         return this.el.select('input.hidden-number-input',true).first();
36705     }
36706     
36707 });
36708
36709  
36710
36711 /*
36712 * Licence: LGPL
36713 */
36714
36715 /**
36716  * @class Roo.bootstrap.DocumentSlider
36717  * @extends Roo.bootstrap.Component
36718  * Bootstrap DocumentSlider class
36719  * 
36720  * @constructor
36721  * Create a new DocumentViewer
36722  * @param {Object} config The config object
36723  */
36724
36725 Roo.bootstrap.DocumentSlider = function(config){
36726     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36727     
36728     this.files = [];
36729     
36730     this.addEvents({
36731         /**
36732          * @event initial
36733          * Fire after initEvent
36734          * @param {Roo.bootstrap.DocumentSlider} this
36735          */
36736         "initial" : true,
36737         /**
36738          * @event update
36739          * Fire after update
36740          * @param {Roo.bootstrap.DocumentSlider} this
36741          */
36742         "update" : true,
36743         /**
36744          * @event click
36745          * Fire after click
36746          * @param {Roo.bootstrap.DocumentSlider} this
36747          */
36748         "click" : true
36749     });
36750 };
36751
36752 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36753     
36754     files : false,
36755     
36756     indicator : 0,
36757     
36758     getAutoCreate : function()
36759     {
36760         var cfg = {
36761             tag : 'div',
36762             cls : 'roo-document-slider',
36763             cn : [
36764                 {
36765                     tag : 'div',
36766                     cls : 'roo-document-slider-header',
36767                     cn : [
36768                         {
36769                             tag : 'div',
36770                             cls : 'roo-document-slider-header-title'
36771                         }
36772                     ]
36773                 },
36774                 {
36775                     tag : 'div',
36776                     cls : 'roo-document-slider-body',
36777                     cn : [
36778                         {
36779                             tag : 'div',
36780                             cls : 'roo-document-slider-prev',
36781                             cn : [
36782                                 {
36783                                     tag : 'i',
36784                                     cls : 'fa fa-chevron-left'
36785                                 }
36786                             ]
36787                         },
36788                         {
36789                             tag : 'div',
36790                             cls : 'roo-document-slider-thumb',
36791                             cn : [
36792                                 {
36793                                     tag : 'img',
36794                                     cls : 'roo-document-slider-image'
36795                                 }
36796                             ]
36797                         },
36798                         {
36799                             tag : 'div',
36800                             cls : 'roo-document-slider-next',
36801                             cn : [
36802                                 {
36803                                     tag : 'i',
36804                                     cls : 'fa fa-chevron-right'
36805                                 }
36806                             ]
36807                         }
36808                     ]
36809                 }
36810             ]
36811         };
36812         
36813         return cfg;
36814     },
36815     
36816     initEvents : function()
36817     {
36818         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36819         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36820         
36821         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36822         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36823         
36824         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36825         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36826         
36827         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36828         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36829         
36830         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36831         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36832         
36833         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36834         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36835         
36836         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36837         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36838         
36839         this.thumbEl.on('click', this.onClick, this);
36840         
36841         this.prevIndicator.on('click', this.prev, this);
36842         
36843         this.nextIndicator.on('click', this.next, this);
36844         
36845     },
36846     
36847     initial : function()
36848     {
36849         if(this.files.length){
36850             this.indicator = 1;
36851             this.update()
36852         }
36853         
36854         this.fireEvent('initial', this);
36855     },
36856     
36857     update : function()
36858     {
36859         this.imageEl.attr('src', this.files[this.indicator - 1]);
36860         
36861         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36862         
36863         this.prevIndicator.show();
36864         
36865         if(this.indicator == 1){
36866             this.prevIndicator.hide();
36867         }
36868         
36869         this.nextIndicator.show();
36870         
36871         if(this.indicator == this.files.length){
36872             this.nextIndicator.hide();
36873         }
36874         
36875         this.thumbEl.scrollTo('top');
36876         
36877         this.fireEvent('update', this);
36878     },
36879     
36880     onClick : function(e)
36881     {
36882         e.preventDefault();
36883         
36884         this.fireEvent('click', this);
36885     },
36886     
36887     prev : function(e)
36888     {
36889         e.preventDefault();
36890         
36891         this.indicator = Math.max(1, this.indicator - 1);
36892         
36893         this.update();
36894     },
36895     
36896     next : function(e)
36897     {
36898         e.preventDefault();
36899         
36900         this.indicator = Math.min(this.files.length, this.indicator + 1);
36901         
36902         this.update();
36903     }
36904 });
36905 /*
36906  * - LGPL
36907  *
36908  * RadioSet
36909  *
36910  *
36911  */
36912
36913 /**
36914  * @class Roo.bootstrap.RadioSet
36915  * @extends Roo.bootstrap.Input
36916  * Bootstrap RadioSet class
36917  * @cfg {String} indicatorpos (left|right) default left
36918  * @cfg {Boolean} inline (true|false) inline the element (default true)
36919  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36920  * @constructor
36921  * Create a new RadioSet
36922  * @param {Object} config The config object
36923  */
36924
36925 Roo.bootstrap.RadioSet = function(config){
36926     
36927     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36928     
36929     this.radioes = [];
36930     
36931     Roo.bootstrap.RadioSet.register(this);
36932     
36933     this.addEvents({
36934         /**
36935         * @event check
36936         * Fires when the element is checked or unchecked.
36937         * @param {Roo.bootstrap.RadioSet} this This radio
36938         * @param {Roo.bootstrap.Radio} item The checked item
36939         */
36940        check : true,
36941        /**
36942         * @event click
36943         * Fires when the element is click.
36944         * @param {Roo.bootstrap.RadioSet} this This radio set
36945         * @param {Roo.bootstrap.Radio} item The checked item
36946         * @param {Roo.EventObject} e The event object
36947         */
36948        click : true
36949     });
36950     
36951 };
36952
36953 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36954
36955     radioes : false,
36956     
36957     inline : true,
36958     
36959     weight : '',
36960     
36961     indicatorpos : 'left',
36962     
36963     getAutoCreate : function()
36964     {
36965         var label = {
36966             tag : 'label',
36967             cls : 'roo-radio-set-label',
36968             cn : [
36969                 {
36970                     tag : 'span',
36971                     html : this.fieldLabel
36972                 }
36973             ]
36974         };
36975         if (Roo.bootstrap.version == 3) {
36976             
36977             
36978             if(this.indicatorpos == 'left'){
36979                 label.cn.unshift({
36980                     tag : 'i',
36981                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36982                     tooltip : 'This field is required'
36983                 });
36984             } else {
36985                 label.cn.push({
36986                     tag : 'i',
36987                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36988                     tooltip : 'This field is required'
36989                 });
36990             }
36991         }
36992         var items = {
36993             tag : 'div',
36994             cls : 'roo-radio-set-items'
36995         };
36996         
36997         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36998         
36999         if (align === 'left' && this.fieldLabel.length) {
37000             
37001             items = {
37002                 cls : "roo-radio-set-right", 
37003                 cn: [
37004                     items
37005                 ]
37006             };
37007             
37008             if(this.labelWidth > 12){
37009                 label.style = "width: " + this.labelWidth + 'px';
37010             }
37011             
37012             if(this.labelWidth < 13 && this.labelmd == 0){
37013                 this.labelmd = this.labelWidth;
37014             }
37015             
37016             if(this.labellg > 0){
37017                 label.cls += ' col-lg-' + this.labellg;
37018                 items.cls += ' col-lg-' + (12 - this.labellg);
37019             }
37020             
37021             if(this.labelmd > 0){
37022                 label.cls += ' col-md-' + this.labelmd;
37023                 items.cls += ' col-md-' + (12 - this.labelmd);
37024             }
37025             
37026             if(this.labelsm > 0){
37027                 label.cls += ' col-sm-' + this.labelsm;
37028                 items.cls += ' col-sm-' + (12 - this.labelsm);
37029             }
37030             
37031             if(this.labelxs > 0){
37032                 label.cls += ' col-xs-' + this.labelxs;
37033                 items.cls += ' col-xs-' + (12 - this.labelxs);
37034             }
37035         }
37036         
37037         var cfg = {
37038             tag : 'div',
37039             cls : 'roo-radio-set',
37040             cn : [
37041                 {
37042                     tag : 'input',
37043                     cls : 'roo-radio-set-input',
37044                     type : 'hidden',
37045                     name : this.name,
37046                     value : this.value ? this.value :  ''
37047                 },
37048                 label,
37049                 items
37050             ]
37051         };
37052         
37053         if(this.weight.length){
37054             cfg.cls += ' roo-radio-' + this.weight;
37055         }
37056         
37057         if(this.inline) {
37058             cfg.cls += ' roo-radio-set-inline';
37059         }
37060         
37061         var settings=this;
37062         ['xs','sm','md','lg'].map(function(size){
37063             if (settings[size]) {
37064                 cfg.cls += ' col-' + size + '-' + settings[size];
37065             }
37066         });
37067         
37068         return cfg;
37069         
37070     },
37071
37072     initEvents : function()
37073     {
37074         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37075         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37076         
37077         if(!this.fieldLabel.length){
37078             this.labelEl.hide();
37079         }
37080         
37081         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37082         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37083         
37084         this.indicator = this.indicatorEl();
37085         
37086         if(this.indicator){
37087             this.indicator.addClass('invisible');
37088         }
37089         
37090         this.originalValue = this.getValue();
37091         
37092     },
37093     
37094     inputEl: function ()
37095     {
37096         return this.el.select('.roo-radio-set-input', true).first();
37097     },
37098     
37099     getChildContainer : function()
37100     {
37101         return this.itemsEl;
37102     },
37103     
37104     register : function(item)
37105     {
37106         this.radioes.push(item);
37107         
37108     },
37109     
37110     validate : function()
37111     {   
37112         if(this.getVisibilityEl().hasClass('hidden')){
37113             return true;
37114         }
37115         
37116         var valid = false;
37117         
37118         Roo.each(this.radioes, function(i){
37119             if(!i.checked){
37120                 return;
37121             }
37122             
37123             valid = true;
37124             return false;
37125         });
37126         
37127         if(this.allowBlank) {
37128             return true;
37129         }
37130         
37131         if(this.disabled || valid){
37132             this.markValid();
37133             return true;
37134         }
37135         
37136         this.markInvalid();
37137         return false;
37138         
37139     },
37140     
37141     markValid : function()
37142     {
37143         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37144             this.indicatorEl().removeClass('visible');
37145             this.indicatorEl().addClass('invisible');
37146         }
37147         
37148         
37149         if (Roo.bootstrap.version == 3) {
37150             this.el.removeClass([this.invalidClass, this.validClass]);
37151             this.el.addClass(this.validClass);
37152         } else {
37153             this.el.removeClass(['is-invalid','is-valid']);
37154             this.el.addClass(['is-valid']);
37155         }
37156         this.fireEvent('valid', this);
37157     },
37158     
37159     markInvalid : function(msg)
37160     {
37161         if(this.allowBlank || this.disabled){
37162             return;
37163         }
37164         
37165         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37166             this.indicatorEl().removeClass('invisible');
37167             this.indicatorEl().addClass('visible');
37168         }
37169         if (Roo.bootstrap.version == 3) {
37170             this.el.removeClass([this.invalidClass, this.validClass]);
37171             this.el.addClass(this.invalidClass);
37172         } else {
37173             this.el.removeClass(['is-invalid','is-valid']);
37174             this.el.addClass(['is-invalid']);
37175         }
37176         
37177         this.fireEvent('invalid', this, msg);
37178         
37179     },
37180     
37181     setValue : function(v, suppressEvent)
37182     {   
37183         if(this.value === v){
37184             return;
37185         }
37186         
37187         this.value = v;
37188         
37189         if(this.rendered){
37190             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37191         }
37192         
37193         Roo.each(this.radioes, function(i){
37194             i.checked = false;
37195             i.el.removeClass('checked');
37196         });
37197         
37198         Roo.each(this.radioes, function(i){
37199             
37200             if(i.value === v || i.value.toString() === v.toString()){
37201                 i.checked = true;
37202                 i.el.addClass('checked');
37203                 
37204                 if(suppressEvent !== true){
37205                     this.fireEvent('check', this, i);
37206                 }
37207                 
37208                 return false;
37209             }
37210             
37211         }, this);
37212         
37213         this.validate();
37214     },
37215     
37216     clearInvalid : function(){
37217         
37218         if(!this.el || this.preventMark){
37219             return;
37220         }
37221         
37222         this.el.removeClass([this.invalidClass]);
37223         
37224         this.fireEvent('valid', this);
37225     }
37226     
37227 });
37228
37229 Roo.apply(Roo.bootstrap.RadioSet, {
37230     
37231     groups: {},
37232     
37233     register : function(set)
37234     {
37235         this.groups[set.name] = set;
37236     },
37237     
37238     get: function(name) 
37239     {
37240         if (typeof(this.groups[name]) == 'undefined') {
37241             return false;
37242         }
37243         
37244         return this.groups[name] ;
37245     }
37246     
37247 });
37248 /*
37249  * Based on:
37250  * Ext JS Library 1.1.1
37251  * Copyright(c) 2006-2007, Ext JS, LLC.
37252  *
37253  * Originally Released Under LGPL - original licence link has changed is not relivant.
37254  *
37255  * Fork - LGPL
37256  * <script type="text/javascript">
37257  */
37258
37259
37260 /**
37261  * @class Roo.bootstrap.SplitBar
37262  * @extends Roo.util.Observable
37263  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37264  * <br><br>
37265  * Usage:
37266  * <pre><code>
37267 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37268                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37269 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37270 split.minSize = 100;
37271 split.maxSize = 600;
37272 split.animate = true;
37273 split.on('moved', splitterMoved);
37274 </code></pre>
37275  * @constructor
37276  * Create a new SplitBar
37277  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37278  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37279  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37280  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37281                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37282                         position of the SplitBar).
37283  */
37284 Roo.bootstrap.SplitBar = function(cfg){
37285     
37286     /** @private */
37287     
37288     //{
37289     //  dragElement : elm
37290     //  resizingElement: el,
37291         // optional..
37292     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37293     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37294         // existingProxy ???
37295     //}
37296     
37297     this.el = Roo.get(cfg.dragElement, true);
37298     this.el.dom.unselectable = "on";
37299     /** @private */
37300     this.resizingEl = Roo.get(cfg.resizingElement, true);
37301
37302     /**
37303      * @private
37304      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37305      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37306      * @type Number
37307      */
37308     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37309     
37310     /**
37311      * The minimum size of the resizing element. (Defaults to 0)
37312      * @type Number
37313      */
37314     this.minSize = 0;
37315     
37316     /**
37317      * The maximum size of the resizing element. (Defaults to 2000)
37318      * @type Number
37319      */
37320     this.maxSize = 2000;
37321     
37322     /**
37323      * Whether to animate the transition to the new size
37324      * @type Boolean
37325      */
37326     this.animate = false;
37327     
37328     /**
37329      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37330      * @type Boolean
37331      */
37332     this.useShim = false;
37333     
37334     /** @private */
37335     this.shim = null;
37336     
37337     if(!cfg.existingProxy){
37338         /** @private */
37339         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37340     }else{
37341         this.proxy = Roo.get(cfg.existingProxy).dom;
37342     }
37343     /** @private */
37344     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37345     
37346     /** @private */
37347     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37348     
37349     /** @private */
37350     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37351     
37352     /** @private */
37353     this.dragSpecs = {};
37354     
37355     /**
37356      * @private The adapter to use to positon and resize elements
37357      */
37358     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37359     this.adapter.init(this);
37360     
37361     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37362         /** @private */
37363         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37364         this.el.addClass("roo-splitbar-h");
37365     }else{
37366         /** @private */
37367         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37368         this.el.addClass("roo-splitbar-v");
37369     }
37370     
37371     this.addEvents({
37372         /**
37373          * @event resize
37374          * Fires when the splitter is moved (alias for {@link #event-moved})
37375          * @param {Roo.bootstrap.SplitBar} this
37376          * @param {Number} newSize the new width or height
37377          */
37378         "resize" : true,
37379         /**
37380          * @event moved
37381          * Fires when the splitter is moved
37382          * @param {Roo.bootstrap.SplitBar} this
37383          * @param {Number} newSize the new width or height
37384          */
37385         "moved" : true,
37386         /**
37387          * @event beforeresize
37388          * Fires before the splitter is dragged
37389          * @param {Roo.bootstrap.SplitBar} this
37390          */
37391         "beforeresize" : true,
37392
37393         "beforeapply" : true
37394     });
37395
37396     Roo.util.Observable.call(this);
37397 };
37398
37399 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37400     onStartProxyDrag : function(x, y){
37401         this.fireEvent("beforeresize", this);
37402         if(!this.overlay){
37403             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37404             o.unselectable();
37405             o.enableDisplayMode("block");
37406             // all splitbars share the same overlay
37407             Roo.bootstrap.SplitBar.prototype.overlay = o;
37408         }
37409         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37410         this.overlay.show();
37411         Roo.get(this.proxy).setDisplayed("block");
37412         var size = this.adapter.getElementSize(this);
37413         this.activeMinSize = this.getMinimumSize();;
37414         this.activeMaxSize = this.getMaximumSize();;
37415         var c1 = size - this.activeMinSize;
37416         var c2 = Math.max(this.activeMaxSize - size, 0);
37417         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37418             this.dd.resetConstraints();
37419             this.dd.setXConstraint(
37420                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37421                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37422             );
37423             this.dd.setYConstraint(0, 0);
37424         }else{
37425             this.dd.resetConstraints();
37426             this.dd.setXConstraint(0, 0);
37427             this.dd.setYConstraint(
37428                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37429                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37430             );
37431          }
37432         this.dragSpecs.startSize = size;
37433         this.dragSpecs.startPoint = [x, y];
37434         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37435     },
37436     
37437     /** 
37438      * @private Called after the drag operation by the DDProxy
37439      */
37440     onEndProxyDrag : function(e){
37441         Roo.get(this.proxy).setDisplayed(false);
37442         var endPoint = Roo.lib.Event.getXY(e);
37443         if(this.overlay){
37444             this.overlay.hide();
37445         }
37446         var newSize;
37447         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37448             newSize = this.dragSpecs.startSize + 
37449                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37450                     endPoint[0] - this.dragSpecs.startPoint[0] :
37451                     this.dragSpecs.startPoint[0] - endPoint[0]
37452                 );
37453         }else{
37454             newSize = this.dragSpecs.startSize + 
37455                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37456                     endPoint[1] - this.dragSpecs.startPoint[1] :
37457                     this.dragSpecs.startPoint[1] - endPoint[1]
37458                 );
37459         }
37460         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37461         if(newSize != this.dragSpecs.startSize){
37462             if(this.fireEvent('beforeapply', this, newSize) !== false){
37463                 this.adapter.setElementSize(this, newSize);
37464                 this.fireEvent("moved", this, newSize);
37465                 this.fireEvent("resize", this, newSize);
37466             }
37467         }
37468     },
37469     
37470     /**
37471      * Get the adapter this SplitBar uses
37472      * @return The adapter object
37473      */
37474     getAdapter : function(){
37475         return this.adapter;
37476     },
37477     
37478     /**
37479      * Set the adapter this SplitBar uses
37480      * @param {Object} adapter A SplitBar adapter object
37481      */
37482     setAdapter : function(adapter){
37483         this.adapter = adapter;
37484         this.adapter.init(this);
37485     },
37486     
37487     /**
37488      * Gets the minimum size for the resizing element
37489      * @return {Number} The minimum size
37490      */
37491     getMinimumSize : function(){
37492         return this.minSize;
37493     },
37494     
37495     /**
37496      * Sets the minimum size for the resizing element
37497      * @param {Number} minSize The minimum size
37498      */
37499     setMinimumSize : function(minSize){
37500         this.minSize = minSize;
37501     },
37502     
37503     /**
37504      * Gets the maximum size for the resizing element
37505      * @return {Number} The maximum size
37506      */
37507     getMaximumSize : function(){
37508         return this.maxSize;
37509     },
37510     
37511     /**
37512      * Sets the maximum size for the resizing element
37513      * @param {Number} maxSize The maximum size
37514      */
37515     setMaximumSize : function(maxSize){
37516         this.maxSize = maxSize;
37517     },
37518     
37519     /**
37520      * Sets the initialize size for the resizing element
37521      * @param {Number} size The initial size
37522      */
37523     setCurrentSize : function(size){
37524         var oldAnimate = this.animate;
37525         this.animate = false;
37526         this.adapter.setElementSize(this, size);
37527         this.animate = oldAnimate;
37528     },
37529     
37530     /**
37531      * Destroy this splitbar. 
37532      * @param {Boolean} removeEl True to remove the element
37533      */
37534     destroy : function(removeEl){
37535         if(this.shim){
37536             this.shim.remove();
37537         }
37538         this.dd.unreg();
37539         this.proxy.parentNode.removeChild(this.proxy);
37540         if(removeEl){
37541             this.el.remove();
37542         }
37543     }
37544 });
37545
37546 /**
37547  * @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.
37548  */
37549 Roo.bootstrap.SplitBar.createProxy = function(dir){
37550     var proxy = new Roo.Element(document.createElement("div"));
37551     proxy.unselectable();
37552     var cls = 'roo-splitbar-proxy';
37553     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37554     document.body.appendChild(proxy.dom);
37555     return proxy.dom;
37556 };
37557
37558 /** 
37559  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37560  * Default Adapter. It assumes the splitter and resizing element are not positioned
37561  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37562  */
37563 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37564 };
37565
37566 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37567     // do nothing for now
37568     init : function(s){
37569     
37570     },
37571     /**
37572      * Called before drag operations to get the current size of the resizing element. 
37573      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37574      */
37575      getElementSize : function(s){
37576         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37577             return s.resizingEl.getWidth();
37578         }else{
37579             return s.resizingEl.getHeight();
37580         }
37581     },
37582     
37583     /**
37584      * Called after drag operations to set the size of the resizing element.
37585      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37586      * @param {Number} newSize The new size to set
37587      * @param {Function} onComplete A function to be invoked when resizing is complete
37588      */
37589     setElementSize : function(s, newSize, onComplete){
37590         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37591             if(!s.animate){
37592                 s.resizingEl.setWidth(newSize);
37593                 if(onComplete){
37594                     onComplete(s, newSize);
37595                 }
37596             }else{
37597                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37598             }
37599         }else{
37600             
37601             if(!s.animate){
37602                 s.resizingEl.setHeight(newSize);
37603                 if(onComplete){
37604                     onComplete(s, newSize);
37605                 }
37606             }else{
37607                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37608             }
37609         }
37610     }
37611 };
37612
37613 /** 
37614  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37615  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37616  * Adapter that  moves the splitter element to align with the resized sizing element. 
37617  * Used with an absolute positioned SplitBar.
37618  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37619  * document.body, make sure you assign an id to the body element.
37620  */
37621 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37622     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37623     this.container = Roo.get(container);
37624 };
37625
37626 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37627     init : function(s){
37628         this.basic.init(s);
37629     },
37630     
37631     getElementSize : function(s){
37632         return this.basic.getElementSize(s);
37633     },
37634     
37635     setElementSize : function(s, newSize, onComplete){
37636         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37637     },
37638     
37639     moveSplitter : function(s){
37640         var yes = Roo.bootstrap.SplitBar;
37641         switch(s.placement){
37642             case yes.LEFT:
37643                 s.el.setX(s.resizingEl.getRight());
37644                 break;
37645             case yes.RIGHT:
37646                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37647                 break;
37648             case yes.TOP:
37649                 s.el.setY(s.resizingEl.getBottom());
37650                 break;
37651             case yes.BOTTOM:
37652                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37653                 break;
37654         }
37655     }
37656 };
37657
37658 /**
37659  * Orientation constant - Create a vertical SplitBar
37660  * @static
37661  * @type Number
37662  */
37663 Roo.bootstrap.SplitBar.VERTICAL = 1;
37664
37665 /**
37666  * Orientation constant - Create a horizontal SplitBar
37667  * @static
37668  * @type Number
37669  */
37670 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37671
37672 /**
37673  * Placement constant - The resizing element is to the left of the splitter element
37674  * @static
37675  * @type Number
37676  */
37677 Roo.bootstrap.SplitBar.LEFT = 1;
37678
37679 /**
37680  * Placement constant - The resizing element is to the right of the splitter element
37681  * @static
37682  * @type Number
37683  */
37684 Roo.bootstrap.SplitBar.RIGHT = 2;
37685
37686 /**
37687  * Placement constant - The resizing element is positioned above the splitter element
37688  * @static
37689  * @type Number
37690  */
37691 Roo.bootstrap.SplitBar.TOP = 3;
37692
37693 /**
37694  * Placement constant - The resizing element is positioned under splitter element
37695  * @static
37696  * @type Number
37697  */
37698 Roo.bootstrap.SplitBar.BOTTOM = 4;
37699 Roo.namespace("Roo.bootstrap.layout");/*
37700  * Based on:
37701  * Ext JS Library 1.1.1
37702  * Copyright(c) 2006-2007, Ext JS, LLC.
37703  *
37704  * Originally Released Under LGPL - original licence link has changed is not relivant.
37705  *
37706  * Fork - LGPL
37707  * <script type="text/javascript">
37708  */
37709
37710 /**
37711  * @class Roo.bootstrap.layout.Manager
37712  * @extends Roo.bootstrap.Component
37713  * Base class for layout managers.
37714  */
37715 Roo.bootstrap.layout.Manager = function(config)
37716 {
37717     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37718
37719
37720
37721
37722
37723     /** false to disable window resize monitoring @type Boolean */
37724     this.monitorWindowResize = true;
37725     this.regions = {};
37726     this.addEvents({
37727         /**
37728          * @event layout
37729          * Fires when a layout is performed.
37730          * @param {Roo.LayoutManager} this
37731          */
37732         "layout" : true,
37733         /**
37734          * @event regionresized
37735          * Fires when the user resizes a region.
37736          * @param {Roo.LayoutRegion} region The resized region
37737          * @param {Number} newSize The new size (width for east/west, height for north/south)
37738          */
37739         "regionresized" : true,
37740         /**
37741          * @event regioncollapsed
37742          * Fires when a region is collapsed.
37743          * @param {Roo.LayoutRegion} region The collapsed region
37744          */
37745         "regioncollapsed" : true,
37746         /**
37747          * @event regionexpanded
37748          * Fires when a region is expanded.
37749          * @param {Roo.LayoutRegion} region The expanded region
37750          */
37751         "regionexpanded" : true
37752     });
37753     this.updating = false;
37754
37755     if (config.el) {
37756         this.el = Roo.get(config.el);
37757         this.initEvents();
37758     }
37759
37760 };
37761
37762 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37763
37764
37765     regions : null,
37766
37767     monitorWindowResize : true,
37768
37769
37770     updating : false,
37771
37772
37773     onRender : function(ct, position)
37774     {
37775         if(!this.el){
37776             this.el = Roo.get(ct);
37777             this.initEvents();
37778         }
37779         //this.fireEvent('render',this);
37780     },
37781
37782
37783     initEvents: function()
37784     {
37785
37786
37787         // ie scrollbar fix
37788         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37789             document.body.scroll = "no";
37790         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37791             this.el.position('relative');
37792         }
37793         this.id = this.el.id;
37794         this.el.addClass("roo-layout-container");
37795         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37796         if(this.el.dom != document.body ) {
37797             this.el.on('resize', this.layout,this);
37798             this.el.on('show', this.layout,this);
37799         }
37800
37801     },
37802
37803     /**
37804      * Returns true if this layout is currently being updated
37805      * @return {Boolean}
37806      */
37807     isUpdating : function(){
37808         return this.updating;
37809     },
37810
37811     /**
37812      * Suspend the LayoutManager from doing auto-layouts while
37813      * making multiple add or remove calls
37814      */
37815     beginUpdate : function(){
37816         this.updating = true;
37817     },
37818
37819     /**
37820      * Restore auto-layouts and optionally disable the manager from performing a layout
37821      * @param {Boolean} noLayout true to disable a layout update
37822      */
37823     endUpdate : function(noLayout){
37824         this.updating = false;
37825         if(!noLayout){
37826             this.layout();
37827         }
37828     },
37829
37830     layout: function(){
37831         // abstract...
37832     },
37833
37834     onRegionResized : function(region, newSize){
37835         this.fireEvent("regionresized", region, newSize);
37836         this.layout();
37837     },
37838
37839     onRegionCollapsed : function(region){
37840         this.fireEvent("regioncollapsed", region);
37841     },
37842
37843     onRegionExpanded : function(region){
37844         this.fireEvent("regionexpanded", region);
37845     },
37846
37847     /**
37848      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37849      * performs box-model adjustments.
37850      * @return {Object} The size as an object {width: (the width), height: (the height)}
37851      */
37852     getViewSize : function()
37853     {
37854         var size;
37855         if(this.el.dom != document.body){
37856             size = this.el.getSize();
37857         }else{
37858             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37859         }
37860         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37861         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37862         return size;
37863     },
37864
37865     /**
37866      * Returns the Element this layout is bound to.
37867      * @return {Roo.Element}
37868      */
37869     getEl : function(){
37870         return this.el;
37871     },
37872
37873     /**
37874      * Returns the specified region.
37875      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37876      * @return {Roo.LayoutRegion}
37877      */
37878     getRegion : function(target){
37879         return this.regions[target.toLowerCase()];
37880     },
37881
37882     onWindowResize : function(){
37883         if(this.monitorWindowResize){
37884             this.layout();
37885         }
37886     }
37887 });
37888 /*
37889  * Based on:
37890  * Ext JS Library 1.1.1
37891  * Copyright(c) 2006-2007, Ext JS, LLC.
37892  *
37893  * Originally Released Under LGPL - original licence link has changed is not relivant.
37894  *
37895  * Fork - LGPL
37896  * <script type="text/javascript">
37897  */
37898 /**
37899  * @class Roo.bootstrap.layout.Border
37900  * @extends Roo.bootstrap.layout.Manager
37901  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37902  * please see: examples/bootstrap/nested.html<br><br>
37903  
37904 <b>The container the layout is rendered into can be either the body element or any other element.
37905 If it is not the body element, the container needs to either be an absolute positioned element,
37906 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37907 the container size if it is not the body element.</b>
37908
37909 * @constructor
37910 * Create a new Border
37911 * @param {Object} config Configuration options
37912  */
37913 Roo.bootstrap.layout.Border = function(config){
37914     config = config || {};
37915     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37916     
37917     
37918     
37919     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37920         if(config[region]){
37921             config[region].region = region;
37922             this.addRegion(config[region]);
37923         }
37924     },this);
37925     
37926 };
37927
37928 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37929
37930 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37931     
37932     parent : false, // this might point to a 'nest' or a ???
37933     
37934     /**
37935      * Creates and adds a new region if it doesn't already exist.
37936      * @param {String} target The target region key (north, south, east, west or center).
37937      * @param {Object} config The regions config object
37938      * @return {BorderLayoutRegion} The new region
37939      */
37940     addRegion : function(config)
37941     {
37942         if(!this.regions[config.region]){
37943             var r = this.factory(config);
37944             this.bindRegion(r);
37945         }
37946         return this.regions[config.region];
37947     },
37948
37949     // private (kinda)
37950     bindRegion : function(r){
37951         this.regions[r.config.region] = r;
37952         
37953         r.on("visibilitychange",    this.layout, this);
37954         r.on("paneladded",          this.layout, this);
37955         r.on("panelremoved",        this.layout, this);
37956         r.on("invalidated",         this.layout, this);
37957         r.on("resized",             this.onRegionResized, this);
37958         r.on("collapsed",           this.onRegionCollapsed, this);
37959         r.on("expanded",            this.onRegionExpanded, this);
37960     },
37961
37962     /**
37963      * Performs a layout update.
37964      */
37965     layout : function()
37966     {
37967         if(this.updating) {
37968             return;
37969         }
37970         
37971         // render all the rebions if they have not been done alreayd?
37972         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37973             if(this.regions[region] && !this.regions[region].bodyEl){
37974                 this.regions[region].onRender(this.el)
37975             }
37976         },this);
37977         
37978         var size = this.getViewSize();
37979         var w = size.width;
37980         var h = size.height;
37981         var centerW = w;
37982         var centerH = h;
37983         var centerY = 0;
37984         var centerX = 0;
37985         //var x = 0, y = 0;
37986
37987         var rs = this.regions;
37988         var north = rs["north"];
37989         var south = rs["south"]; 
37990         var west = rs["west"];
37991         var east = rs["east"];
37992         var center = rs["center"];
37993         //if(this.hideOnLayout){ // not supported anymore
37994             //c.el.setStyle("display", "none");
37995         //}
37996         if(north && north.isVisible()){
37997             var b = north.getBox();
37998             var m = north.getMargins();
37999             b.width = w - (m.left+m.right);
38000             b.x = m.left;
38001             b.y = m.top;
38002             centerY = b.height + b.y + m.bottom;
38003             centerH -= centerY;
38004             north.updateBox(this.safeBox(b));
38005         }
38006         if(south && south.isVisible()){
38007             var b = south.getBox();
38008             var m = south.getMargins();
38009             b.width = w - (m.left+m.right);
38010             b.x = m.left;
38011             var totalHeight = (b.height + m.top + m.bottom);
38012             b.y = h - totalHeight + m.top;
38013             centerH -= totalHeight;
38014             south.updateBox(this.safeBox(b));
38015         }
38016         if(west && west.isVisible()){
38017             var b = west.getBox();
38018             var m = west.getMargins();
38019             b.height = centerH - (m.top+m.bottom);
38020             b.x = m.left;
38021             b.y = centerY + m.top;
38022             var totalWidth = (b.width + m.left + m.right);
38023             centerX += totalWidth;
38024             centerW -= totalWidth;
38025             west.updateBox(this.safeBox(b));
38026         }
38027         if(east && east.isVisible()){
38028             var b = east.getBox();
38029             var m = east.getMargins();
38030             b.height = centerH - (m.top+m.bottom);
38031             var totalWidth = (b.width + m.left + m.right);
38032             b.x = w - totalWidth + m.left;
38033             b.y = centerY + m.top;
38034             centerW -= totalWidth;
38035             east.updateBox(this.safeBox(b));
38036         }
38037         if(center){
38038             var m = center.getMargins();
38039             var centerBox = {
38040                 x: centerX + m.left,
38041                 y: centerY + m.top,
38042                 width: centerW - (m.left+m.right),
38043                 height: centerH - (m.top+m.bottom)
38044             };
38045             //if(this.hideOnLayout){
38046                 //center.el.setStyle("display", "block");
38047             //}
38048             center.updateBox(this.safeBox(centerBox));
38049         }
38050         this.el.repaint();
38051         this.fireEvent("layout", this);
38052     },
38053
38054     // private
38055     safeBox : function(box){
38056         box.width = Math.max(0, box.width);
38057         box.height = Math.max(0, box.height);
38058         return box;
38059     },
38060
38061     /**
38062      * Adds a ContentPanel (or subclass) to this layout.
38063      * @param {String} target The target region key (north, south, east, west or center).
38064      * @param {Roo.ContentPanel} panel The panel to add
38065      * @return {Roo.ContentPanel} The added panel
38066      */
38067     add : function(target, panel){
38068          
38069         target = target.toLowerCase();
38070         return this.regions[target].add(panel);
38071     },
38072
38073     /**
38074      * Remove a ContentPanel (or subclass) to this layout.
38075      * @param {String} target The target region key (north, south, east, west or center).
38076      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38077      * @return {Roo.ContentPanel} The removed panel
38078      */
38079     remove : function(target, panel){
38080         target = target.toLowerCase();
38081         return this.regions[target].remove(panel);
38082     },
38083
38084     /**
38085      * Searches all regions for a panel with the specified id
38086      * @param {String} panelId
38087      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38088      */
38089     findPanel : function(panelId){
38090         var rs = this.regions;
38091         for(var target in rs){
38092             if(typeof rs[target] != "function"){
38093                 var p = rs[target].getPanel(panelId);
38094                 if(p){
38095                     return p;
38096                 }
38097             }
38098         }
38099         return null;
38100     },
38101
38102     /**
38103      * Searches all regions for a panel with the specified id and activates (shows) it.
38104      * @param {String/ContentPanel} panelId The panels id or the panel itself
38105      * @return {Roo.ContentPanel} The shown panel or null
38106      */
38107     showPanel : function(panelId) {
38108       var rs = this.regions;
38109       for(var target in rs){
38110          var r = rs[target];
38111          if(typeof r != "function"){
38112             if(r.hasPanel(panelId)){
38113                return r.showPanel(panelId);
38114             }
38115          }
38116       }
38117       return null;
38118    },
38119
38120    /**
38121      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38122      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38123      */
38124    /*
38125     restoreState : function(provider){
38126         if(!provider){
38127             provider = Roo.state.Manager;
38128         }
38129         var sm = new Roo.LayoutStateManager();
38130         sm.init(this, provider);
38131     },
38132 */
38133  
38134  
38135     /**
38136      * Adds a xtype elements to the layout.
38137      * <pre><code>
38138
38139 layout.addxtype({
38140        xtype : 'ContentPanel',
38141        region: 'west',
38142        items: [ .... ]
38143    }
38144 );
38145
38146 layout.addxtype({
38147         xtype : 'NestedLayoutPanel',
38148         region: 'west',
38149         layout: {
38150            center: { },
38151            west: { }   
38152         },
38153         items : [ ... list of content panels or nested layout panels.. ]
38154    }
38155 );
38156 </code></pre>
38157      * @param {Object} cfg Xtype definition of item to add.
38158      */
38159     addxtype : function(cfg)
38160     {
38161         // basically accepts a pannel...
38162         // can accept a layout region..!?!?
38163         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38164         
38165         
38166         // theory?  children can only be panels??
38167         
38168         //if (!cfg.xtype.match(/Panel$/)) {
38169         //    return false;
38170         //}
38171         var ret = false;
38172         
38173         if (typeof(cfg.region) == 'undefined') {
38174             Roo.log("Failed to add Panel, region was not set");
38175             Roo.log(cfg);
38176             return false;
38177         }
38178         var region = cfg.region;
38179         delete cfg.region;
38180         
38181           
38182         var xitems = [];
38183         if (cfg.items) {
38184             xitems = cfg.items;
38185             delete cfg.items;
38186         }
38187         var nb = false;
38188         
38189         if ( region == 'center') {
38190             Roo.log("Center: " + cfg.title);
38191         }
38192         
38193         
38194         switch(cfg.xtype) 
38195         {
38196             case 'Content':  // ContentPanel (el, cfg)
38197             case 'Scroll':  // ContentPanel (el, cfg)
38198             case 'View': 
38199                 cfg.autoCreate = cfg.autoCreate || true;
38200                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38201                 //} else {
38202                 //    var el = this.el.createChild();
38203                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38204                 //}
38205                 
38206                 this.add(region, ret);
38207                 break;
38208             
38209             /*
38210             case 'TreePanel': // our new panel!
38211                 cfg.el = this.el.createChild();
38212                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38213                 this.add(region, ret);
38214                 break;
38215             */
38216             
38217             case 'Nest': 
38218                 // create a new Layout (which is  a Border Layout...
38219                 
38220                 var clayout = cfg.layout;
38221                 clayout.el  = this.el.createChild();
38222                 clayout.items   = clayout.items  || [];
38223                 
38224                 delete cfg.layout;
38225                 
38226                 // replace this exitems with the clayout ones..
38227                 xitems = clayout.items;
38228                  
38229                 // force background off if it's in center...
38230                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38231                     cfg.background = false;
38232                 }
38233                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38234                 
38235                 
38236                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38237                 //console.log('adding nested layout panel '  + cfg.toSource());
38238                 this.add(region, ret);
38239                 nb = {}; /// find first...
38240                 break;
38241             
38242             case 'Grid':
38243                 
38244                 // needs grid and region
38245                 
38246                 //var el = this.getRegion(region).el.createChild();
38247                 /*
38248                  *var el = this.el.createChild();
38249                 // create the grid first...
38250                 cfg.grid.container = el;
38251                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38252                 */
38253                 
38254                 if (region == 'center' && this.active ) {
38255                     cfg.background = false;
38256                 }
38257                 
38258                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38259                 
38260                 this.add(region, ret);
38261                 /*
38262                 if (cfg.background) {
38263                     // render grid on panel activation (if panel background)
38264                     ret.on('activate', function(gp) {
38265                         if (!gp.grid.rendered) {
38266                     //        gp.grid.render(el);
38267                         }
38268                     });
38269                 } else {
38270                   //  cfg.grid.render(el);
38271                 }
38272                 */
38273                 break;
38274            
38275            
38276             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38277                 // it was the old xcomponent building that caused this before.
38278                 // espeically if border is the top element in the tree.
38279                 ret = this;
38280                 break; 
38281                 
38282                     
38283                 
38284                 
38285                 
38286             default:
38287                 /*
38288                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38289                     
38290                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38291                     this.add(region, ret);
38292                 } else {
38293                 */
38294                     Roo.log(cfg);
38295                     throw "Can not add '" + cfg.xtype + "' to Border";
38296                     return null;
38297              
38298                                 
38299              
38300         }
38301         this.beginUpdate();
38302         // add children..
38303         var region = '';
38304         var abn = {};
38305         Roo.each(xitems, function(i)  {
38306             region = nb && i.region ? i.region : false;
38307             
38308             var add = ret.addxtype(i);
38309            
38310             if (region) {
38311                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38312                 if (!i.background) {
38313                     abn[region] = nb[region] ;
38314                 }
38315             }
38316             
38317         });
38318         this.endUpdate();
38319
38320         // make the last non-background panel active..
38321         //if (nb) { Roo.log(abn); }
38322         if (nb) {
38323             
38324             for(var r in abn) {
38325                 region = this.getRegion(r);
38326                 if (region) {
38327                     // tried using nb[r], but it does not work..
38328                      
38329                     region.showPanel(abn[r]);
38330                    
38331                 }
38332             }
38333         }
38334         return ret;
38335         
38336     },
38337     
38338     
38339 // private
38340     factory : function(cfg)
38341     {
38342         
38343         var validRegions = Roo.bootstrap.layout.Border.regions;
38344
38345         var target = cfg.region;
38346         cfg.mgr = this;
38347         
38348         var r = Roo.bootstrap.layout;
38349         Roo.log(target);
38350         switch(target){
38351             case "north":
38352                 return new r.North(cfg);
38353             case "south":
38354                 return new r.South(cfg);
38355             case "east":
38356                 return new r.East(cfg);
38357             case "west":
38358                 return new r.West(cfg);
38359             case "center":
38360                 return new r.Center(cfg);
38361         }
38362         throw 'Layout region "'+target+'" not supported.';
38363     }
38364     
38365     
38366 });
38367  /*
38368  * Based on:
38369  * Ext JS Library 1.1.1
38370  * Copyright(c) 2006-2007, Ext JS, LLC.
38371  *
38372  * Originally Released Under LGPL - original licence link has changed is not relivant.
38373  *
38374  * Fork - LGPL
38375  * <script type="text/javascript">
38376  */
38377  
38378 /**
38379  * @class Roo.bootstrap.layout.Basic
38380  * @extends Roo.util.Observable
38381  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38382  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38383  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38384  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38385  * @cfg {string}   region  the region that it inhabits..
38386  * @cfg {bool}   skipConfig skip config?
38387  * 
38388
38389  */
38390 Roo.bootstrap.layout.Basic = function(config){
38391     
38392     this.mgr = config.mgr;
38393     
38394     this.position = config.region;
38395     
38396     var skipConfig = config.skipConfig;
38397     
38398     this.events = {
38399         /**
38400          * @scope Roo.BasicLayoutRegion
38401          */
38402         
38403         /**
38404          * @event beforeremove
38405          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38406          * @param {Roo.LayoutRegion} this
38407          * @param {Roo.ContentPanel} panel The panel
38408          * @param {Object} e The cancel event object
38409          */
38410         "beforeremove" : true,
38411         /**
38412          * @event invalidated
38413          * Fires when the layout for this region is changed.
38414          * @param {Roo.LayoutRegion} this
38415          */
38416         "invalidated" : true,
38417         /**
38418          * @event visibilitychange
38419          * Fires when this region is shown or hidden 
38420          * @param {Roo.LayoutRegion} this
38421          * @param {Boolean} visibility true or false
38422          */
38423         "visibilitychange" : true,
38424         /**
38425          * @event paneladded
38426          * Fires when a panel is added. 
38427          * @param {Roo.LayoutRegion} this
38428          * @param {Roo.ContentPanel} panel The panel
38429          */
38430         "paneladded" : true,
38431         /**
38432          * @event panelremoved
38433          * Fires when a panel is removed. 
38434          * @param {Roo.LayoutRegion} this
38435          * @param {Roo.ContentPanel} panel The panel
38436          */
38437         "panelremoved" : true,
38438         /**
38439          * @event beforecollapse
38440          * Fires when this region before collapse.
38441          * @param {Roo.LayoutRegion} this
38442          */
38443         "beforecollapse" : true,
38444         /**
38445          * @event collapsed
38446          * Fires when this region is collapsed.
38447          * @param {Roo.LayoutRegion} this
38448          */
38449         "collapsed" : true,
38450         /**
38451          * @event expanded
38452          * Fires when this region is expanded.
38453          * @param {Roo.LayoutRegion} this
38454          */
38455         "expanded" : true,
38456         /**
38457          * @event slideshow
38458          * Fires when this region is slid into view.
38459          * @param {Roo.LayoutRegion} this
38460          */
38461         "slideshow" : true,
38462         /**
38463          * @event slidehide
38464          * Fires when this region slides out of view. 
38465          * @param {Roo.LayoutRegion} this
38466          */
38467         "slidehide" : true,
38468         /**
38469          * @event panelactivated
38470          * Fires when a panel is activated. 
38471          * @param {Roo.LayoutRegion} this
38472          * @param {Roo.ContentPanel} panel The activated panel
38473          */
38474         "panelactivated" : true,
38475         /**
38476          * @event resized
38477          * Fires when the user resizes this region. 
38478          * @param {Roo.LayoutRegion} this
38479          * @param {Number} newSize The new size (width for east/west, height for north/south)
38480          */
38481         "resized" : true
38482     };
38483     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38484     this.panels = new Roo.util.MixedCollection();
38485     this.panels.getKey = this.getPanelId.createDelegate(this);
38486     this.box = null;
38487     this.activePanel = null;
38488     // ensure listeners are added...
38489     
38490     if (config.listeners || config.events) {
38491         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38492             listeners : config.listeners || {},
38493             events : config.events || {}
38494         });
38495     }
38496     
38497     if(skipConfig !== true){
38498         this.applyConfig(config);
38499     }
38500 };
38501
38502 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38503 {
38504     getPanelId : function(p){
38505         return p.getId();
38506     },
38507     
38508     applyConfig : function(config){
38509         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38510         this.config = config;
38511         
38512     },
38513     
38514     /**
38515      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38516      * the width, for horizontal (north, south) the height.
38517      * @param {Number} newSize The new width or height
38518      */
38519     resizeTo : function(newSize){
38520         var el = this.el ? this.el :
38521                  (this.activePanel ? this.activePanel.getEl() : null);
38522         if(el){
38523             switch(this.position){
38524                 case "east":
38525                 case "west":
38526                     el.setWidth(newSize);
38527                     this.fireEvent("resized", this, newSize);
38528                 break;
38529                 case "north":
38530                 case "south":
38531                     el.setHeight(newSize);
38532                     this.fireEvent("resized", this, newSize);
38533                 break;                
38534             }
38535         }
38536     },
38537     
38538     getBox : function(){
38539         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38540     },
38541     
38542     getMargins : function(){
38543         return this.margins;
38544     },
38545     
38546     updateBox : function(box){
38547         this.box = box;
38548         var el = this.activePanel.getEl();
38549         el.dom.style.left = box.x + "px";
38550         el.dom.style.top = box.y + "px";
38551         this.activePanel.setSize(box.width, box.height);
38552     },
38553     
38554     /**
38555      * Returns the container element for this region.
38556      * @return {Roo.Element}
38557      */
38558     getEl : function(){
38559         return this.activePanel;
38560     },
38561     
38562     /**
38563      * Returns true if this region is currently visible.
38564      * @return {Boolean}
38565      */
38566     isVisible : function(){
38567         return this.activePanel ? true : false;
38568     },
38569     
38570     setActivePanel : function(panel){
38571         panel = this.getPanel(panel);
38572         if(this.activePanel && this.activePanel != panel){
38573             this.activePanel.setActiveState(false);
38574             this.activePanel.getEl().setLeftTop(-10000,-10000);
38575         }
38576         this.activePanel = panel;
38577         panel.setActiveState(true);
38578         if(this.box){
38579             panel.setSize(this.box.width, this.box.height);
38580         }
38581         this.fireEvent("panelactivated", this, panel);
38582         this.fireEvent("invalidated");
38583     },
38584     
38585     /**
38586      * Show the specified panel.
38587      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38588      * @return {Roo.ContentPanel} The shown panel or null
38589      */
38590     showPanel : function(panel){
38591         panel = this.getPanel(panel);
38592         if(panel){
38593             this.setActivePanel(panel);
38594         }
38595         return panel;
38596     },
38597     
38598     /**
38599      * Get the active panel for this region.
38600      * @return {Roo.ContentPanel} The active panel or null
38601      */
38602     getActivePanel : function(){
38603         return this.activePanel;
38604     },
38605     
38606     /**
38607      * Add the passed ContentPanel(s)
38608      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38609      * @return {Roo.ContentPanel} The panel added (if only one was added)
38610      */
38611     add : function(panel){
38612         if(arguments.length > 1){
38613             for(var i = 0, len = arguments.length; i < len; i++) {
38614                 this.add(arguments[i]);
38615             }
38616             return null;
38617         }
38618         if(this.hasPanel(panel)){
38619             this.showPanel(panel);
38620             return panel;
38621         }
38622         var el = panel.getEl();
38623         if(el.dom.parentNode != this.mgr.el.dom){
38624             this.mgr.el.dom.appendChild(el.dom);
38625         }
38626         if(panel.setRegion){
38627             panel.setRegion(this);
38628         }
38629         this.panels.add(panel);
38630         el.setStyle("position", "absolute");
38631         if(!panel.background){
38632             this.setActivePanel(panel);
38633             if(this.config.initialSize && this.panels.getCount()==1){
38634                 this.resizeTo(this.config.initialSize);
38635             }
38636         }
38637         this.fireEvent("paneladded", this, panel);
38638         return panel;
38639     },
38640     
38641     /**
38642      * Returns true if the panel is in this region.
38643      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38644      * @return {Boolean}
38645      */
38646     hasPanel : function(panel){
38647         if(typeof panel == "object"){ // must be panel obj
38648             panel = panel.getId();
38649         }
38650         return this.getPanel(panel) ? true : false;
38651     },
38652     
38653     /**
38654      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38655      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38656      * @param {Boolean} preservePanel Overrides the config preservePanel option
38657      * @return {Roo.ContentPanel} The panel that was removed
38658      */
38659     remove : function(panel, preservePanel){
38660         panel = this.getPanel(panel);
38661         if(!panel){
38662             return null;
38663         }
38664         var e = {};
38665         this.fireEvent("beforeremove", this, panel, e);
38666         if(e.cancel === true){
38667             return null;
38668         }
38669         var panelId = panel.getId();
38670         this.panels.removeKey(panelId);
38671         return panel;
38672     },
38673     
38674     /**
38675      * Returns the panel specified or null if it's not in this region.
38676      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38677      * @return {Roo.ContentPanel}
38678      */
38679     getPanel : function(id){
38680         if(typeof id == "object"){ // must be panel obj
38681             return id;
38682         }
38683         return this.panels.get(id);
38684     },
38685     
38686     /**
38687      * Returns this regions position (north/south/east/west/center).
38688      * @return {String} 
38689      */
38690     getPosition: function(){
38691         return this.position;    
38692     }
38693 });/*
38694  * Based on:
38695  * Ext JS Library 1.1.1
38696  * Copyright(c) 2006-2007, Ext JS, LLC.
38697  *
38698  * Originally Released Under LGPL - original licence link has changed is not relivant.
38699  *
38700  * Fork - LGPL
38701  * <script type="text/javascript">
38702  */
38703  
38704 /**
38705  * @class Roo.bootstrap.layout.Region
38706  * @extends Roo.bootstrap.layout.Basic
38707  * This class represents a region in a layout manager.
38708  
38709  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38710  * @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})
38711  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38712  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38713  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38714  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38715  * @cfg {String}    title           The title for the region (overrides panel titles)
38716  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38717  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38718  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38719  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38720  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38721  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38722  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38723  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38724  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38725  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38726
38727  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38728  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38729  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38730  * @cfg {Number}    width           For East/West panels
38731  * @cfg {Number}    height          For North/South panels
38732  * @cfg {Boolean}   split           To show the splitter
38733  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38734  * 
38735  * @cfg {string}   cls             Extra CSS classes to add to region
38736  * 
38737  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38738  * @cfg {string}   region  the region that it inhabits..
38739  *
38740
38741  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38742  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38743
38744  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38745  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38746  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38747  */
38748 Roo.bootstrap.layout.Region = function(config)
38749 {
38750     this.applyConfig(config);
38751
38752     var mgr = config.mgr;
38753     var pos = config.region;
38754     config.skipConfig = true;
38755     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38756     
38757     if (mgr.el) {
38758         this.onRender(mgr.el);   
38759     }
38760      
38761     this.visible = true;
38762     this.collapsed = false;
38763     this.unrendered_panels = [];
38764 };
38765
38766 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38767
38768     position: '', // set by wrapper (eg. north/south etc..)
38769     unrendered_panels : null,  // unrendered panels.
38770     
38771     tabPosition : false,
38772     
38773     mgr: false, // points to 'Border'
38774     
38775     
38776     createBody : function(){
38777         /** This region's body element 
38778         * @type Roo.Element */
38779         this.bodyEl = this.el.createChild({
38780                 tag: "div",
38781                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38782         });
38783     },
38784
38785     onRender: function(ctr, pos)
38786     {
38787         var dh = Roo.DomHelper;
38788         /** This region's container element 
38789         * @type Roo.Element */
38790         this.el = dh.append(ctr.dom, {
38791                 tag: "div",
38792                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38793             }, true);
38794         /** This region's title element 
38795         * @type Roo.Element */
38796     
38797         this.titleEl = dh.append(this.el.dom,  {
38798                 tag: "div",
38799                 unselectable: "on",
38800                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38801                 children:[
38802                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38803                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38804                 ]
38805             }, true);
38806         
38807         this.titleEl.enableDisplayMode();
38808         /** This region's title text element 
38809         * @type HTMLElement */
38810         this.titleTextEl = this.titleEl.dom.firstChild;
38811         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38812         /*
38813         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38814         this.closeBtn.enableDisplayMode();
38815         this.closeBtn.on("click", this.closeClicked, this);
38816         this.closeBtn.hide();
38817     */
38818         this.createBody(this.config);
38819         if(this.config.hideWhenEmpty){
38820             this.hide();
38821             this.on("paneladded", this.validateVisibility, this);
38822             this.on("panelremoved", this.validateVisibility, this);
38823         }
38824         if(this.autoScroll){
38825             this.bodyEl.setStyle("overflow", "auto");
38826         }else{
38827             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38828         }
38829         //if(c.titlebar !== false){
38830             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38831                 this.titleEl.hide();
38832             }else{
38833                 this.titleEl.show();
38834                 if(this.config.title){
38835                     this.titleTextEl.innerHTML = this.config.title;
38836                 }
38837             }
38838         //}
38839         if(this.config.collapsed){
38840             this.collapse(true);
38841         }
38842         if(this.config.hidden){
38843             this.hide();
38844         }
38845         
38846         if (this.unrendered_panels && this.unrendered_panels.length) {
38847             for (var i =0;i< this.unrendered_panels.length; i++) {
38848                 this.add(this.unrendered_panels[i]);
38849             }
38850             this.unrendered_panels = null;
38851             
38852         }
38853         
38854     },
38855     
38856     applyConfig : function(c)
38857     {
38858         /*
38859          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38860             var dh = Roo.DomHelper;
38861             if(c.titlebar !== false){
38862                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38863                 this.collapseBtn.on("click", this.collapse, this);
38864                 this.collapseBtn.enableDisplayMode();
38865                 /*
38866                 if(c.showPin === true || this.showPin){
38867                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38868                     this.stickBtn.enableDisplayMode();
38869                     this.stickBtn.on("click", this.expand, this);
38870                     this.stickBtn.hide();
38871                 }
38872                 
38873             }
38874             */
38875             /** This region's collapsed element
38876             * @type Roo.Element */
38877             /*
38878              *
38879             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38880                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38881             ]}, true);
38882             
38883             if(c.floatable !== false){
38884                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38885                this.collapsedEl.on("click", this.collapseClick, this);
38886             }
38887
38888             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38889                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38890                    id: "message", unselectable: "on", style:{"float":"left"}});
38891                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38892              }
38893             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38894             this.expandBtn.on("click", this.expand, this);
38895             
38896         }
38897         
38898         if(this.collapseBtn){
38899             this.collapseBtn.setVisible(c.collapsible == true);
38900         }
38901         
38902         this.cmargins = c.cmargins || this.cmargins ||
38903                          (this.position == "west" || this.position == "east" ?
38904                              {top: 0, left: 2, right:2, bottom: 0} :
38905                              {top: 2, left: 0, right:0, bottom: 2});
38906         */
38907         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38908         
38909         
38910         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38911         
38912         this.autoScroll = c.autoScroll || false;
38913         
38914         
38915        
38916         
38917         this.duration = c.duration || .30;
38918         this.slideDuration = c.slideDuration || .45;
38919         this.config = c;
38920        
38921     },
38922     /**
38923      * Returns true if this region is currently visible.
38924      * @return {Boolean}
38925      */
38926     isVisible : function(){
38927         return this.visible;
38928     },
38929
38930     /**
38931      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38932      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38933      */
38934     //setCollapsedTitle : function(title){
38935     //    title = title || "&#160;";
38936      //   if(this.collapsedTitleTextEl){
38937       //      this.collapsedTitleTextEl.innerHTML = title;
38938        // }
38939     //},
38940
38941     getBox : function(){
38942         var b;
38943       //  if(!this.collapsed){
38944             b = this.el.getBox(false, true);
38945        // }else{
38946           //  b = this.collapsedEl.getBox(false, true);
38947         //}
38948         return b;
38949     },
38950
38951     getMargins : function(){
38952         return this.margins;
38953         //return this.collapsed ? this.cmargins : this.margins;
38954     },
38955 /*
38956     highlight : function(){
38957         this.el.addClass("x-layout-panel-dragover");
38958     },
38959
38960     unhighlight : function(){
38961         this.el.removeClass("x-layout-panel-dragover");
38962     },
38963 */
38964     updateBox : function(box)
38965     {
38966         if (!this.bodyEl) {
38967             return; // not rendered yet..
38968         }
38969         
38970         this.box = box;
38971         if(!this.collapsed){
38972             this.el.dom.style.left = box.x + "px";
38973             this.el.dom.style.top = box.y + "px";
38974             this.updateBody(box.width, box.height);
38975         }else{
38976             this.collapsedEl.dom.style.left = box.x + "px";
38977             this.collapsedEl.dom.style.top = box.y + "px";
38978             this.collapsedEl.setSize(box.width, box.height);
38979         }
38980         if(this.tabs){
38981             this.tabs.autoSizeTabs();
38982         }
38983     },
38984
38985     updateBody : function(w, h)
38986     {
38987         if(w !== null){
38988             this.el.setWidth(w);
38989             w -= this.el.getBorderWidth("rl");
38990             if(this.config.adjustments){
38991                 w += this.config.adjustments[0];
38992             }
38993         }
38994         if(h !== null && h > 0){
38995             this.el.setHeight(h);
38996             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38997             h -= this.el.getBorderWidth("tb");
38998             if(this.config.adjustments){
38999                 h += this.config.adjustments[1];
39000             }
39001             this.bodyEl.setHeight(h);
39002             if(this.tabs){
39003                 h = this.tabs.syncHeight(h);
39004             }
39005         }
39006         if(this.panelSize){
39007             w = w !== null ? w : this.panelSize.width;
39008             h = h !== null ? h : this.panelSize.height;
39009         }
39010         if(this.activePanel){
39011             var el = this.activePanel.getEl();
39012             w = w !== null ? w : el.getWidth();
39013             h = h !== null ? h : el.getHeight();
39014             this.panelSize = {width: w, height: h};
39015             this.activePanel.setSize(w, h);
39016         }
39017         if(Roo.isIE && this.tabs){
39018             this.tabs.el.repaint();
39019         }
39020     },
39021
39022     /**
39023      * Returns the container element for this region.
39024      * @return {Roo.Element}
39025      */
39026     getEl : function(){
39027         return this.el;
39028     },
39029
39030     /**
39031      * Hides this region.
39032      */
39033     hide : function(){
39034         //if(!this.collapsed){
39035             this.el.dom.style.left = "-2000px";
39036             this.el.hide();
39037         //}else{
39038          //   this.collapsedEl.dom.style.left = "-2000px";
39039          //   this.collapsedEl.hide();
39040        // }
39041         this.visible = false;
39042         this.fireEvent("visibilitychange", this, false);
39043     },
39044
39045     /**
39046      * Shows this region if it was previously hidden.
39047      */
39048     show : function(){
39049         //if(!this.collapsed){
39050             this.el.show();
39051         //}else{
39052         //    this.collapsedEl.show();
39053        // }
39054         this.visible = true;
39055         this.fireEvent("visibilitychange", this, true);
39056     },
39057 /*
39058     closeClicked : function(){
39059         if(this.activePanel){
39060             this.remove(this.activePanel);
39061         }
39062     },
39063
39064     collapseClick : function(e){
39065         if(this.isSlid){
39066            e.stopPropagation();
39067            this.slideIn();
39068         }else{
39069            e.stopPropagation();
39070            this.slideOut();
39071         }
39072     },
39073 */
39074     /**
39075      * Collapses this region.
39076      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39077      */
39078     /*
39079     collapse : function(skipAnim, skipCheck = false){
39080         if(this.collapsed) {
39081             return;
39082         }
39083         
39084         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39085             
39086             this.collapsed = true;
39087             if(this.split){
39088                 this.split.el.hide();
39089             }
39090             if(this.config.animate && skipAnim !== true){
39091                 this.fireEvent("invalidated", this);
39092                 this.animateCollapse();
39093             }else{
39094                 this.el.setLocation(-20000,-20000);
39095                 this.el.hide();
39096                 this.collapsedEl.show();
39097                 this.fireEvent("collapsed", this);
39098                 this.fireEvent("invalidated", this);
39099             }
39100         }
39101         
39102     },
39103 */
39104     animateCollapse : function(){
39105         // overridden
39106     },
39107
39108     /**
39109      * Expands this region if it was previously collapsed.
39110      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39111      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39112      */
39113     /*
39114     expand : function(e, skipAnim){
39115         if(e) {
39116             e.stopPropagation();
39117         }
39118         if(!this.collapsed || this.el.hasActiveFx()) {
39119             return;
39120         }
39121         if(this.isSlid){
39122             this.afterSlideIn();
39123             skipAnim = true;
39124         }
39125         this.collapsed = false;
39126         if(this.config.animate && skipAnim !== true){
39127             this.animateExpand();
39128         }else{
39129             this.el.show();
39130             if(this.split){
39131                 this.split.el.show();
39132             }
39133             this.collapsedEl.setLocation(-2000,-2000);
39134             this.collapsedEl.hide();
39135             this.fireEvent("invalidated", this);
39136             this.fireEvent("expanded", this);
39137         }
39138     },
39139 */
39140     animateExpand : function(){
39141         // overridden
39142     },
39143
39144     initTabs : function()
39145     {
39146         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39147         
39148         var ts = new Roo.bootstrap.panel.Tabs({
39149             el: this.bodyEl.dom,
39150             region : this,
39151             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39152             disableTooltips: this.config.disableTabTips,
39153             toolbar : this.config.toolbar
39154         });
39155         
39156         if(this.config.hideTabs){
39157             ts.stripWrap.setDisplayed(false);
39158         }
39159         this.tabs = ts;
39160         ts.resizeTabs = this.config.resizeTabs === true;
39161         ts.minTabWidth = this.config.minTabWidth || 40;
39162         ts.maxTabWidth = this.config.maxTabWidth || 250;
39163         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39164         ts.monitorResize = false;
39165         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39166         ts.bodyEl.addClass('roo-layout-tabs-body');
39167         this.panels.each(this.initPanelAsTab, this);
39168     },
39169
39170     initPanelAsTab : function(panel){
39171         var ti = this.tabs.addTab(
39172             panel.getEl().id,
39173             panel.getTitle(),
39174             null,
39175             this.config.closeOnTab && panel.isClosable(),
39176             panel.tpl
39177         );
39178         if(panel.tabTip !== undefined){
39179             ti.setTooltip(panel.tabTip);
39180         }
39181         ti.on("activate", function(){
39182               this.setActivePanel(panel);
39183         }, this);
39184         
39185         if(this.config.closeOnTab){
39186             ti.on("beforeclose", function(t, e){
39187                 e.cancel = true;
39188                 this.remove(panel);
39189             }, this);
39190         }
39191         
39192         panel.tabItem = ti;
39193         
39194         return ti;
39195     },
39196
39197     updatePanelTitle : function(panel, title)
39198     {
39199         if(this.activePanel == panel){
39200             this.updateTitle(title);
39201         }
39202         if(this.tabs){
39203             var ti = this.tabs.getTab(panel.getEl().id);
39204             ti.setText(title);
39205             if(panel.tabTip !== undefined){
39206                 ti.setTooltip(panel.tabTip);
39207             }
39208         }
39209     },
39210
39211     updateTitle : function(title){
39212         if(this.titleTextEl && !this.config.title){
39213             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39214         }
39215     },
39216
39217     setActivePanel : function(panel)
39218     {
39219         panel = this.getPanel(panel);
39220         if(this.activePanel && this.activePanel != panel){
39221             if(this.activePanel.setActiveState(false) === false){
39222                 return;
39223             }
39224         }
39225         this.activePanel = panel;
39226         panel.setActiveState(true);
39227         if(this.panelSize){
39228             panel.setSize(this.panelSize.width, this.panelSize.height);
39229         }
39230         if(this.closeBtn){
39231             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39232         }
39233         this.updateTitle(panel.getTitle());
39234         if(this.tabs){
39235             this.fireEvent("invalidated", this);
39236         }
39237         this.fireEvent("panelactivated", this, panel);
39238     },
39239
39240     /**
39241      * Shows the specified panel.
39242      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39243      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39244      */
39245     showPanel : function(panel)
39246     {
39247         panel = this.getPanel(panel);
39248         if(panel){
39249             if(this.tabs){
39250                 var tab = this.tabs.getTab(panel.getEl().id);
39251                 if(tab.isHidden()){
39252                     this.tabs.unhideTab(tab.id);
39253                 }
39254                 tab.activate();
39255             }else{
39256                 this.setActivePanel(panel);
39257             }
39258         }
39259         return panel;
39260     },
39261
39262     /**
39263      * Get the active panel for this region.
39264      * @return {Roo.ContentPanel} The active panel or null
39265      */
39266     getActivePanel : function(){
39267         return this.activePanel;
39268     },
39269
39270     validateVisibility : function(){
39271         if(this.panels.getCount() < 1){
39272             this.updateTitle("&#160;");
39273             this.closeBtn.hide();
39274             this.hide();
39275         }else{
39276             if(!this.isVisible()){
39277                 this.show();
39278             }
39279         }
39280     },
39281
39282     /**
39283      * Adds the passed ContentPanel(s) to this region.
39284      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39285      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39286      */
39287     add : function(panel)
39288     {
39289         if(arguments.length > 1){
39290             for(var i = 0, len = arguments.length; i < len; i++) {
39291                 this.add(arguments[i]);
39292             }
39293             return null;
39294         }
39295         
39296         // if we have not been rendered yet, then we can not really do much of this..
39297         if (!this.bodyEl) {
39298             this.unrendered_panels.push(panel);
39299             return panel;
39300         }
39301         
39302         
39303         
39304         
39305         if(this.hasPanel(panel)){
39306             this.showPanel(panel);
39307             return panel;
39308         }
39309         panel.setRegion(this);
39310         this.panels.add(panel);
39311        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39312             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39313             // and hide them... ???
39314             this.bodyEl.dom.appendChild(panel.getEl().dom);
39315             if(panel.background !== true){
39316                 this.setActivePanel(panel);
39317             }
39318             this.fireEvent("paneladded", this, panel);
39319             return panel;
39320         }
39321         */
39322         if(!this.tabs){
39323             this.initTabs();
39324         }else{
39325             this.initPanelAsTab(panel);
39326         }
39327         
39328         
39329         if(panel.background !== true){
39330             this.tabs.activate(panel.getEl().id);
39331         }
39332         this.fireEvent("paneladded", this, panel);
39333         return panel;
39334     },
39335
39336     /**
39337      * Hides the tab for the specified panel.
39338      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39339      */
39340     hidePanel : function(panel){
39341         if(this.tabs && (panel = this.getPanel(panel))){
39342             this.tabs.hideTab(panel.getEl().id);
39343         }
39344     },
39345
39346     /**
39347      * Unhides the tab for a previously hidden panel.
39348      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39349      */
39350     unhidePanel : function(panel){
39351         if(this.tabs && (panel = this.getPanel(panel))){
39352             this.tabs.unhideTab(panel.getEl().id);
39353         }
39354     },
39355
39356     clearPanels : function(){
39357         while(this.panels.getCount() > 0){
39358              this.remove(this.panels.first());
39359         }
39360     },
39361
39362     /**
39363      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39364      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39365      * @param {Boolean} preservePanel Overrides the config preservePanel option
39366      * @return {Roo.ContentPanel} The panel that was removed
39367      */
39368     remove : function(panel, preservePanel)
39369     {
39370         panel = this.getPanel(panel);
39371         if(!panel){
39372             return null;
39373         }
39374         var e = {};
39375         this.fireEvent("beforeremove", this, panel, e);
39376         if(e.cancel === true){
39377             return null;
39378         }
39379         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39380         var panelId = panel.getId();
39381         this.panels.removeKey(panelId);
39382         if(preservePanel){
39383             document.body.appendChild(panel.getEl().dom);
39384         }
39385         if(this.tabs){
39386             this.tabs.removeTab(panel.getEl().id);
39387         }else if (!preservePanel){
39388             this.bodyEl.dom.removeChild(panel.getEl().dom);
39389         }
39390         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39391             var p = this.panels.first();
39392             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39393             tempEl.appendChild(p.getEl().dom);
39394             this.bodyEl.update("");
39395             this.bodyEl.dom.appendChild(p.getEl().dom);
39396             tempEl = null;
39397             this.updateTitle(p.getTitle());
39398             this.tabs = null;
39399             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39400             this.setActivePanel(p);
39401         }
39402         panel.setRegion(null);
39403         if(this.activePanel == panel){
39404             this.activePanel = null;
39405         }
39406         if(this.config.autoDestroy !== false && preservePanel !== true){
39407             try{panel.destroy();}catch(e){}
39408         }
39409         this.fireEvent("panelremoved", this, panel);
39410         return panel;
39411     },
39412
39413     /**
39414      * Returns the TabPanel component used by this region
39415      * @return {Roo.TabPanel}
39416      */
39417     getTabs : function(){
39418         return this.tabs;
39419     },
39420
39421     createTool : function(parentEl, className){
39422         var btn = Roo.DomHelper.append(parentEl, {
39423             tag: "div",
39424             cls: "x-layout-tools-button",
39425             children: [ {
39426                 tag: "div",
39427                 cls: "roo-layout-tools-button-inner " + className,
39428                 html: "&#160;"
39429             }]
39430         }, true);
39431         btn.addClassOnOver("roo-layout-tools-button-over");
39432         return btn;
39433     }
39434 });/*
39435  * Based on:
39436  * Ext JS Library 1.1.1
39437  * Copyright(c) 2006-2007, Ext JS, LLC.
39438  *
39439  * Originally Released Under LGPL - original licence link has changed is not relivant.
39440  *
39441  * Fork - LGPL
39442  * <script type="text/javascript">
39443  */
39444  
39445
39446
39447 /**
39448  * @class Roo.SplitLayoutRegion
39449  * @extends Roo.LayoutRegion
39450  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39451  */
39452 Roo.bootstrap.layout.Split = function(config){
39453     this.cursor = config.cursor;
39454     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39455 };
39456
39457 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39458 {
39459     splitTip : "Drag to resize.",
39460     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39461     useSplitTips : false,
39462
39463     applyConfig : function(config){
39464         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39465     },
39466     
39467     onRender : function(ctr,pos) {
39468         
39469         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39470         if(!this.config.split){
39471             return;
39472         }
39473         if(!this.split){
39474             
39475             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39476                             tag: "div",
39477                             id: this.el.id + "-split",
39478                             cls: "roo-layout-split roo-layout-split-"+this.position,
39479                             html: "&#160;"
39480             });
39481             /** The SplitBar for this region 
39482             * @type Roo.SplitBar */
39483             // does not exist yet...
39484             Roo.log([this.position, this.orientation]);
39485             
39486             this.split = new Roo.bootstrap.SplitBar({
39487                 dragElement : splitEl,
39488                 resizingElement: this.el,
39489                 orientation : this.orientation
39490             });
39491             
39492             this.split.on("moved", this.onSplitMove, this);
39493             this.split.useShim = this.config.useShim === true;
39494             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39495             if(this.useSplitTips){
39496                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39497             }
39498             //if(config.collapsible){
39499             //    this.split.el.on("dblclick", this.collapse,  this);
39500             //}
39501         }
39502         if(typeof this.config.minSize != "undefined"){
39503             this.split.minSize = this.config.minSize;
39504         }
39505         if(typeof this.config.maxSize != "undefined"){
39506             this.split.maxSize = this.config.maxSize;
39507         }
39508         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39509             this.hideSplitter();
39510         }
39511         
39512     },
39513
39514     getHMaxSize : function(){
39515          var cmax = this.config.maxSize || 10000;
39516          var center = this.mgr.getRegion("center");
39517          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39518     },
39519
39520     getVMaxSize : function(){
39521          var cmax = this.config.maxSize || 10000;
39522          var center = this.mgr.getRegion("center");
39523          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39524     },
39525
39526     onSplitMove : function(split, newSize){
39527         this.fireEvent("resized", this, newSize);
39528     },
39529     
39530     /** 
39531      * Returns the {@link Roo.SplitBar} for this region.
39532      * @return {Roo.SplitBar}
39533      */
39534     getSplitBar : function(){
39535         return this.split;
39536     },
39537     
39538     hide : function(){
39539         this.hideSplitter();
39540         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39541     },
39542
39543     hideSplitter : function(){
39544         if(this.split){
39545             this.split.el.setLocation(-2000,-2000);
39546             this.split.el.hide();
39547         }
39548     },
39549
39550     show : function(){
39551         if(this.split){
39552             this.split.el.show();
39553         }
39554         Roo.bootstrap.layout.Split.superclass.show.call(this);
39555     },
39556     
39557     beforeSlide: function(){
39558         if(Roo.isGecko){// firefox overflow auto bug workaround
39559             this.bodyEl.clip();
39560             if(this.tabs) {
39561                 this.tabs.bodyEl.clip();
39562             }
39563             if(this.activePanel){
39564                 this.activePanel.getEl().clip();
39565                 
39566                 if(this.activePanel.beforeSlide){
39567                     this.activePanel.beforeSlide();
39568                 }
39569             }
39570         }
39571     },
39572     
39573     afterSlide : function(){
39574         if(Roo.isGecko){// firefox overflow auto bug workaround
39575             this.bodyEl.unclip();
39576             if(this.tabs) {
39577                 this.tabs.bodyEl.unclip();
39578             }
39579             if(this.activePanel){
39580                 this.activePanel.getEl().unclip();
39581                 if(this.activePanel.afterSlide){
39582                     this.activePanel.afterSlide();
39583                 }
39584             }
39585         }
39586     },
39587
39588     initAutoHide : function(){
39589         if(this.autoHide !== false){
39590             if(!this.autoHideHd){
39591                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39592                 this.autoHideHd = {
39593                     "mouseout": function(e){
39594                         if(!e.within(this.el, true)){
39595                             st.delay(500);
39596                         }
39597                     },
39598                     "mouseover" : function(e){
39599                         st.cancel();
39600                     },
39601                     scope : this
39602                 };
39603             }
39604             this.el.on(this.autoHideHd);
39605         }
39606     },
39607
39608     clearAutoHide : function(){
39609         if(this.autoHide !== false){
39610             this.el.un("mouseout", this.autoHideHd.mouseout);
39611             this.el.un("mouseover", this.autoHideHd.mouseover);
39612         }
39613     },
39614
39615     clearMonitor : function(){
39616         Roo.get(document).un("click", this.slideInIf, this);
39617     },
39618
39619     // these names are backwards but not changed for compat
39620     slideOut : function(){
39621         if(this.isSlid || this.el.hasActiveFx()){
39622             return;
39623         }
39624         this.isSlid = true;
39625         if(this.collapseBtn){
39626             this.collapseBtn.hide();
39627         }
39628         this.closeBtnState = this.closeBtn.getStyle('display');
39629         this.closeBtn.hide();
39630         if(this.stickBtn){
39631             this.stickBtn.show();
39632         }
39633         this.el.show();
39634         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39635         this.beforeSlide();
39636         this.el.setStyle("z-index", 10001);
39637         this.el.slideIn(this.getSlideAnchor(), {
39638             callback: function(){
39639                 this.afterSlide();
39640                 this.initAutoHide();
39641                 Roo.get(document).on("click", this.slideInIf, this);
39642                 this.fireEvent("slideshow", this);
39643             },
39644             scope: this,
39645             block: true
39646         });
39647     },
39648
39649     afterSlideIn : function(){
39650         this.clearAutoHide();
39651         this.isSlid = false;
39652         this.clearMonitor();
39653         this.el.setStyle("z-index", "");
39654         if(this.collapseBtn){
39655             this.collapseBtn.show();
39656         }
39657         this.closeBtn.setStyle('display', this.closeBtnState);
39658         if(this.stickBtn){
39659             this.stickBtn.hide();
39660         }
39661         this.fireEvent("slidehide", this);
39662     },
39663
39664     slideIn : function(cb){
39665         if(!this.isSlid || this.el.hasActiveFx()){
39666             Roo.callback(cb);
39667             return;
39668         }
39669         this.isSlid = false;
39670         this.beforeSlide();
39671         this.el.slideOut(this.getSlideAnchor(), {
39672             callback: function(){
39673                 this.el.setLeftTop(-10000, -10000);
39674                 this.afterSlide();
39675                 this.afterSlideIn();
39676                 Roo.callback(cb);
39677             },
39678             scope: this,
39679             block: true
39680         });
39681     },
39682     
39683     slideInIf : function(e){
39684         if(!e.within(this.el)){
39685             this.slideIn();
39686         }
39687     },
39688
39689     animateCollapse : function(){
39690         this.beforeSlide();
39691         this.el.setStyle("z-index", 20000);
39692         var anchor = this.getSlideAnchor();
39693         this.el.slideOut(anchor, {
39694             callback : function(){
39695                 this.el.setStyle("z-index", "");
39696                 this.collapsedEl.slideIn(anchor, {duration:.3});
39697                 this.afterSlide();
39698                 this.el.setLocation(-10000,-10000);
39699                 this.el.hide();
39700                 this.fireEvent("collapsed", this);
39701             },
39702             scope: this,
39703             block: true
39704         });
39705     },
39706
39707     animateExpand : function(){
39708         this.beforeSlide();
39709         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39710         this.el.setStyle("z-index", 20000);
39711         this.collapsedEl.hide({
39712             duration:.1
39713         });
39714         this.el.slideIn(this.getSlideAnchor(), {
39715             callback : function(){
39716                 this.el.setStyle("z-index", "");
39717                 this.afterSlide();
39718                 if(this.split){
39719                     this.split.el.show();
39720                 }
39721                 this.fireEvent("invalidated", this);
39722                 this.fireEvent("expanded", this);
39723             },
39724             scope: this,
39725             block: true
39726         });
39727     },
39728
39729     anchors : {
39730         "west" : "left",
39731         "east" : "right",
39732         "north" : "top",
39733         "south" : "bottom"
39734     },
39735
39736     sanchors : {
39737         "west" : "l",
39738         "east" : "r",
39739         "north" : "t",
39740         "south" : "b"
39741     },
39742
39743     canchors : {
39744         "west" : "tl-tr",
39745         "east" : "tr-tl",
39746         "north" : "tl-bl",
39747         "south" : "bl-tl"
39748     },
39749
39750     getAnchor : function(){
39751         return this.anchors[this.position];
39752     },
39753
39754     getCollapseAnchor : function(){
39755         return this.canchors[this.position];
39756     },
39757
39758     getSlideAnchor : function(){
39759         return this.sanchors[this.position];
39760     },
39761
39762     getAlignAdj : function(){
39763         var cm = this.cmargins;
39764         switch(this.position){
39765             case "west":
39766                 return [0, 0];
39767             break;
39768             case "east":
39769                 return [0, 0];
39770             break;
39771             case "north":
39772                 return [0, 0];
39773             break;
39774             case "south":
39775                 return [0, 0];
39776             break;
39777         }
39778     },
39779
39780     getExpandAdj : function(){
39781         var c = this.collapsedEl, cm = this.cmargins;
39782         switch(this.position){
39783             case "west":
39784                 return [-(cm.right+c.getWidth()+cm.left), 0];
39785             break;
39786             case "east":
39787                 return [cm.right+c.getWidth()+cm.left, 0];
39788             break;
39789             case "north":
39790                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39791             break;
39792             case "south":
39793                 return [0, cm.top+cm.bottom+c.getHeight()];
39794             break;
39795         }
39796     }
39797 });/*
39798  * Based on:
39799  * Ext JS Library 1.1.1
39800  * Copyright(c) 2006-2007, Ext JS, LLC.
39801  *
39802  * Originally Released Under LGPL - original licence link has changed is not relivant.
39803  *
39804  * Fork - LGPL
39805  * <script type="text/javascript">
39806  */
39807 /*
39808  * These classes are private internal classes
39809  */
39810 Roo.bootstrap.layout.Center = function(config){
39811     config.region = "center";
39812     Roo.bootstrap.layout.Region.call(this, config);
39813     this.visible = true;
39814     this.minWidth = config.minWidth || 20;
39815     this.minHeight = config.minHeight || 20;
39816 };
39817
39818 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39819     hide : function(){
39820         // center panel can't be hidden
39821     },
39822     
39823     show : function(){
39824         // center panel can't be hidden
39825     },
39826     
39827     getMinWidth: function(){
39828         return this.minWidth;
39829     },
39830     
39831     getMinHeight: function(){
39832         return this.minHeight;
39833     }
39834 });
39835
39836
39837
39838
39839  
39840
39841
39842
39843
39844
39845
39846 Roo.bootstrap.layout.North = function(config)
39847 {
39848     config.region = 'north';
39849     config.cursor = 'n-resize';
39850     
39851     Roo.bootstrap.layout.Split.call(this, config);
39852     
39853     
39854     if(this.split){
39855         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39856         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39857         this.split.el.addClass("roo-layout-split-v");
39858     }
39859     //var size = config.initialSize || config.height;
39860     //if(this.el && typeof size != "undefined"){
39861     //    this.el.setHeight(size);
39862     //}
39863 };
39864 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39865 {
39866     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39867      
39868      
39869     onRender : function(ctr, pos)
39870     {
39871         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39872         var size = this.config.initialSize || this.config.height;
39873         if(this.el && typeof size != "undefined"){
39874             this.el.setHeight(size);
39875         }
39876     
39877     },
39878     
39879     getBox : function(){
39880         if(this.collapsed){
39881             return this.collapsedEl.getBox();
39882         }
39883         var box = this.el.getBox();
39884         if(this.split){
39885             box.height += this.split.el.getHeight();
39886         }
39887         return box;
39888     },
39889     
39890     updateBox : function(box){
39891         if(this.split && !this.collapsed){
39892             box.height -= this.split.el.getHeight();
39893             this.split.el.setLeft(box.x);
39894             this.split.el.setTop(box.y+box.height);
39895             this.split.el.setWidth(box.width);
39896         }
39897         if(this.collapsed){
39898             this.updateBody(box.width, null);
39899         }
39900         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39901     }
39902 });
39903
39904
39905
39906
39907
39908 Roo.bootstrap.layout.South = function(config){
39909     config.region = 'south';
39910     config.cursor = 's-resize';
39911     Roo.bootstrap.layout.Split.call(this, config);
39912     if(this.split){
39913         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39914         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39915         this.split.el.addClass("roo-layout-split-v");
39916     }
39917     
39918 };
39919
39920 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39921     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39922     
39923     onRender : function(ctr, pos)
39924     {
39925         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39926         var size = this.config.initialSize || this.config.height;
39927         if(this.el && typeof size != "undefined"){
39928             this.el.setHeight(size);
39929         }
39930     
39931     },
39932     
39933     getBox : function(){
39934         if(this.collapsed){
39935             return this.collapsedEl.getBox();
39936         }
39937         var box = this.el.getBox();
39938         if(this.split){
39939             var sh = this.split.el.getHeight();
39940             box.height += sh;
39941             box.y -= sh;
39942         }
39943         return box;
39944     },
39945     
39946     updateBox : function(box){
39947         if(this.split && !this.collapsed){
39948             var sh = this.split.el.getHeight();
39949             box.height -= sh;
39950             box.y += sh;
39951             this.split.el.setLeft(box.x);
39952             this.split.el.setTop(box.y-sh);
39953             this.split.el.setWidth(box.width);
39954         }
39955         if(this.collapsed){
39956             this.updateBody(box.width, null);
39957         }
39958         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39959     }
39960 });
39961
39962 Roo.bootstrap.layout.East = function(config){
39963     config.region = "east";
39964     config.cursor = "e-resize";
39965     Roo.bootstrap.layout.Split.call(this, config);
39966     if(this.split){
39967         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39968         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39969         this.split.el.addClass("roo-layout-split-h");
39970     }
39971     
39972 };
39973 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39974     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39975     
39976     onRender : function(ctr, pos)
39977     {
39978         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39979         var size = this.config.initialSize || this.config.width;
39980         if(this.el && typeof size != "undefined"){
39981             this.el.setWidth(size);
39982         }
39983     
39984     },
39985     
39986     getBox : function(){
39987         if(this.collapsed){
39988             return this.collapsedEl.getBox();
39989         }
39990         var box = this.el.getBox();
39991         if(this.split){
39992             var sw = this.split.el.getWidth();
39993             box.width += sw;
39994             box.x -= sw;
39995         }
39996         return box;
39997     },
39998
39999     updateBox : function(box){
40000         if(this.split && !this.collapsed){
40001             var sw = this.split.el.getWidth();
40002             box.width -= sw;
40003             this.split.el.setLeft(box.x);
40004             this.split.el.setTop(box.y);
40005             this.split.el.setHeight(box.height);
40006             box.x += sw;
40007         }
40008         if(this.collapsed){
40009             this.updateBody(null, box.height);
40010         }
40011         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40012     }
40013 });
40014
40015 Roo.bootstrap.layout.West = function(config){
40016     config.region = "west";
40017     config.cursor = "w-resize";
40018     
40019     Roo.bootstrap.layout.Split.call(this, config);
40020     if(this.split){
40021         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
40022         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
40023         this.split.el.addClass("roo-layout-split-h");
40024     }
40025     
40026 };
40027 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
40028     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
40029     
40030     onRender: function(ctr, pos)
40031     {
40032         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
40033         var size = this.config.initialSize || this.config.width;
40034         if(typeof size != "undefined"){
40035             this.el.setWidth(size);
40036         }
40037     },
40038     
40039     getBox : function(){
40040         if(this.collapsed){
40041             return this.collapsedEl.getBox();
40042         }
40043         var box = this.el.getBox();
40044         if (box.width == 0) {
40045             box.width = this.config.width; // kludge?
40046         }
40047         if(this.split){
40048             box.width += this.split.el.getWidth();
40049         }
40050         return box;
40051     },
40052     
40053     updateBox : function(box){
40054         if(this.split && !this.collapsed){
40055             var sw = this.split.el.getWidth();
40056             box.width -= sw;
40057             this.split.el.setLeft(box.x+box.width);
40058             this.split.el.setTop(box.y);
40059             this.split.el.setHeight(box.height);
40060         }
40061         if(this.collapsed){
40062             this.updateBody(null, box.height);
40063         }
40064         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40065     }
40066 });Roo.namespace("Roo.bootstrap.panel");/*
40067  * Based on:
40068  * Ext JS Library 1.1.1
40069  * Copyright(c) 2006-2007, Ext JS, LLC.
40070  *
40071  * Originally Released Under LGPL - original licence link has changed is not relivant.
40072  *
40073  * Fork - LGPL
40074  * <script type="text/javascript">
40075  */
40076 /**
40077  * @class Roo.ContentPanel
40078  * @extends Roo.util.Observable
40079  * A basic ContentPanel element.
40080  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40081  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40082  * @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
40083  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40084  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40085  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40086  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40087  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40088  * @cfg {String} title          The title for this panel
40089  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40090  * @cfg {String} url            Calls {@link #setUrl} with this value
40091  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40092  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40093  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40094  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40095  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40096  * @cfg {Boolean} badges render the badges
40097  * @cfg {String} cls  extra classes to use  
40098  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40099
40100  * @constructor
40101  * Create a new ContentPanel.
40102  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40103  * @param {String/Object} config A string to set only the title or a config object
40104  * @param {String} content (optional) Set the HTML content for this panel
40105  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40106  */
40107 Roo.bootstrap.panel.Content = function( config){
40108     
40109     this.tpl = config.tpl || false;
40110     
40111     var el = config.el;
40112     var content = config.content;
40113
40114     if(config.autoCreate){ // xtype is available if this is called from factory
40115         el = Roo.id();
40116     }
40117     this.el = Roo.get(el);
40118     if(!this.el && config && config.autoCreate){
40119         if(typeof config.autoCreate == "object"){
40120             if(!config.autoCreate.id){
40121                 config.autoCreate.id = config.id||el;
40122             }
40123             this.el = Roo.DomHelper.append(document.body,
40124                         config.autoCreate, true);
40125         }else{
40126             var elcfg =  {
40127                 tag: "div",
40128                 cls: (config.cls || '') +
40129                     (config.background ? ' bg-' + config.background : '') +
40130                     " roo-layout-inactive-content",
40131                 id: config.id||el
40132             };
40133             if (config.iframe) {
40134                 elcfg.cn = [
40135                     {
40136                         tag : 'iframe',
40137                         style : 'border: 0px',
40138                         src : 'about:blank'
40139                     }
40140                 ];
40141             }
40142               
40143             if (config.html) {
40144                 elcfg.html = config.html;
40145                 
40146             }
40147                         
40148             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40149             if (config.iframe) {
40150                 this.iframeEl = this.el.select('iframe',true).first();
40151             }
40152             
40153         }
40154     } 
40155     this.closable = false;
40156     this.loaded = false;
40157     this.active = false;
40158    
40159       
40160     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40161         
40162         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40163         
40164         this.wrapEl = this.el; //this.el.wrap();
40165         var ti = [];
40166         if (config.toolbar.items) {
40167             ti = config.toolbar.items ;
40168             delete config.toolbar.items ;
40169         }
40170         
40171         var nitems = [];
40172         this.toolbar.render(this.wrapEl, 'before');
40173         for(var i =0;i < ti.length;i++) {
40174           //  Roo.log(['add child', items[i]]);
40175             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40176         }
40177         this.toolbar.items = nitems;
40178         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40179         delete config.toolbar;
40180         
40181     }
40182     /*
40183     // xtype created footer. - not sure if will work as we normally have to render first..
40184     if (this.footer && !this.footer.el && this.footer.xtype) {
40185         if (!this.wrapEl) {
40186             this.wrapEl = this.el.wrap();
40187         }
40188     
40189         this.footer.container = this.wrapEl.createChild();
40190          
40191         this.footer = Roo.factory(this.footer, Roo);
40192         
40193     }
40194     */
40195     
40196      if(typeof config == "string"){
40197         this.title = config;
40198     }else{
40199         Roo.apply(this, config);
40200     }
40201     
40202     if(this.resizeEl){
40203         this.resizeEl = Roo.get(this.resizeEl, true);
40204     }else{
40205         this.resizeEl = this.el;
40206     }
40207     // handle view.xtype
40208     
40209  
40210     
40211     
40212     this.addEvents({
40213         /**
40214          * @event activate
40215          * Fires when this panel is activated. 
40216          * @param {Roo.ContentPanel} this
40217          */
40218         "activate" : true,
40219         /**
40220          * @event deactivate
40221          * Fires when this panel is activated. 
40222          * @param {Roo.ContentPanel} this
40223          */
40224         "deactivate" : true,
40225
40226         /**
40227          * @event resize
40228          * Fires when this panel is resized if fitToFrame is true.
40229          * @param {Roo.ContentPanel} this
40230          * @param {Number} width The width after any component adjustments
40231          * @param {Number} height The height after any component adjustments
40232          */
40233         "resize" : true,
40234         
40235          /**
40236          * @event render
40237          * Fires when this tab is created
40238          * @param {Roo.ContentPanel} this
40239          */
40240         "render" : true,
40241         
40242           /**
40243          * @event scroll
40244          * Fires when this content is scrolled
40245          * @param {Roo.ContentPanel} this
40246          * @param {Event} scrollEvent
40247          */
40248         "scroll" : true
40249         
40250         
40251         
40252     });
40253     
40254
40255     
40256     
40257     if(this.autoScroll && !this.iframe){
40258         this.resizeEl.setStyle("overflow", "auto");
40259         this.resizeEl.on('scroll', this.onScroll, this);
40260     } else {
40261         // fix randome scrolling
40262         //this.el.on('scroll', function() {
40263         //    Roo.log('fix random scolling');
40264         //    this.scrollTo('top',0); 
40265         //});
40266     }
40267     content = content || this.content;
40268     if(content){
40269         this.setContent(content);
40270     }
40271     if(config && config.url){
40272         this.setUrl(this.url, this.params, this.loadOnce);
40273     }
40274     
40275     
40276     
40277     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40278     
40279     if (this.view && typeof(this.view.xtype) != 'undefined') {
40280         this.view.el = this.el.appendChild(document.createElement("div"));
40281         this.view = Roo.factory(this.view); 
40282         this.view.render  &&  this.view.render(false, '');  
40283     }
40284     
40285     
40286     this.fireEvent('render', this);
40287 };
40288
40289 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40290     
40291     cls : '',
40292     background : '',
40293     
40294     tabTip : '',
40295     
40296     iframe : false,
40297     iframeEl : false,
40298     
40299     /* Resize Element - use this to work out scroll etc. */
40300     resizeEl : false,
40301     
40302     setRegion : function(region){
40303         this.region = region;
40304         this.setActiveClass(region && !this.background);
40305     },
40306     
40307     
40308     setActiveClass: function(state)
40309     {
40310         if(state){
40311            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40312            this.el.setStyle('position','relative');
40313         }else{
40314            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40315            this.el.setStyle('position', 'absolute');
40316         } 
40317     },
40318     
40319     /**
40320      * Returns the toolbar for this Panel if one was configured. 
40321      * @return {Roo.Toolbar} 
40322      */
40323     getToolbar : function(){
40324         return this.toolbar;
40325     },
40326     
40327     setActiveState : function(active)
40328     {
40329         this.active = active;
40330         this.setActiveClass(active);
40331         if(!active){
40332             if(this.fireEvent("deactivate", this) === false){
40333                 return false;
40334             }
40335             return true;
40336         }
40337         this.fireEvent("activate", this);
40338         return true;
40339     },
40340     /**
40341      * Updates this panel's element (not for iframe)
40342      * @param {String} content The new content
40343      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40344     */
40345     setContent : function(content, loadScripts){
40346         if (this.iframe) {
40347             return;
40348         }
40349         
40350         this.el.update(content, loadScripts);
40351     },
40352
40353     ignoreResize : function(w, h){
40354         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40355             return true;
40356         }else{
40357             this.lastSize = {width: w, height: h};
40358             return false;
40359         }
40360     },
40361     /**
40362      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40363      * @return {Roo.UpdateManager} The UpdateManager
40364      */
40365     getUpdateManager : function(){
40366         if (this.iframe) {
40367             return false;
40368         }
40369         return this.el.getUpdateManager();
40370     },
40371      /**
40372      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40373      * Does not work with IFRAME contents
40374      * @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:
40375 <pre><code>
40376 panel.load({
40377     url: "your-url.php",
40378     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40379     callback: yourFunction,
40380     scope: yourObject, //(optional scope)
40381     discardUrl: false,
40382     nocache: false,
40383     text: "Loading...",
40384     timeout: 30,
40385     scripts: false
40386 });
40387 </code></pre>
40388      
40389      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40390      * 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.
40391      * @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}
40392      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40393      * @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.
40394      * @return {Roo.ContentPanel} this
40395      */
40396     load : function(){
40397         
40398         if (this.iframe) {
40399             return this;
40400         }
40401         
40402         var um = this.el.getUpdateManager();
40403         um.update.apply(um, arguments);
40404         return this;
40405     },
40406
40407
40408     /**
40409      * 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.
40410      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40411      * @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)
40412      * @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)
40413      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40414      */
40415     setUrl : function(url, params, loadOnce){
40416         if (this.iframe) {
40417             this.iframeEl.dom.src = url;
40418             return false;
40419         }
40420         
40421         if(this.refreshDelegate){
40422             this.removeListener("activate", this.refreshDelegate);
40423         }
40424         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40425         this.on("activate", this.refreshDelegate);
40426         return this.el.getUpdateManager();
40427     },
40428     
40429     _handleRefresh : function(url, params, loadOnce){
40430         if(!loadOnce || !this.loaded){
40431             var updater = this.el.getUpdateManager();
40432             updater.update(url, params, this._setLoaded.createDelegate(this));
40433         }
40434     },
40435     
40436     _setLoaded : function(){
40437         this.loaded = true;
40438     }, 
40439     
40440     /**
40441      * Returns this panel's id
40442      * @return {String} 
40443      */
40444     getId : function(){
40445         return this.el.id;
40446     },
40447     
40448     /** 
40449      * Returns this panel's element - used by regiosn to add.
40450      * @return {Roo.Element} 
40451      */
40452     getEl : function(){
40453         return this.wrapEl || this.el;
40454     },
40455     
40456    
40457     
40458     adjustForComponents : function(width, height)
40459     {
40460         //Roo.log('adjustForComponents ');
40461         if(this.resizeEl != this.el){
40462             width -= this.el.getFrameWidth('lr');
40463             height -= this.el.getFrameWidth('tb');
40464         }
40465         if(this.toolbar){
40466             var te = this.toolbar.getEl();
40467             te.setWidth(width);
40468             height -= te.getHeight();
40469         }
40470         if(this.footer){
40471             var te = this.footer.getEl();
40472             te.setWidth(width);
40473             height -= te.getHeight();
40474         }
40475         
40476         
40477         if(this.adjustments){
40478             width += this.adjustments[0];
40479             height += this.adjustments[1];
40480         }
40481         return {"width": width, "height": height};
40482     },
40483     
40484     setSize : function(width, height){
40485         if(this.fitToFrame && !this.ignoreResize(width, height)){
40486             if(this.fitContainer && this.resizeEl != this.el){
40487                 this.el.setSize(width, height);
40488             }
40489             var size = this.adjustForComponents(width, height);
40490             if (this.iframe) {
40491                 this.iframeEl.setSize(width,height);
40492             }
40493             
40494             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40495             this.fireEvent('resize', this, size.width, size.height);
40496             
40497             
40498         }
40499     },
40500     
40501     /**
40502      * Returns this panel's title
40503      * @return {String} 
40504      */
40505     getTitle : function(){
40506         
40507         if (typeof(this.title) != 'object') {
40508             return this.title;
40509         }
40510         
40511         var t = '';
40512         for (var k in this.title) {
40513             if (!this.title.hasOwnProperty(k)) {
40514                 continue;
40515             }
40516             
40517             if (k.indexOf('-') >= 0) {
40518                 var s = k.split('-');
40519                 for (var i = 0; i<s.length; i++) {
40520                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40521                 }
40522             } else {
40523                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40524             }
40525         }
40526         return t;
40527     },
40528     
40529     /**
40530      * Set this panel's title
40531      * @param {String} title
40532      */
40533     setTitle : function(title){
40534         this.title = title;
40535         if(this.region){
40536             this.region.updatePanelTitle(this, title);
40537         }
40538     },
40539     
40540     /**
40541      * Returns true is this panel was configured to be closable
40542      * @return {Boolean} 
40543      */
40544     isClosable : function(){
40545         return this.closable;
40546     },
40547     
40548     beforeSlide : function(){
40549         this.el.clip();
40550         this.resizeEl.clip();
40551     },
40552     
40553     afterSlide : function(){
40554         this.el.unclip();
40555         this.resizeEl.unclip();
40556     },
40557     
40558     /**
40559      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40560      *   Will fail silently if the {@link #setUrl} method has not been called.
40561      *   This does not activate the panel, just updates its content.
40562      */
40563     refresh : function(){
40564         if(this.refreshDelegate){
40565            this.loaded = false;
40566            this.refreshDelegate();
40567         }
40568     },
40569     
40570     /**
40571      * Destroys this panel
40572      */
40573     destroy : function(){
40574         this.el.removeAllListeners();
40575         var tempEl = document.createElement("span");
40576         tempEl.appendChild(this.el.dom);
40577         tempEl.innerHTML = "";
40578         this.el.remove();
40579         this.el = null;
40580     },
40581     
40582     /**
40583      * form - if the content panel contains a form - this is a reference to it.
40584      * @type {Roo.form.Form}
40585      */
40586     form : false,
40587     /**
40588      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40589      *    This contains a reference to it.
40590      * @type {Roo.View}
40591      */
40592     view : false,
40593     
40594       /**
40595      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40596      * <pre><code>
40597
40598 layout.addxtype({
40599        xtype : 'Form',
40600        items: [ .... ]
40601    }
40602 );
40603
40604 </code></pre>
40605      * @param {Object} cfg Xtype definition of item to add.
40606      */
40607     
40608     
40609     getChildContainer: function () {
40610         return this.getEl();
40611     },
40612     
40613     
40614     onScroll : function(e)
40615     {
40616         this.fireEvent('scroll', this, e);
40617     }
40618     
40619     
40620     /*
40621         var  ret = new Roo.factory(cfg);
40622         return ret;
40623         
40624         
40625         // add form..
40626         if (cfg.xtype.match(/^Form$/)) {
40627             
40628             var el;
40629             //if (this.footer) {
40630             //    el = this.footer.container.insertSibling(false, 'before');
40631             //} else {
40632                 el = this.el.createChild();
40633             //}
40634
40635             this.form = new  Roo.form.Form(cfg);
40636             
40637             
40638             if ( this.form.allItems.length) {
40639                 this.form.render(el.dom);
40640             }
40641             return this.form;
40642         }
40643         // should only have one of theses..
40644         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40645             // views.. should not be just added - used named prop 'view''
40646             
40647             cfg.el = this.el.appendChild(document.createElement("div"));
40648             // factory?
40649             
40650             var ret = new Roo.factory(cfg);
40651              
40652              ret.render && ret.render(false, ''); // render blank..
40653             this.view = ret;
40654             return ret;
40655         }
40656         return false;
40657     }
40658     \*/
40659 });
40660  
40661 /**
40662  * @class Roo.bootstrap.panel.Grid
40663  * @extends Roo.bootstrap.panel.Content
40664  * @constructor
40665  * Create a new GridPanel.
40666  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40667  * @param {Object} config A the config object
40668   
40669  */
40670
40671
40672
40673 Roo.bootstrap.panel.Grid = function(config)
40674 {
40675     
40676       
40677     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40678         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40679
40680     config.el = this.wrapper;
40681     //this.el = this.wrapper;
40682     
40683       if (config.container) {
40684         // ctor'ed from a Border/panel.grid
40685         
40686         
40687         this.wrapper.setStyle("overflow", "hidden");
40688         this.wrapper.addClass('roo-grid-container');
40689
40690     }
40691     
40692     
40693     if(config.toolbar){
40694         var tool_el = this.wrapper.createChild();    
40695         this.toolbar = Roo.factory(config.toolbar);
40696         var ti = [];
40697         if (config.toolbar.items) {
40698             ti = config.toolbar.items ;
40699             delete config.toolbar.items ;
40700         }
40701         
40702         var nitems = [];
40703         this.toolbar.render(tool_el);
40704         for(var i =0;i < ti.length;i++) {
40705           //  Roo.log(['add child', items[i]]);
40706             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40707         }
40708         this.toolbar.items = nitems;
40709         
40710         delete config.toolbar;
40711     }
40712     
40713     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40714     config.grid.scrollBody = true;;
40715     config.grid.monitorWindowResize = false; // turn off autosizing
40716     config.grid.autoHeight = false;
40717     config.grid.autoWidth = false;
40718     
40719     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40720     
40721     if (config.background) {
40722         // render grid on panel activation (if panel background)
40723         this.on('activate', function(gp) {
40724             if (!gp.grid.rendered) {
40725                 gp.grid.render(this.wrapper);
40726                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40727             }
40728         });
40729             
40730     } else {
40731         this.grid.render(this.wrapper);
40732         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40733
40734     }
40735     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40736     // ??? needed ??? config.el = this.wrapper;
40737     
40738     
40739     
40740   
40741     // xtype created footer. - not sure if will work as we normally have to render first..
40742     if (this.footer && !this.footer.el && this.footer.xtype) {
40743         
40744         var ctr = this.grid.getView().getFooterPanel(true);
40745         this.footer.dataSource = this.grid.dataSource;
40746         this.footer = Roo.factory(this.footer, Roo);
40747         this.footer.render(ctr);
40748         
40749     }
40750     
40751     
40752     
40753     
40754      
40755 };
40756
40757 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40758     getId : function(){
40759         return this.grid.id;
40760     },
40761     
40762     /**
40763      * Returns the grid for this panel
40764      * @return {Roo.bootstrap.Table} 
40765      */
40766     getGrid : function(){
40767         return this.grid;    
40768     },
40769     
40770     setSize : function(width, height){
40771         if(!this.ignoreResize(width, height)){
40772             var grid = this.grid;
40773             var size = this.adjustForComponents(width, height);
40774             // tfoot is not a footer?
40775           
40776             
40777             var gridel = grid.getGridEl();
40778             gridel.setSize(size.width, size.height);
40779             
40780             var tbd = grid.getGridEl().select('tbody', true).first();
40781             var thd = grid.getGridEl().select('thead',true).first();
40782             var tbf= grid.getGridEl().select('tfoot', true).first();
40783
40784             if (tbf) {
40785                 size.height -= tbf.getHeight();
40786             }
40787             if (thd) {
40788                 size.height -= thd.getHeight();
40789             }
40790             
40791             tbd.setSize(size.width, size.height );
40792             // this is for the account management tab -seems to work there.
40793             var thd = grid.getGridEl().select('thead',true).first();
40794             //if (tbd) {
40795             //    tbd.setSize(size.width, size.height - thd.getHeight());
40796             //}
40797              
40798             grid.autoSize();
40799         }
40800     },
40801      
40802     
40803     
40804     beforeSlide : function(){
40805         this.grid.getView().scroller.clip();
40806     },
40807     
40808     afterSlide : function(){
40809         this.grid.getView().scroller.unclip();
40810     },
40811     
40812     destroy : function(){
40813         this.grid.destroy();
40814         delete this.grid;
40815         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40816     }
40817 });
40818
40819 /**
40820  * @class Roo.bootstrap.panel.Nest
40821  * @extends Roo.bootstrap.panel.Content
40822  * @constructor
40823  * Create a new Panel, that can contain a layout.Border.
40824  * 
40825  * 
40826  * @param {Roo.BorderLayout} layout The layout for this panel
40827  * @param {String/Object} config A string to set only the title or a config object
40828  */
40829 Roo.bootstrap.panel.Nest = function(config)
40830 {
40831     // construct with only one argument..
40832     /* FIXME - implement nicer consturctors
40833     if (layout.layout) {
40834         config = layout;
40835         layout = config.layout;
40836         delete config.layout;
40837     }
40838     if (layout.xtype && !layout.getEl) {
40839         // then layout needs constructing..
40840         layout = Roo.factory(layout, Roo);
40841     }
40842     */
40843     
40844     config.el =  config.layout.getEl();
40845     
40846     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40847     
40848     config.layout.monitorWindowResize = false; // turn off autosizing
40849     this.layout = config.layout;
40850     this.layout.getEl().addClass("roo-layout-nested-layout");
40851     this.layout.parent = this;
40852     
40853     
40854     
40855     
40856 };
40857
40858 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40859
40860     setSize : function(width, height){
40861         if(!this.ignoreResize(width, height)){
40862             var size = this.adjustForComponents(width, height);
40863             var el = this.layout.getEl();
40864             if (size.height < 1) {
40865                 el.setWidth(size.width);   
40866             } else {
40867                 el.setSize(size.width, size.height);
40868             }
40869             var touch = el.dom.offsetWidth;
40870             this.layout.layout();
40871             // ie requires a double layout on the first pass
40872             if(Roo.isIE && !this.initialized){
40873                 this.initialized = true;
40874                 this.layout.layout();
40875             }
40876         }
40877     },
40878     
40879     // activate all subpanels if not currently active..
40880     
40881     setActiveState : function(active){
40882         this.active = active;
40883         this.setActiveClass(active);
40884         
40885         if(!active){
40886             this.fireEvent("deactivate", this);
40887             return;
40888         }
40889         
40890         this.fireEvent("activate", this);
40891         // not sure if this should happen before or after..
40892         if (!this.layout) {
40893             return; // should not happen..
40894         }
40895         var reg = false;
40896         for (var r in this.layout.regions) {
40897             reg = this.layout.getRegion(r);
40898             if (reg.getActivePanel()) {
40899                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40900                 reg.setActivePanel(reg.getActivePanel());
40901                 continue;
40902             }
40903             if (!reg.panels.length) {
40904                 continue;
40905             }
40906             reg.showPanel(reg.getPanel(0));
40907         }
40908         
40909         
40910         
40911         
40912     },
40913     
40914     /**
40915      * Returns the nested BorderLayout for this panel
40916      * @return {Roo.BorderLayout} 
40917      */
40918     getLayout : function(){
40919         return this.layout;
40920     },
40921     
40922      /**
40923      * Adds a xtype elements to the layout of the nested panel
40924      * <pre><code>
40925
40926 panel.addxtype({
40927        xtype : 'ContentPanel',
40928        region: 'west',
40929        items: [ .... ]
40930    }
40931 );
40932
40933 panel.addxtype({
40934         xtype : 'NestedLayoutPanel',
40935         region: 'west',
40936         layout: {
40937            center: { },
40938            west: { }   
40939         },
40940         items : [ ... list of content panels or nested layout panels.. ]
40941    }
40942 );
40943 </code></pre>
40944      * @param {Object} cfg Xtype definition of item to add.
40945      */
40946     addxtype : function(cfg) {
40947         return this.layout.addxtype(cfg);
40948     
40949     }
40950 });/*
40951  * Based on:
40952  * Ext JS Library 1.1.1
40953  * Copyright(c) 2006-2007, Ext JS, LLC.
40954  *
40955  * Originally Released Under LGPL - original licence link has changed is not relivant.
40956  *
40957  * Fork - LGPL
40958  * <script type="text/javascript">
40959  */
40960 /**
40961  * @class Roo.TabPanel
40962  * @extends Roo.util.Observable
40963  * A lightweight tab container.
40964  * <br><br>
40965  * Usage:
40966  * <pre><code>
40967 // basic tabs 1, built from existing content
40968 var tabs = new Roo.TabPanel("tabs1");
40969 tabs.addTab("script", "View Script");
40970 tabs.addTab("markup", "View Markup");
40971 tabs.activate("script");
40972
40973 // more advanced tabs, built from javascript
40974 var jtabs = new Roo.TabPanel("jtabs");
40975 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40976
40977 // set up the UpdateManager
40978 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40979 var updater = tab2.getUpdateManager();
40980 updater.setDefaultUrl("ajax1.htm");
40981 tab2.on('activate', updater.refresh, updater, true);
40982
40983 // Use setUrl for Ajax loading
40984 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40985 tab3.setUrl("ajax2.htm", null, true);
40986
40987 // Disabled tab
40988 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40989 tab4.disable();
40990
40991 jtabs.activate("jtabs-1");
40992  * </code></pre>
40993  * @constructor
40994  * Create a new TabPanel.
40995  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40996  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40997  */
40998 Roo.bootstrap.panel.Tabs = function(config){
40999     /**
41000     * The container element for this TabPanel.
41001     * @type Roo.Element
41002     */
41003     this.el = Roo.get(config.el);
41004     delete config.el;
41005     if(config){
41006         if(typeof config == "boolean"){
41007             this.tabPosition = config ? "bottom" : "top";
41008         }else{
41009             Roo.apply(this, config);
41010         }
41011     }
41012     
41013     if(this.tabPosition == "bottom"){
41014         // if tabs are at the bottom = create the body first.
41015         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41016         this.el.addClass("roo-tabs-bottom");
41017     }
41018     // next create the tabs holders
41019     
41020     if (this.tabPosition == "west"){
41021         
41022         var reg = this.region; // fake it..
41023         while (reg) {
41024             if (!reg.mgr.parent) {
41025                 break;
41026             }
41027             reg = reg.mgr.parent.region;
41028         }
41029         Roo.log("got nest?");
41030         Roo.log(reg);
41031         if (reg.mgr.getRegion('west')) {
41032             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
41033             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
41034             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41035             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41036             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41037         
41038             
41039         }
41040         
41041         
41042     } else {
41043      
41044         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
41045         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
41046         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
41047         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
41048     }
41049     
41050     
41051     if(Roo.isIE){
41052         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
41053     }
41054     
41055     // finally - if tabs are at the top, then create the body last..
41056     if(this.tabPosition != "bottom"){
41057         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
41058          * @type Roo.Element
41059          */
41060         this.bodyEl = Roo.get(this.createBody(this.el.dom));
41061         this.el.addClass("roo-tabs-top");
41062     }
41063     this.items = [];
41064
41065     this.bodyEl.setStyle("position", "relative");
41066
41067     this.active = null;
41068     this.activateDelegate = this.activate.createDelegate(this);
41069
41070     this.addEvents({
41071         /**
41072          * @event tabchange
41073          * Fires when the active tab changes
41074          * @param {Roo.TabPanel} this
41075          * @param {Roo.TabPanelItem} activePanel The new active tab
41076          */
41077         "tabchange": true,
41078         /**
41079          * @event beforetabchange
41080          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41081          * @param {Roo.TabPanel} this
41082          * @param {Object} e Set cancel to true on this object to cancel the tab change
41083          * @param {Roo.TabPanelItem} tab The tab being changed to
41084          */
41085         "beforetabchange" : true
41086     });
41087
41088     Roo.EventManager.onWindowResize(this.onResize, this);
41089     this.cpad = this.el.getPadding("lr");
41090     this.hiddenCount = 0;
41091
41092
41093     // toolbar on the tabbar support...
41094     if (this.toolbar) {
41095         alert("no toolbar support yet");
41096         this.toolbar  = false;
41097         /*
41098         var tcfg = this.toolbar;
41099         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41100         this.toolbar = new Roo.Toolbar(tcfg);
41101         if (Roo.isSafari) {
41102             var tbl = tcfg.container.child('table', true);
41103             tbl.setAttribute('width', '100%');
41104         }
41105         */
41106         
41107     }
41108    
41109
41110
41111     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41112 };
41113
41114 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41115     /*
41116      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41117      */
41118     tabPosition : "top",
41119     /*
41120      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41121      */
41122     currentTabWidth : 0,
41123     /*
41124      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41125      */
41126     minTabWidth : 40,
41127     /*
41128      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41129      */
41130     maxTabWidth : 250,
41131     /*
41132      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41133      */
41134     preferredTabWidth : 175,
41135     /*
41136      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41137      */
41138     resizeTabs : false,
41139     /*
41140      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41141      */
41142     monitorResize : true,
41143     /*
41144      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41145      */
41146     toolbar : false,  // set by caller..
41147     
41148     region : false, /// set by caller
41149     
41150     disableTooltips : true, // not used yet...
41151
41152     /**
41153      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41154      * @param {String} id The id of the div to use <b>or create</b>
41155      * @param {String} text The text for the tab
41156      * @param {String} content (optional) Content to put in the TabPanelItem body
41157      * @param {Boolean} closable (optional) True to create a close icon on the tab
41158      * @return {Roo.TabPanelItem} The created TabPanelItem
41159      */
41160     addTab : function(id, text, content, closable, tpl)
41161     {
41162         var item = new Roo.bootstrap.panel.TabItem({
41163             panel: this,
41164             id : id,
41165             text : text,
41166             closable : closable,
41167             tpl : tpl
41168         });
41169         this.addTabItem(item);
41170         if(content){
41171             item.setContent(content);
41172         }
41173         return item;
41174     },
41175
41176     /**
41177      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41178      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41179      * @return {Roo.TabPanelItem}
41180      */
41181     getTab : function(id){
41182         return this.items[id];
41183     },
41184
41185     /**
41186      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41187      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41188      */
41189     hideTab : function(id){
41190         var t = this.items[id];
41191         if(!t.isHidden()){
41192            t.setHidden(true);
41193            this.hiddenCount++;
41194            this.autoSizeTabs();
41195         }
41196     },
41197
41198     /**
41199      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41200      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41201      */
41202     unhideTab : function(id){
41203         var t = this.items[id];
41204         if(t.isHidden()){
41205            t.setHidden(false);
41206            this.hiddenCount--;
41207            this.autoSizeTabs();
41208         }
41209     },
41210
41211     /**
41212      * Adds an existing {@link Roo.TabPanelItem}.
41213      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41214      */
41215     addTabItem : function(item)
41216     {
41217         this.items[item.id] = item;
41218         this.items.push(item);
41219         this.autoSizeTabs();
41220       //  if(this.resizeTabs){
41221     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41222   //         this.autoSizeTabs();
41223 //        }else{
41224 //            item.autoSize();
41225        // }
41226     },
41227
41228     /**
41229      * Removes a {@link Roo.TabPanelItem}.
41230      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41231      */
41232     removeTab : function(id){
41233         var items = this.items;
41234         var tab = items[id];
41235         if(!tab) { return; }
41236         var index = items.indexOf(tab);
41237         if(this.active == tab && items.length > 1){
41238             var newTab = this.getNextAvailable(index);
41239             if(newTab) {
41240                 newTab.activate();
41241             }
41242         }
41243         this.stripEl.dom.removeChild(tab.pnode.dom);
41244         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41245             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41246         }
41247         items.splice(index, 1);
41248         delete this.items[tab.id];
41249         tab.fireEvent("close", tab);
41250         tab.purgeListeners();
41251         this.autoSizeTabs();
41252     },
41253
41254     getNextAvailable : function(start){
41255         var items = this.items;
41256         var index = start;
41257         // look for a next tab that will slide over to
41258         // replace the one being removed
41259         while(index < items.length){
41260             var item = items[++index];
41261             if(item && !item.isHidden()){
41262                 return item;
41263             }
41264         }
41265         // if one isn't found select the previous tab (on the left)
41266         index = start;
41267         while(index >= 0){
41268             var item = items[--index];
41269             if(item && !item.isHidden()){
41270                 return item;
41271             }
41272         }
41273         return null;
41274     },
41275
41276     /**
41277      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41278      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41279      */
41280     disableTab : function(id){
41281         var tab = this.items[id];
41282         if(tab && this.active != tab){
41283             tab.disable();
41284         }
41285     },
41286
41287     /**
41288      * Enables a {@link Roo.TabPanelItem} that is disabled.
41289      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41290      */
41291     enableTab : function(id){
41292         var tab = this.items[id];
41293         tab.enable();
41294     },
41295
41296     /**
41297      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41298      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41299      * @return {Roo.TabPanelItem} The TabPanelItem.
41300      */
41301     activate : function(id)
41302     {
41303         //Roo.log('activite:'  + id);
41304         
41305         var tab = this.items[id];
41306         if(!tab){
41307             return null;
41308         }
41309         if(tab == this.active || tab.disabled){
41310             return tab;
41311         }
41312         var e = {};
41313         this.fireEvent("beforetabchange", this, e, tab);
41314         if(e.cancel !== true && !tab.disabled){
41315             if(this.active){
41316                 this.active.hide();
41317             }
41318             this.active = this.items[id];
41319             this.active.show();
41320             this.fireEvent("tabchange", this, this.active);
41321         }
41322         return tab;
41323     },
41324
41325     /**
41326      * Gets the active {@link Roo.TabPanelItem}.
41327      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41328      */
41329     getActiveTab : function(){
41330         return this.active;
41331     },
41332
41333     /**
41334      * Updates the tab body element to fit the height of the container element
41335      * for overflow scrolling
41336      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41337      */
41338     syncHeight : function(targetHeight){
41339         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41340         var bm = this.bodyEl.getMargins();
41341         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41342         this.bodyEl.setHeight(newHeight);
41343         return newHeight;
41344     },
41345
41346     onResize : function(){
41347         if(this.monitorResize){
41348             this.autoSizeTabs();
41349         }
41350     },
41351
41352     /**
41353      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41354      */
41355     beginUpdate : function(){
41356         this.updating = true;
41357     },
41358
41359     /**
41360      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41361      */
41362     endUpdate : function(){
41363         this.updating = false;
41364         this.autoSizeTabs();
41365     },
41366
41367     /**
41368      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41369      */
41370     autoSizeTabs : function()
41371     {
41372         var count = this.items.length;
41373         var vcount = count - this.hiddenCount;
41374         
41375         if (vcount < 2) {
41376             this.stripEl.hide();
41377         } else {
41378             this.stripEl.show();
41379         }
41380         
41381         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41382             return;
41383         }
41384         
41385         
41386         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41387         var availWidth = Math.floor(w / vcount);
41388         var b = this.stripBody;
41389         if(b.getWidth() > w){
41390             var tabs = this.items;
41391             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41392             if(availWidth < this.minTabWidth){
41393                 /*if(!this.sleft){    // incomplete scrolling code
41394                     this.createScrollButtons();
41395                 }
41396                 this.showScroll();
41397                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41398             }
41399         }else{
41400             if(this.currentTabWidth < this.preferredTabWidth){
41401                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41402             }
41403         }
41404     },
41405
41406     /**
41407      * Returns the number of tabs in this TabPanel.
41408      * @return {Number}
41409      */
41410      getCount : function(){
41411          return this.items.length;
41412      },
41413
41414     /**
41415      * Resizes all the tabs to the passed width
41416      * @param {Number} The new width
41417      */
41418     setTabWidth : function(width){
41419         this.currentTabWidth = width;
41420         for(var i = 0, len = this.items.length; i < len; i++) {
41421                 if(!this.items[i].isHidden()) {
41422                 this.items[i].setWidth(width);
41423             }
41424         }
41425     },
41426
41427     /**
41428      * Destroys this TabPanel
41429      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41430      */
41431     destroy : function(removeEl){
41432         Roo.EventManager.removeResizeListener(this.onResize, this);
41433         for(var i = 0, len = this.items.length; i < len; i++){
41434             this.items[i].purgeListeners();
41435         }
41436         if(removeEl === true){
41437             this.el.update("");
41438             this.el.remove();
41439         }
41440     },
41441     
41442     createStrip : function(container)
41443     {
41444         var strip = document.createElement("nav");
41445         strip.className = Roo.bootstrap.version == 4 ?
41446             "navbar-light bg-light" : 
41447             "navbar navbar-default"; //"x-tabs-wrap";
41448         container.appendChild(strip);
41449         return strip;
41450     },
41451     
41452     createStripList : function(strip)
41453     {
41454         // div wrapper for retard IE
41455         // returns the "tr" element.
41456         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41457         //'<div class="x-tabs-strip-wrap">'+
41458           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41459           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41460         return strip.firstChild; //.firstChild.firstChild.firstChild;
41461     },
41462     createBody : function(container)
41463     {
41464         var body = document.createElement("div");
41465         Roo.id(body, "tab-body");
41466         //Roo.fly(body).addClass("x-tabs-body");
41467         Roo.fly(body).addClass("tab-content");
41468         container.appendChild(body);
41469         return body;
41470     },
41471     createItemBody :function(bodyEl, id){
41472         var body = Roo.getDom(id);
41473         if(!body){
41474             body = document.createElement("div");
41475             body.id = id;
41476         }
41477         //Roo.fly(body).addClass("x-tabs-item-body");
41478         Roo.fly(body).addClass("tab-pane");
41479          bodyEl.insertBefore(body, bodyEl.firstChild);
41480         return body;
41481     },
41482     /** @private */
41483     createStripElements :  function(stripEl, text, closable, tpl)
41484     {
41485         var td = document.createElement("li"); // was td..
41486         td.className = 'nav-item';
41487         
41488         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41489         
41490         
41491         stripEl.appendChild(td);
41492         /*if(closable){
41493             td.className = "x-tabs-closable";
41494             if(!this.closeTpl){
41495                 this.closeTpl = new Roo.Template(
41496                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41497                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41498                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41499                 );
41500             }
41501             var el = this.closeTpl.overwrite(td, {"text": text});
41502             var close = el.getElementsByTagName("div")[0];
41503             var inner = el.getElementsByTagName("em")[0];
41504             return {"el": el, "close": close, "inner": inner};
41505         } else {
41506         */
41507         // not sure what this is..
41508 //            if(!this.tabTpl){
41509                 //this.tabTpl = new Roo.Template(
41510                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41511                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41512                 //);
41513 //                this.tabTpl = new Roo.Template(
41514 //                   '<a href="#">' +
41515 //                   '<span unselectable="on"' +
41516 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41517 //                            ' >{text}</span></a>'
41518 //                );
41519 //                
41520 //            }
41521
41522
41523             var template = tpl || this.tabTpl || false;
41524             
41525             if(!template){
41526                 template =  new Roo.Template(
41527                         Roo.bootstrap.version == 4 ? 
41528                             (
41529                                 '<a class="nav-link" href="#" unselectable="on"' +
41530                                      (this.disableTooltips ? '' : ' title="{text}"') +
41531                                      ' >{text}</a>'
41532                             ) : (
41533                                 '<a class="nav-link" href="#">' +
41534                                 '<span unselectable="on"' +
41535                                          (this.disableTooltips ? '' : ' title="{text}"') +
41536                                     ' >{text}</span></a>'
41537                             )
41538                 );
41539             }
41540             
41541             switch (typeof(template)) {
41542                 case 'object' :
41543                     break;
41544                 case 'string' :
41545                     template = new Roo.Template(template);
41546                     break;
41547                 default :
41548                     break;
41549             }
41550             
41551             var el = template.overwrite(td, {"text": text});
41552             
41553             var inner = el.getElementsByTagName("span")[0];
41554             
41555             return {"el": el, "inner": inner};
41556             
41557     }
41558         
41559     
41560 });
41561
41562 /**
41563  * @class Roo.TabPanelItem
41564  * @extends Roo.util.Observable
41565  * Represents an individual item (tab plus body) in a TabPanel.
41566  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41567  * @param {String} id The id of this TabPanelItem
41568  * @param {String} text The text for the tab of this TabPanelItem
41569  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41570  */
41571 Roo.bootstrap.panel.TabItem = function(config){
41572     /**
41573      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41574      * @type Roo.TabPanel
41575      */
41576     this.tabPanel = config.panel;
41577     /**
41578      * The id for this TabPanelItem
41579      * @type String
41580      */
41581     this.id = config.id;
41582     /** @private */
41583     this.disabled = false;
41584     /** @private */
41585     this.text = config.text;
41586     /** @private */
41587     this.loaded = false;
41588     this.closable = config.closable;
41589
41590     /**
41591      * The body element for this TabPanelItem.
41592      * @type Roo.Element
41593      */
41594     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41595     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41596     this.bodyEl.setStyle("display", "block");
41597     this.bodyEl.setStyle("zoom", "1");
41598     //this.hideAction();
41599
41600     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41601     /** @private */
41602     this.el = Roo.get(els.el);
41603     this.inner = Roo.get(els.inner, true);
41604      this.textEl = Roo.bootstrap.version == 4 ?
41605         this.el : Roo.get(this.el.dom.firstChild, true);
41606
41607     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41608     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41609
41610     
41611 //    this.el.on("mousedown", this.onTabMouseDown, this);
41612     this.el.on("click", this.onTabClick, this);
41613     /** @private */
41614     if(config.closable){
41615         var c = Roo.get(els.close, true);
41616         c.dom.title = this.closeText;
41617         c.addClassOnOver("close-over");
41618         c.on("click", this.closeClick, this);
41619      }
41620
41621     this.addEvents({
41622          /**
41623          * @event activate
41624          * Fires when this tab becomes the active tab.
41625          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41626          * @param {Roo.TabPanelItem} this
41627          */
41628         "activate": true,
41629         /**
41630          * @event beforeclose
41631          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41632          * @param {Roo.TabPanelItem} this
41633          * @param {Object} e Set cancel to true on this object to cancel the close.
41634          */
41635         "beforeclose": true,
41636         /**
41637          * @event close
41638          * Fires when this tab is closed.
41639          * @param {Roo.TabPanelItem} this
41640          */
41641          "close": true,
41642         /**
41643          * @event deactivate
41644          * Fires when this tab is no longer the active tab.
41645          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41646          * @param {Roo.TabPanelItem} this
41647          */
41648          "deactivate" : true
41649     });
41650     this.hidden = false;
41651
41652     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41653 };
41654
41655 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41656            {
41657     purgeListeners : function(){
41658        Roo.util.Observable.prototype.purgeListeners.call(this);
41659        this.el.removeAllListeners();
41660     },
41661     /**
41662      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41663      */
41664     show : function(){
41665         this.status_node.addClass("active");
41666         this.showAction();
41667         if(Roo.isOpera){
41668             this.tabPanel.stripWrap.repaint();
41669         }
41670         this.fireEvent("activate", this.tabPanel, this);
41671     },
41672
41673     /**
41674      * Returns true if this tab is the active tab.
41675      * @return {Boolean}
41676      */
41677     isActive : function(){
41678         return this.tabPanel.getActiveTab() == this;
41679     },
41680
41681     /**
41682      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41683      */
41684     hide : function(){
41685         this.status_node.removeClass("active");
41686         this.hideAction();
41687         this.fireEvent("deactivate", this.tabPanel, this);
41688     },
41689
41690     hideAction : function(){
41691         this.bodyEl.hide();
41692         this.bodyEl.setStyle("position", "absolute");
41693         this.bodyEl.setLeft("-20000px");
41694         this.bodyEl.setTop("-20000px");
41695     },
41696
41697     showAction : function(){
41698         this.bodyEl.setStyle("position", "relative");
41699         this.bodyEl.setTop("");
41700         this.bodyEl.setLeft("");
41701         this.bodyEl.show();
41702     },
41703
41704     /**
41705      * Set the tooltip for the tab.
41706      * @param {String} tooltip The tab's tooltip
41707      */
41708     setTooltip : function(text){
41709         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41710             this.textEl.dom.qtip = text;
41711             this.textEl.dom.removeAttribute('title');
41712         }else{
41713             this.textEl.dom.title = text;
41714         }
41715     },
41716
41717     onTabClick : function(e){
41718         e.preventDefault();
41719         this.tabPanel.activate(this.id);
41720     },
41721
41722     onTabMouseDown : function(e){
41723         e.preventDefault();
41724         this.tabPanel.activate(this.id);
41725     },
41726 /*
41727     getWidth : function(){
41728         return this.inner.getWidth();
41729     },
41730
41731     setWidth : function(width){
41732         var iwidth = width - this.linode.getPadding("lr");
41733         this.inner.setWidth(iwidth);
41734         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41735         this.linode.setWidth(width);
41736     },
41737 */
41738     /**
41739      * Show or hide the tab
41740      * @param {Boolean} hidden True to hide or false to show.
41741      */
41742     setHidden : function(hidden){
41743         this.hidden = hidden;
41744         this.linode.setStyle("display", hidden ? "none" : "");
41745     },
41746
41747     /**
41748      * Returns true if this tab is "hidden"
41749      * @return {Boolean}
41750      */
41751     isHidden : function(){
41752         return this.hidden;
41753     },
41754
41755     /**
41756      * Returns the text for this tab
41757      * @return {String}
41758      */
41759     getText : function(){
41760         return this.text;
41761     },
41762     /*
41763     autoSize : function(){
41764         //this.el.beginMeasure();
41765         this.textEl.setWidth(1);
41766         /*
41767          *  #2804 [new] Tabs in Roojs
41768          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41769          */
41770         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41771         //this.el.endMeasure();
41772     //},
41773
41774     /**
41775      * Sets the text for the tab (Note: this also sets the tooltip text)
41776      * @param {String} text The tab's text and tooltip
41777      */
41778     setText : function(text){
41779         this.text = text;
41780         this.textEl.update(text);
41781         this.setTooltip(text);
41782         //if(!this.tabPanel.resizeTabs){
41783         //    this.autoSize();
41784         //}
41785     },
41786     /**
41787      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41788      */
41789     activate : function(){
41790         this.tabPanel.activate(this.id);
41791     },
41792
41793     /**
41794      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41795      */
41796     disable : function(){
41797         if(this.tabPanel.active != this){
41798             this.disabled = true;
41799             this.status_node.addClass("disabled");
41800         }
41801     },
41802
41803     /**
41804      * Enables this TabPanelItem if it was previously disabled.
41805      */
41806     enable : function(){
41807         this.disabled = false;
41808         this.status_node.removeClass("disabled");
41809     },
41810
41811     /**
41812      * Sets the content for this TabPanelItem.
41813      * @param {String} content The content
41814      * @param {Boolean} loadScripts true to look for and load scripts
41815      */
41816     setContent : function(content, loadScripts){
41817         this.bodyEl.update(content, loadScripts);
41818     },
41819
41820     /**
41821      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41822      * @return {Roo.UpdateManager} The UpdateManager
41823      */
41824     getUpdateManager : function(){
41825         return this.bodyEl.getUpdateManager();
41826     },
41827
41828     /**
41829      * Set a URL to be used to load the content for this TabPanelItem.
41830      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41831      * @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)
41832      * @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)
41833      * @return {Roo.UpdateManager} The UpdateManager
41834      */
41835     setUrl : function(url, params, loadOnce){
41836         if(this.refreshDelegate){
41837             this.un('activate', this.refreshDelegate);
41838         }
41839         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41840         this.on("activate", this.refreshDelegate);
41841         return this.bodyEl.getUpdateManager();
41842     },
41843
41844     /** @private */
41845     _handleRefresh : function(url, params, loadOnce){
41846         if(!loadOnce || !this.loaded){
41847             var updater = this.bodyEl.getUpdateManager();
41848             updater.update(url, params, this._setLoaded.createDelegate(this));
41849         }
41850     },
41851
41852     /**
41853      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41854      *   Will fail silently if the setUrl method has not been called.
41855      *   This does not activate the panel, just updates its content.
41856      */
41857     refresh : function(){
41858         if(this.refreshDelegate){
41859            this.loaded = false;
41860            this.refreshDelegate();
41861         }
41862     },
41863
41864     /** @private */
41865     _setLoaded : function(){
41866         this.loaded = true;
41867     },
41868
41869     /** @private */
41870     closeClick : function(e){
41871         var o = {};
41872         e.stopEvent();
41873         this.fireEvent("beforeclose", this, o);
41874         if(o.cancel !== true){
41875             this.tabPanel.removeTab(this.id);
41876         }
41877     },
41878     /**
41879      * The text displayed in the tooltip for the close icon.
41880      * @type String
41881      */
41882     closeText : "Close this tab"
41883 });
41884 /**
41885 *    This script refer to:
41886 *    Title: International Telephone Input
41887 *    Author: Jack O'Connor
41888 *    Code version:  v12.1.12
41889 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41890 **/
41891
41892 Roo.bootstrap.PhoneInputData = function() {
41893     var d = [
41894       [
41895         "Afghanistan (‫افغانستان‬‎)",
41896         "af",
41897         "93"
41898       ],
41899       [
41900         "Albania (Shqipëri)",
41901         "al",
41902         "355"
41903       ],
41904       [
41905         "Algeria (‫الجزائر‬‎)",
41906         "dz",
41907         "213"
41908       ],
41909       [
41910         "American Samoa",
41911         "as",
41912         "1684"
41913       ],
41914       [
41915         "Andorra",
41916         "ad",
41917         "376"
41918       ],
41919       [
41920         "Angola",
41921         "ao",
41922         "244"
41923       ],
41924       [
41925         "Anguilla",
41926         "ai",
41927         "1264"
41928       ],
41929       [
41930         "Antigua and Barbuda",
41931         "ag",
41932         "1268"
41933       ],
41934       [
41935         "Argentina",
41936         "ar",
41937         "54"
41938       ],
41939       [
41940         "Armenia (Հայաստան)",
41941         "am",
41942         "374"
41943       ],
41944       [
41945         "Aruba",
41946         "aw",
41947         "297"
41948       ],
41949       [
41950         "Australia",
41951         "au",
41952         "61",
41953         0
41954       ],
41955       [
41956         "Austria (Österreich)",
41957         "at",
41958         "43"
41959       ],
41960       [
41961         "Azerbaijan (Azərbaycan)",
41962         "az",
41963         "994"
41964       ],
41965       [
41966         "Bahamas",
41967         "bs",
41968         "1242"
41969       ],
41970       [
41971         "Bahrain (‫البحرين‬‎)",
41972         "bh",
41973         "973"
41974       ],
41975       [
41976         "Bangladesh (বাংলাদেশ)",
41977         "bd",
41978         "880"
41979       ],
41980       [
41981         "Barbados",
41982         "bb",
41983         "1246"
41984       ],
41985       [
41986         "Belarus (Беларусь)",
41987         "by",
41988         "375"
41989       ],
41990       [
41991         "Belgium (België)",
41992         "be",
41993         "32"
41994       ],
41995       [
41996         "Belize",
41997         "bz",
41998         "501"
41999       ],
42000       [
42001         "Benin (Bénin)",
42002         "bj",
42003         "229"
42004       ],
42005       [
42006         "Bermuda",
42007         "bm",
42008         "1441"
42009       ],
42010       [
42011         "Bhutan (འབྲུག)",
42012         "bt",
42013         "975"
42014       ],
42015       [
42016         "Bolivia",
42017         "bo",
42018         "591"
42019       ],
42020       [
42021         "Bosnia and Herzegovina (Босна и Херцеговина)",
42022         "ba",
42023         "387"
42024       ],
42025       [
42026         "Botswana",
42027         "bw",
42028         "267"
42029       ],
42030       [
42031         "Brazil (Brasil)",
42032         "br",
42033         "55"
42034       ],
42035       [
42036         "British Indian Ocean Territory",
42037         "io",
42038         "246"
42039       ],
42040       [
42041         "British Virgin Islands",
42042         "vg",
42043         "1284"
42044       ],
42045       [
42046         "Brunei",
42047         "bn",
42048         "673"
42049       ],
42050       [
42051         "Bulgaria (България)",
42052         "bg",
42053         "359"
42054       ],
42055       [
42056         "Burkina Faso",
42057         "bf",
42058         "226"
42059       ],
42060       [
42061         "Burundi (Uburundi)",
42062         "bi",
42063         "257"
42064       ],
42065       [
42066         "Cambodia (កម្ពុជា)",
42067         "kh",
42068         "855"
42069       ],
42070       [
42071         "Cameroon (Cameroun)",
42072         "cm",
42073         "237"
42074       ],
42075       [
42076         "Canada",
42077         "ca",
42078         "1",
42079         1,
42080         ["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"]
42081       ],
42082       [
42083         "Cape Verde (Kabu Verdi)",
42084         "cv",
42085         "238"
42086       ],
42087       [
42088         "Caribbean Netherlands",
42089         "bq",
42090         "599",
42091         1
42092       ],
42093       [
42094         "Cayman Islands",
42095         "ky",
42096         "1345"
42097       ],
42098       [
42099         "Central African Republic (République centrafricaine)",
42100         "cf",
42101         "236"
42102       ],
42103       [
42104         "Chad (Tchad)",
42105         "td",
42106         "235"
42107       ],
42108       [
42109         "Chile",
42110         "cl",
42111         "56"
42112       ],
42113       [
42114         "China (中国)",
42115         "cn",
42116         "86"
42117       ],
42118       [
42119         "Christmas Island",
42120         "cx",
42121         "61",
42122         2
42123       ],
42124       [
42125         "Cocos (Keeling) Islands",
42126         "cc",
42127         "61",
42128         1
42129       ],
42130       [
42131         "Colombia",
42132         "co",
42133         "57"
42134       ],
42135       [
42136         "Comoros (‫جزر القمر‬‎)",
42137         "km",
42138         "269"
42139       ],
42140       [
42141         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42142         "cd",
42143         "243"
42144       ],
42145       [
42146         "Congo (Republic) (Congo-Brazzaville)",
42147         "cg",
42148         "242"
42149       ],
42150       [
42151         "Cook Islands",
42152         "ck",
42153         "682"
42154       ],
42155       [
42156         "Costa Rica",
42157         "cr",
42158         "506"
42159       ],
42160       [
42161         "Côte d’Ivoire",
42162         "ci",
42163         "225"
42164       ],
42165       [
42166         "Croatia (Hrvatska)",
42167         "hr",
42168         "385"
42169       ],
42170       [
42171         "Cuba",
42172         "cu",
42173         "53"
42174       ],
42175       [
42176         "Curaçao",
42177         "cw",
42178         "599",
42179         0
42180       ],
42181       [
42182         "Cyprus (Κύπρος)",
42183         "cy",
42184         "357"
42185       ],
42186       [
42187         "Czech Republic (Česká republika)",
42188         "cz",
42189         "420"
42190       ],
42191       [
42192         "Denmark (Danmark)",
42193         "dk",
42194         "45"
42195       ],
42196       [
42197         "Djibouti",
42198         "dj",
42199         "253"
42200       ],
42201       [
42202         "Dominica",
42203         "dm",
42204         "1767"
42205       ],
42206       [
42207         "Dominican Republic (República Dominicana)",
42208         "do",
42209         "1",
42210         2,
42211         ["809", "829", "849"]
42212       ],
42213       [
42214         "Ecuador",
42215         "ec",
42216         "593"
42217       ],
42218       [
42219         "Egypt (‫مصر‬‎)",
42220         "eg",
42221         "20"
42222       ],
42223       [
42224         "El Salvador",
42225         "sv",
42226         "503"
42227       ],
42228       [
42229         "Equatorial Guinea (Guinea Ecuatorial)",
42230         "gq",
42231         "240"
42232       ],
42233       [
42234         "Eritrea",
42235         "er",
42236         "291"
42237       ],
42238       [
42239         "Estonia (Eesti)",
42240         "ee",
42241         "372"
42242       ],
42243       [
42244         "Ethiopia",
42245         "et",
42246         "251"
42247       ],
42248       [
42249         "Falkland Islands (Islas Malvinas)",
42250         "fk",
42251         "500"
42252       ],
42253       [
42254         "Faroe Islands (Føroyar)",
42255         "fo",
42256         "298"
42257       ],
42258       [
42259         "Fiji",
42260         "fj",
42261         "679"
42262       ],
42263       [
42264         "Finland (Suomi)",
42265         "fi",
42266         "358",
42267         0
42268       ],
42269       [
42270         "France",
42271         "fr",
42272         "33"
42273       ],
42274       [
42275         "French Guiana (Guyane française)",
42276         "gf",
42277         "594"
42278       ],
42279       [
42280         "French Polynesia (Polynésie française)",
42281         "pf",
42282         "689"
42283       ],
42284       [
42285         "Gabon",
42286         "ga",
42287         "241"
42288       ],
42289       [
42290         "Gambia",
42291         "gm",
42292         "220"
42293       ],
42294       [
42295         "Georgia (საქართველო)",
42296         "ge",
42297         "995"
42298       ],
42299       [
42300         "Germany (Deutschland)",
42301         "de",
42302         "49"
42303       ],
42304       [
42305         "Ghana (Gaana)",
42306         "gh",
42307         "233"
42308       ],
42309       [
42310         "Gibraltar",
42311         "gi",
42312         "350"
42313       ],
42314       [
42315         "Greece (Ελλάδα)",
42316         "gr",
42317         "30"
42318       ],
42319       [
42320         "Greenland (Kalaallit Nunaat)",
42321         "gl",
42322         "299"
42323       ],
42324       [
42325         "Grenada",
42326         "gd",
42327         "1473"
42328       ],
42329       [
42330         "Guadeloupe",
42331         "gp",
42332         "590",
42333         0
42334       ],
42335       [
42336         "Guam",
42337         "gu",
42338         "1671"
42339       ],
42340       [
42341         "Guatemala",
42342         "gt",
42343         "502"
42344       ],
42345       [
42346         "Guernsey",
42347         "gg",
42348         "44",
42349         1
42350       ],
42351       [
42352         "Guinea (Guinée)",
42353         "gn",
42354         "224"
42355       ],
42356       [
42357         "Guinea-Bissau (Guiné Bissau)",
42358         "gw",
42359         "245"
42360       ],
42361       [
42362         "Guyana",
42363         "gy",
42364         "592"
42365       ],
42366       [
42367         "Haiti",
42368         "ht",
42369         "509"
42370       ],
42371       [
42372         "Honduras",
42373         "hn",
42374         "504"
42375       ],
42376       [
42377         "Hong Kong (香港)",
42378         "hk",
42379         "852"
42380       ],
42381       [
42382         "Hungary (Magyarország)",
42383         "hu",
42384         "36"
42385       ],
42386       [
42387         "Iceland (Ísland)",
42388         "is",
42389         "354"
42390       ],
42391       [
42392         "India (भारत)",
42393         "in",
42394         "91"
42395       ],
42396       [
42397         "Indonesia",
42398         "id",
42399         "62"
42400       ],
42401       [
42402         "Iran (‫ایران‬‎)",
42403         "ir",
42404         "98"
42405       ],
42406       [
42407         "Iraq (‫العراق‬‎)",
42408         "iq",
42409         "964"
42410       ],
42411       [
42412         "Ireland",
42413         "ie",
42414         "353"
42415       ],
42416       [
42417         "Isle of Man",
42418         "im",
42419         "44",
42420         2
42421       ],
42422       [
42423         "Israel (‫ישראל‬‎)",
42424         "il",
42425         "972"
42426       ],
42427       [
42428         "Italy (Italia)",
42429         "it",
42430         "39",
42431         0
42432       ],
42433       [
42434         "Jamaica",
42435         "jm",
42436         "1876"
42437       ],
42438       [
42439         "Japan (日本)",
42440         "jp",
42441         "81"
42442       ],
42443       [
42444         "Jersey",
42445         "je",
42446         "44",
42447         3
42448       ],
42449       [
42450         "Jordan (‫الأردن‬‎)",
42451         "jo",
42452         "962"
42453       ],
42454       [
42455         "Kazakhstan (Казахстан)",
42456         "kz",
42457         "7",
42458         1
42459       ],
42460       [
42461         "Kenya",
42462         "ke",
42463         "254"
42464       ],
42465       [
42466         "Kiribati",
42467         "ki",
42468         "686"
42469       ],
42470       [
42471         "Kosovo",
42472         "xk",
42473         "383"
42474       ],
42475       [
42476         "Kuwait (‫الكويت‬‎)",
42477         "kw",
42478         "965"
42479       ],
42480       [
42481         "Kyrgyzstan (Кыргызстан)",
42482         "kg",
42483         "996"
42484       ],
42485       [
42486         "Laos (ລາວ)",
42487         "la",
42488         "856"
42489       ],
42490       [
42491         "Latvia (Latvija)",
42492         "lv",
42493         "371"
42494       ],
42495       [
42496         "Lebanon (‫لبنان‬‎)",
42497         "lb",
42498         "961"
42499       ],
42500       [
42501         "Lesotho",
42502         "ls",
42503         "266"
42504       ],
42505       [
42506         "Liberia",
42507         "lr",
42508         "231"
42509       ],
42510       [
42511         "Libya (‫ليبيا‬‎)",
42512         "ly",
42513         "218"
42514       ],
42515       [
42516         "Liechtenstein",
42517         "li",
42518         "423"
42519       ],
42520       [
42521         "Lithuania (Lietuva)",
42522         "lt",
42523         "370"
42524       ],
42525       [
42526         "Luxembourg",
42527         "lu",
42528         "352"
42529       ],
42530       [
42531         "Macau (澳門)",
42532         "mo",
42533         "853"
42534       ],
42535       [
42536         "Macedonia (FYROM) (Македонија)",
42537         "mk",
42538         "389"
42539       ],
42540       [
42541         "Madagascar (Madagasikara)",
42542         "mg",
42543         "261"
42544       ],
42545       [
42546         "Malawi",
42547         "mw",
42548         "265"
42549       ],
42550       [
42551         "Malaysia",
42552         "my",
42553         "60"
42554       ],
42555       [
42556         "Maldives",
42557         "mv",
42558         "960"
42559       ],
42560       [
42561         "Mali",
42562         "ml",
42563         "223"
42564       ],
42565       [
42566         "Malta",
42567         "mt",
42568         "356"
42569       ],
42570       [
42571         "Marshall Islands",
42572         "mh",
42573         "692"
42574       ],
42575       [
42576         "Martinique",
42577         "mq",
42578         "596"
42579       ],
42580       [
42581         "Mauritania (‫موريتانيا‬‎)",
42582         "mr",
42583         "222"
42584       ],
42585       [
42586         "Mauritius (Moris)",
42587         "mu",
42588         "230"
42589       ],
42590       [
42591         "Mayotte",
42592         "yt",
42593         "262",
42594         1
42595       ],
42596       [
42597         "Mexico (México)",
42598         "mx",
42599         "52"
42600       ],
42601       [
42602         "Micronesia",
42603         "fm",
42604         "691"
42605       ],
42606       [
42607         "Moldova (Republica Moldova)",
42608         "md",
42609         "373"
42610       ],
42611       [
42612         "Monaco",
42613         "mc",
42614         "377"
42615       ],
42616       [
42617         "Mongolia (Монгол)",
42618         "mn",
42619         "976"
42620       ],
42621       [
42622         "Montenegro (Crna Gora)",
42623         "me",
42624         "382"
42625       ],
42626       [
42627         "Montserrat",
42628         "ms",
42629         "1664"
42630       ],
42631       [
42632         "Morocco (‫المغرب‬‎)",
42633         "ma",
42634         "212",
42635         0
42636       ],
42637       [
42638         "Mozambique (Moçambique)",
42639         "mz",
42640         "258"
42641       ],
42642       [
42643         "Myanmar (Burma) (မြန်မာ)",
42644         "mm",
42645         "95"
42646       ],
42647       [
42648         "Namibia (Namibië)",
42649         "na",
42650         "264"
42651       ],
42652       [
42653         "Nauru",
42654         "nr",
42655         "674"
42656       ],
42657       [
42658         "Nepal (नेपाल)",
42659         "np",
42660         "977"
42661       ],
42662       [
42663         "Netherlands (Nederland)",
42664         "nl",
42665         "31"
42666       ],
42667       [
42668         "New Caledonia (Nouvelle-Calédonie)",
42669         "nc",
42670         "687"
42671       ],
42672       [
42673         "New Zealand",
42674         "nz",
42675         "64"
42676       ],
42677       [
42678         "Nicaragua",
42679         "ni",
42680         "505"
42681       ],
42682       [
42683         "Niger (Nijar)",
42684         "ne",
42685         "227"
42686       ],
42687       [
42688         "Nigeria",
42689         "ng",
42690         "234"
42691       ],
42692       [
42693         "Niue",
42694         "nu",
42695         "683"
42696       ],
42697       [
42698         "Norfolk Island",
42699         "nf",
42700         "672"
42701       ],
42702       [
42703         "North Korea (조선 민주주의 인민 공화국)",
42704         "kp",
42705         "850"
42706       ],
42707       [
42708         "Northern Mariana Islands",
42709         "mp",
42710         "1670"
42711       ],
42712       [
42713         "Norway (Norge)",
42714         "no",
42715         "47",
42716         0
42717       ],
42718       [
42719         "Oman (‫عُمان‬‎)",
42720         "om",
42721         "968"
42722       ],
42723       [
42724         "Pakistan (‫پاکستان‬‎)",
42725         "pk",
42726         "92"
42727       ],
42728       [
42729         "Palau",
42730         "pw",
42731         "680"
42732       ],
42733       [
42734         "Palestine (‫فلسطين‬‎)",
42735         "ps",
42736         "970"
42737       ],
42738       [
42739         "Panama (Panamá)",
42740         "pa",
42741         "507"
42742       ],
42743       [
42744         "Papua New Guinea",
42745         "pg",
42746         "675"
42747       ],
42748       [
42749         "Paraguay",
42750         "py",
42751         "595"
42752       ],
42753       [
42754         "Peru (Perú)",
42755         "pe",
42756         "51"
42757       ],
42758       [
42759         "Philippines",
42760         "ph",
42761         "63"
42762       ],
42763       [
42764         "Poland (Polska)",
42765         "pl",
42766         "48"
42767       ],
42768       [
42769         "Portugal",
42770         "pt",
42771         "351"
42772       ],
42773       [
42774         "Puerto Rico",
42775         "pr",
42776         "1",
42777         3,
42778         ["787", "939"]
42779       ],
42780       [
42781         "Qatar (‫قطر‬‎)",
42782         "qa",
42783         "974"
42784       ],
42785       [
42786         "Réunion (La Réunion)",
42787         "re",
42788         "262",
42789         0
42790       ],
42791       [
42792         "Romania (România)",
42793         "ro",
42794         "40"
42795       ],
42796       [
42797         "Russia (Россия)",
42798         "ru",
42799         "7",
42800         0
42801       ],
42802       [
42803         "Rwanda",
42804         "rw",
42805         "250"
42806       ],
42807       [
42808         "Saint Barthélemy",
42809         "bl",
42810         "590",
42811         1
42812       ],
42813       [
42814         "Saint Helena",
42815         "sh",
42816         "290"
42817       ],
42818       [
42819         "Saint Kitts and Nevis",
42820         "kn",
42821         "1869"
42822       ],
42823       [
42824         "Saint Lucia",
42825         "lc",
42826         "1758"
42827       ],
42828       [
42829         "Saint Martin (Saint-Martin (partie française))",
42830         "mf",
42831         "590",
42832         2
42833       ],
42834       [
42835         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42836         "pm",
42837         "508"
42838       ],
42839       [
42840         "Saint Vincent and the Grenadines",
42841         "vc",
42842         "1784"
42843       ],
42844       [
42845         "Samoa",
42846         "ws",
42847         "685"
42848       ],
42849       [
42850         "San Marino",
42851         "sm",
42852         "378"
42853       ],
42854       [
42855         "São Tomé and Príncipe (São Tomé e Príncipe)",
42856         "st",
42857         "239"
42858       ],
42859       [
42860         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42861         "sa",
42862         "966"
42863       ],
42864       [
42865         "Senegal (Sénégal)",
42866         "sn",
42867         "221"
42868       ],
42869       [
42870         "Serbia (Србија)",
42871         "rs",
42872         "381"
42873       ],
42874       [
42875         "Seychelles",
42876         "sc",
42877         "248"
42878       ],
42879       [
42880         "Sierra Leone",
42881         "sl",
42882         "232"
42883       ],
42884       [
42885         "Singapore",
42886         "sg",
42887         "65"
42888       ],
42889       [
42890         "Sint Maarten",
42891         "sx",
42892         "1721"
42893       ],
42894       [
42895         "Slovakia (Slovensko)",
42896         "sk",
42897         "421"
42898       ],
42899       [
42900         "Slovenia (Slovenija)",
42901         "si",
42902         "386"
42903       ],
42904       [
42905         "Solomon Islands",
42906         "sb",
42907         "677"
42908       ],
42909       [
42910         "Somalia (Soomaaliya)",
42911         "so",
42912         "252"
42913       ],
42914       [
42915         "South Africa",
42916         "za",
42917         "27"
42918       ],
42919       [
42920         "South Korea (대한민국)",
42921         "kr",
42922         "82"
42923       ],
42924       [
42925         "South Sudan (‫جنوب السودان‬‎)",
42926         "ss",
42927         "211"
42928       ],
42929       [
42930         "Spain (España)",
42931         "es",
42932         "34"
42933       ],
42934       [
42935         "Sri Lanka (ශ්‍රී ලංකාව)",
42936         "lk",
42937         "94"
42938       ],
42939       [
42940         "Sudan (‫السودان‬‎)",
42941         "sd",
42942         "249"
42943       ],
42944       [
42945         "Suriname",
42946         "sr",
42947         "597"
42948       ],
42949       [
42950         "Svalbard and Jan Mayen",
42951         "sj",
42952         "47",
42953         1
42954       ],
42955       [
42956         "Swaziland",
42957         "sz",
42958         "268"
42959       ],
42960       [
42961         "Sweden (Sverige)",
42962         "se",
42963         "46"
42964       ],
42965       [
42966         "Switzerland (Schweiz)",
42967         "ch",
42968         "41"
42969       ],
42970       [
42971         "Syria (‫سوريا‬‎)",
42972         "sy",
42973         "963"
42974       ],
42975       [
42976         "Taiwan (台灣)",
42977         "tw",
42978         "886"
42979       ],
42980       [
42981         "Tajikistan",
42982         "tj",
42983         "992"
42984       ],
42985       [
42986         "Tanzania",
42987         "tz",
42988         "255"
42989       ],
42990       [
42991         "Thailand (ไทย)",
42992         "th",
42993         "66"
42994       ],
42995       [
42996         "Timor-Leste",
42997         "tl",
42998         "670"
42999       ],
43000       [
43001         "Togo",
43002         "tg",
43003         "228"
43004       ],
43005       [
43006         "Tokelau",
43007         "tk",
43008         "690"
43009       ],
43010       [
43011         "Tonga",
43012         "to",
43013         "676"
43014       ],
43015       [
43016         "Trinidad and Tobago",
43017         "tt",
43018         "1868"
43019       ],
43020       [
43021         "Tunisia (‫تونس‬‎)",
43022         "tn",
43023         "216"
43024       ],
43025       [
43026         "Turkey (Türkiye)",
43027         "tr",
43028         "90"
43029       ],
43030       [
43031         "Turkmenistan",
43032         "tm",
43033         "993"
43034       ],
43035       [
43036         "Turks and Caicos Islands",
43037         "tc",
43038         "1649"
43039       ],
43040       [
43041         "Tuvalu",
43042         "tv",
43043         "688"
43044       ],
43045       [
43046         "U.S. Virgin Islands",
43047         "vi",
43048         "1340"
43049       ],
43050       [
43051         "Uganda",
43052         "ug",
43053         "256"
43054       ],
43055       [
43056         "Ukraine (Україна)",
43057         "ua",
43058         "380"
43059       ],
43060       [
43061         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
43062         "ae",
43063         "971"
43064       ],
43065       [
43066         "United Kingdom",
43067         "gb",
43068         "44",
43069         0
43070       ],
43071       [
43072         "United States",
43073         "us",
43074         "1",
43075         0
43076       ],
43077       [
43078         "Uruguay",
43079         "uy",
43080         "598"
43081       ],
43082       [
43083         "Uzbekistan (Oʻzbekiston)",
43084         "uz",
43085         "998"
43086       ],
43087       [
43088         "Vanuatu",
43089         "vu",
43090         "678"
43091       ],
43092       [
43093         "Vatican City (Città del Vaticano)",
43094         "va",
43095         "39",
43096         1
43097       ],
43098       [
43099         "Venezuela",
43100         "ve",
43101         "58"
43102       ],
43103       [
43104         "Vietnam (Việt Nam)",
43105         "vn",
43106         "84"
43107       ],
43108       [
43109         "Wallis and Futuna (Wallis-et-Futuna)",
43110         "wf",
43111         "681"
43112       ],
43113       [
43114         "Western Sahara (‫الصحراء الغربية‬‎)",
43115         "eh",
43116         "212",
43117         1
43118       ],
43119       [
43120         "Yemen (‫اليمن‬‎)",
43121         "ye",
43122         "967"
43123       ],
43124       [
43125         "Zambia",
43126         "zm",
43127         "260"
43128       ],
43129       [
43130         "Zimbabwe",
43131         "zw",
43132         "263"
43133       ],
43134       [
43135         "Åland Islands",
43136         "ax",
43137         "358",
43138         1
43139       ]
43140   ];
43141   
43142   return d;
43143 }/**
43144 *    This script refer to:
43145 *    Title: International Telephone Input
43146 *    Author: Jack O'Connor
43147 *    Code version:  v12.1.12
43148 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43149 **/
43150
43151 /**
43152  * @class Roo.bootstrap.PhoneInput
43153  * @extends Roo.bootstrap.TriggerField
43154  * An input with International dial-code selection
43155  
43156  * @cfg {String} defaultDialCode default '+852'
43157  * @cfg {Array} preferedCountries default []
43158   
43159  * @constructor
43160  * Create a new PhoneInput.
43161  * @param {Object} config Configuration options
43162  */
43163
43164 Roo.bootstrap.PhoneInput = function(config) {
43165     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43166 };
43167
43168 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43169         
43170         listWidth: undefined,
43171         
43172         selectedClass: 'active',
43173         
43174         invalidClass : "has-warning",
43175         
43176         validClass: 'has-success',
43177         
43178         allowed: '0123456789',
43179         
43180         max_length: 15,
43181         
43182         /**
43183          * @cfg {String} defaultDialCode The default dial code when initializing the input
43184          */
43185         defaultDialCode: '+852',
43186         
43187         /**
43188          * @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
43189          */
43190         preferedCountries: false,
43191         
43192         getAutoCreate : function()
43193         {
43194             var data = Roo.bootstrap.PhoneInputData();
43195             var align = this.labelAlign || this.parentLabelAlign();
43196             var id = Roo.id();
43197             
43198             this.allCountries = [];
43199             this.dialCodeMapping = [];
43200             
43201             for (var i = 0; i < data.length; i++) {
43202               var c = data[i];
43203               this.allCountries[i] = {
43204                 name: c[0],
43205                 iso2: c[1],
43206                 dialCode: c[2],
43207                 priority: c[3] || 0,
43208                 areaCodes: c[4] || null
43209               };
43210               this.dialCodeMapping[c[2]] = {
43211                   name: c[0],
43212                   iso2: c[1],
43213                   priority: c[3] || 0,
43214                   areaCodes: c[4] || null
43215               };
43216             }
43217             
43218             var cfg = {
43219                 cls: 'form-group',
43220                 cn: []
43221             };
43222             
43223             var input =  {
43224                 tag: 'input',
43225                 id : id,
43226                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43227                 maxlength: this.max_length,
43228                 cls : 'form-control tel-input',
43229                 autocomplete: 'new-password'
43230             };
43231             
43232             var hiddenInput = {
43233                 tag: 'input',
43234                 type: 'hidden',
43235                 cls: 'hidden-tel-input'
43236             };
43237             
43238             if (this.name) {
43239                 hiddenInput.name = this.name;
43240             }
43241             
43242             if (this.disabled) {
43243                 input.disabled = true;
43244             }
43245             
43246             var flag_container = {
43247                 tag: 'div',
43248                 cls: 'flag-box',
43249                 cn: [
43250                     {
43251                         tag: 'div',
43252                         cls: 'flag'
43253                     },
43254                     {
43255                         tag: 'div',
43256                         cls: 'caret'
43257                     }
43258                 ]
43259             };
43260             
43261             var box = {
43262                 tag: 'div',
43263                 cls: this.hasFeedback ? 'has-feedback' : '',
43264                 cn: [
43265                     hiddenInput,
43266                     input,
43267                     {
43268                         tag: 'input',
43269                         cls: 'dial-code-holder',
43270                         disabled: true
43271                     }
43272                 ]
43273             };
43274             
43275             var container = {
43276                 cls: 'roo-select2-container input-group',
43277                 cn: [
43278                     flag_container,
43279                     box
43280                 ]
43281             };
43282             
43283             if (this.fieldLabel.length) {
43284                 var indicator = {
43285                     tag: 'i',
43286                     tooltip: 'This field is required'
43287                 };
43288                 
43289                 var label = {
43290                     tag: 'label',
43291                     'for':  id,
43292                     cls: 'control-label',
43293                     cn: []
43294                 };
43295                 
43296                 var label_text = {
43297                     tag: 'span',
43298                     html: this.fieldLabel
43299                 };
43300                 
43301                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43302                 label.cn = [
43303                     indicator,
43304                     label_text
43305                 ];
43306                 
43307                 if(this.indicatorpos == 'right') {
43308                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43309                     label.cn = [
43310                         label_text,
43311                         indicator
43312                     ];
43313                 }
43314                 
43315                 if(align == 'left') {
43316                     container = {
43317                         tag: 'div',
43318                         cn: [
43319                             container
43320                         ]
43321                     };
43322                     
43323                     if(this.labelWidth > 12){
43324                         label.style = "width: " + this.labelWidth + 'px';
43325                     }
43326                     if(this.labelWidth < 13 && this.labelmd == 0){
43327                         this.labelmd = this.labelWidth;
43328                     }
43329                     if(this.labellg > 0){
43330                         label.cls += ' col-lg-' + this.labellg;
43331                         input.cls += ' col-lg-' + (12 - this.labellg);
43332                     }
43333                     if(this.labelmd > 0){
43334                         label.cls += ' col-md-' + this.labelmd;
43335                         container.cls += ' col-md-' + (12 - this.labelmd);
43336                     }
43337                     if(this.labelsm > 0){
43338                         label.cls += ' col-sm-' + this.labelsm;
43339                         container.cls += ' col-sm-' + (12 - this.labelsm);
43340                     }
43341                     if(this.labelxs > 0){
43342                         label.cls += ' col-xs-' + this.labelxs;
43343                         container.cls += ' col-xs-' + (12 - this.labelxs);
43344                     }
43345                 }
43346             }
43347             
43348             cfg.cn = [
43349                 label,
43350                 container
43351             ];
43352             
43353             var settings = this;
43354             
43355             ['xs','sm','md','lg'].map(function(size){
43356                 if (settings[size]) {
43357                     cfg.cls += ' col-' + size + '-' + settings[size];
43358                 }
43359             });
43360             
43361             this.store = new Roo.data.Store({
43362                 proxy : new Roo.data.MemoryProxy({}),
43363                 reader : new Roo.data.JsonReader({
43364                     fields : [
43365                         {
43366                             'name' : 'name',
43367                             'type' : 'string'
43368                         },
43369                         {
43370                             'name' : 'iso2',
43371                             'type' : 'string'
43372                         },
43373                         {
43374                             'name' : 'dialCode',
43375                             'type' : 'string'
43376                         },
43377                         {
43378                             'name' : 'priority',
43379                             'type' : 'string'
43380                         },
43381                         {
43382                             'name' : 'areaCodes',
43383                             'type' : 'string'
43384                         }
43385                     ]
43386                 })
43387             });
43388             
43389             if(!this.preferedCountries) {
43390                 this.preferedCountries = [
43391                     'hk',
43392                     'gb',
43393                     'us'
43394                 ];
43395             }
43396             
43397             var p = this.preferedCountries.reverse();
43398             
43399             if(p) {
43400                 for (var i = 0; i < p.length; i++) {
43401                     for (var j = 0; j < this.allCountries.length; j++) {
43402                         if(this.allCountries[j].iso2 == p[i]) {
43403                             var t = this.allCountries[j];
43404                             this.allCountries.splice(j,1);
43405                             this.allCountries.unshift(t);
43406                         }
43407                     } 
43408                 }
43409             }
43410             
43411             this.store.proxy.data = {
43412                 success: true,
43413                 data: this.allCountries
43414             };
43415             
43416             return cfg;
43417         },
43418         
43419         initEvents : function()
43420         {
43421             this.createList();
43422             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43423             
43424             this.indicator = this.indicatorEl();
43425             this.flag = this.flagEl();
43426             this.dialCodeHolder = this.dialCodeHolderEl();
43427             
43428             this.trigger = this.el.select('div.flag-box',true).first();
43429             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43430             
43431             var _this = this;
43432             
43433             (function(){
43434                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43435                 _this.list.setWidth(lw);
43436             }).defer(100);
43437             
43438             this.list.on('mouseover', this.onViewOver, this);
43439             this.list.on('mousemove', this.onViewMove, this);
43440             this.inputEl().on("keyup", this.onKeyUp, this);
43441             this.inputEl().on("keypress", this.onKeyPress, this);
43442             
43443             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43444
43445             this.view = new Roo.View(this.list, this.tpl, {
43446                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43447             });
43448             
43449             this.view.on('click', this.onViewClick, this);
43450             this.setValue(this.defaultDialCode);
43451         },
43452         
43453         onTriggerClick : function(e)
43454         {
43455             Roo.log('trigger click');
43456             if(this.disabled){
43457                 return;
43458             }
43459             
43460             if(this.isExpanded()){
43461                 this.collapse();
43462                 this.hasFocus = false;
43463             }else {
43464                 this.store.load({});
43465                 this.hasFocus = true;
43466                 this.expand();
43467             }
43468         },
43469         
43470         isExpanded : function()
43471         {
43472             return this.list.isVisible();
43473         },
43474         
43475         collapse : function()
43476         {
43477             if(!this.isExpanded()){
43478                 return;
43479             }
43480             this.list.hide();
43481             Roo.get(document).un('mousedown', this.collapseIf, this);
43482             Roo.get(document).un('mousewheel', this.collapseIf, this);
43483             this.fireEvent('collapse', this);
43484             this.validate();
43485         },
43486         
43487         expand : function()
43488         {
43489             Roo.log('expand');
43490
43491             if(this.isExpanded() || !this.hasFocus){
43492                 return;
43493             }
43494             
43495             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43496             this.list.setWidth(lw);
43497             
43498             this.list.show();
43499             this.restrictHeight();
43500             
43501             Roo.get(document).on('mousedown', this.collapseIf, this);
43502             Roo.get(document).on('mousewheel', this.collapseIf, this);
43503             
43504             this.fireEvent('expand', this);
43505         },
43506         
43507         restrictHeight : function()
43508         {
43509             this.list.alignTo(this.inputEl(), this.listAlign);
43510             this.list.alignTo(this.inputEl(), this.listAlign);
43511         },
43512         
43513         onViewOver : function(e, t)
43514         {
43515             if(this.inKeyMode){
43516                 return;
43517             }
43518             var item = this.view.findItemFromChild(t);
43519             
43520             if(item){
43521                 var index = this.view.indexOf(item);
43522                 this.select(index, false);
43523             }
43524         },
43525
43526         // private
43527         onViewClick : function(view, doFocus, el, e)
43528         {
43529             var index = this.view.getSelectedIndexes()[0];
43530             
43531             var r = this.store.getAt(index);
43532             
43533             if(r){
43534                 this.onSelect(r, index);
43535             }
43536             if(doFocus !== false && !this.blockFocus){
43537                 this.inputEl().focus();
43538             }
43539         },
43540         
43541         onViewMove : function(e, t)
43542         {
43543             this.inKeyMode = false;
43544         },
43545         
43546         select : function(index, scrollIntoView)
43547         {
43548             this.selectedIndex = index;
43549             this.view.select(index);
43550             if(scrollIntoView !== false){
43551                 var el = this.view.getNode(index);
43552                 if(el){
43553                     this.list.scrollChildIntoView(el, false);
43554                 }
43555             }
43556         },
43557         
43558         createList : function()
43559         {
43560             this.list = Roo.get(document.body).createChild({
43561                 tag: 'ul',
43562                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43563                 style: 'display:none'
43564             });
43565             
43566             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43567         },
43568         
43569         collapseIf : function(e)
43570         {
43571             var in_combo  = e.within(this.el);
43572             var in_list =  e.within(this.list);
43573             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43574             
43575             if (in_combo || in_list || is_list) {
43576                 return;
43577             }
43578             this.collapse();
43579         },
43580         
43581         onSelect : function(record, index)
43582         {
43583             if(this.fireEvent('beforeselect', this, record, index) !== false){
43584                 
43585                 this.setFlagClass(record.data.iso2);
43586                 this.setDialCode(record.data.dialCode);
43587                 this.hasFocus = false;
43588                 this.collapse();
43589                 this.fireEvent('select', this, record, index);
43590             }
43591         },
43592         
43593         flagEl : function()
43594         {
43595             var flag = this.el.select('div.flag',true).first();
43596             if(!flag){
43597                 return false;
43598             }
43599             return flag;
43600         },
43601         
43602         dialCodeHolderEl : function()
43603         {
43604             var d = this.el.select('input.dial-code-holder',true).first();
43605             if(!d){
43606                 return false;
43607             }
43608             return d;
43609         },
43610         
43611         setDialCode : function(v)
43612         {
43613             this.dialCodeHolder.dom.value = '+'+v;
43614         },
43615         
43616         setFlagClass : function(n)
43617         {
43618             this.flag.dom.className = 'flag '+n;
43619         },
43620         
43621         getValue : function()
43622         {
43623             var v = this.inputEl().getValue();
43624             if(this.dialCodeHolder) {
43625                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43626             }
43627             return v;
43628         },
43629         
43630         setValue : function(v)
43631         {
43632             var d = this.getDialCode(v);
43633             
43634             //invalid dial code
43635             if(v.length == 0 || !d || d.length == 0) {
43636                 if(this.rendered){
43637                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43638                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43639                 }
43640                 return;
43641             }
43642             
43643             //valid dial code
43644             this.setFlagClass(this.dialCodeMapping[d].iso2);
43645             this.setDialCode(d);
43646             this.inputEl().dom.value = v.replace('+'+d,'');
43647             this.hiddenEl().dom.value = this.getValue();
43648             
43649             this.validate();
43650         },
43651         
43652         getDialCode : function(v)
43653         {
43654             v = v ||  '';
43655             
43656             if (v.length == 0) {
43657                 return this.dialCodeHolder.dom.value;
43658             }
43659             
43660             var dialCode = "";
43661             if (v.charAt(0) != "+") {
43662                 return false;
43663             }
43664             var numericChars = "";
43665             for (var i = 1; i < v.length; i++) {
43666               var c = v.charAt(i);
43667               if (!isNaN(c)) {
43668                 numericChars += c;
43669                 if (this.dialCodeMapping[numericChars]) {
43670                   dialCode = v.substr(1, i);
43671                 }
43672                 if (numericChars.length == 4) {
43673                   break;
43674                 }
43675               }
43676             }
43677             return dialCode;
43678         },
43679         
43680         reset : function()
43681         {
43682             this.setValue(this.defaultDialCode);
43683             this.validate();
43684         },
43685         
43686         hiddenEl : function()
43687         {
43688             return this.el.select('input.hidden-tel-input',true).first();
43689         },
43690         
43691         // after setting val
43692         onKeyUp : function(e){
43693             this.setValue(this.getValue());
43694         },
43695         
43696         onKeyPress : function(e){
43697             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43698                 e.stopEvent();
43699             }
43700         }
43701         
43702 });
43703 /**
43704  * @class Roo.bootstrap.MoneyField
43705  * @extends Roo.bootstrap.ComboBox
43706  * Bootstrap MoneyField class
43707  * 
43708  * @constructor
43709  * Create a new MoneyField.
43710  * @param {Object} config Configuration options
43711  */
43712
43713 Roo.bootstrap.MoneyField = function(config) {
43714     
43715     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43716     
43717 };
43718
43719 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43720     
43721     /**
43722      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43723      */
43724     allowDecimals : true,
43725     /**
43726      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43727      */
43728     decimalSeparator : ".",
43729     /**
43730      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43731      */
43732     decimalPrecision : 0,
43733     /**
43734      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43735      */
43736     allowNegative : true,
43737     /**
43738      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43739      */
43740     allowZero: true,
43741     /**
43742      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43743      */
43744     minValue : Number.NEGATIVE_INFINITY,
43745     /**
43746      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43747      */
43748     maxValue : Number.MAX_VALUE,
43749     /**
43750      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43751      */
43752     minText : "The minimum value for this field is {0}",
43753     /**
43754      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43755      */
43756     maxText : "The maximum value for this field is {0}",
43757     /**
43758      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43759      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43760      */
43761     nanText : "{0} is not a valid number",
43762     /**
43763      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43764      */
43765     castInt : true,
43766     /**
43767      * @cfg {String} defaults currency of the MoneyField
43768      * value should be in lkey
43769      */
43770     defaultCurrency : false,
43771     /**
43772      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43773      */
43774     thousandsDelimiter : false,
43775     /**
43776      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43777      */
43778     max_length: false,
43779     
43780     inputlg : 9,
43781     inputmd : 9,
43782     inputsm : 9,
43783     inputxs : 6,
43784     
43785     store : false,
43786     
43787     getAutoCreate : function()
43788     {
43789         var align = this.labelAlign || this.parentLabelAlign();
43790         
43791         var id = Roo.id();
43792
43793         var cfg = {
43794             cls: 'form-group',
43795             cn: []
43796         };
43797
43798         var input =  {
43799             tag: 'input',
43800             id : id,
43801             cls : 'form-control roo-money-amount-input',
43802             autocomplete: 'new-password'
43803         };
43804         
43805         var hiddenInput = {
43806             tag: 'input',
43807             type: 'hidden',
43808             id: Roo.id(),
43809             cls: 'hidden-number-input'
43810         };
43811         
43812         if(this.max_length) {
43813             input.maxlength = this.max_length; 
43814         }
43815         
43816         if (this.name) {
43817             hiddenInput.name = this.name;
43818         }
43819
43820         if (this.disabled) {
43821             input.disabled = true;
43822         }
43823
43824         var clg = 12 - this.inputlg;
43825         var cmd = 12 - this.inputmd;
43826         var csm = 12 - this.inputsm;
43827         var cxs = 12 - this.inputxs;
43828         
43829         var container = {
43830             tag : 'div',
43831             cls : 'row roo-money-field',
43832             cn : [
43833                 {
43834                     tag : 'div',
43835                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43836                     cn : [
43837                         {
43838                             tag : 'div',
43839                             cls: 'roo-select2-container input-group',
43840                             cn: [
43841                                 {
43842                                     tag : 'input',
43843                                     cls : 'form-control roo-money-currency-input',
43844                                     autocomplete: 'new-password',
43845                                     readOnly : 1,
43846                                     name : this.currencyName
43847                                 },
43848                                 {
43849                                     tag :'span',
43850                                     cls : 'input-group-addon',
43851                                     cn : [
43852                                         {
43853                                             tag: 'span',
43854                                             cls: 'caret'
43855                                         }
43856                                     ]
43857                                 }
43858                             ]
43859                         }
43860                     ]
43861                 },
43862                 {
43863                     tag : 'div',
43864                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43865                     cn : [
43866                         {
43867                             tag: 'div',
43868                             cls: this.hasFeedback ? 'has-feedback' : '',
43869                             cn: [
43870                                 input
43871                             ]
43872                         }
43873                     ]
43874                 }
43875             ]
43876             
43877         };
43878         
43879         if (this.fieldLabel.length) {
43880             var indicator = {
43881                 tag: 'i',
43882                 tooltip: 'This field is required'
43883             };
43884
43885             var label = {
43886                 tag: 'label',
43887                 'for':  id,
43888                 cls: 'control-label',
43889                 cn: []
43890             };
43891
43892             var label_text = {
43893                 tag: 'span',
43894                 html: this.fieldLabel
43895             };
43896
43897             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43898             label.cn = [
43899                 indicator,
43900                 label_text
43901             ];
43902
43903             if(this.indicatorpos == 'right') {
43904                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43905                 label.cn = [
43906                     label_text,
43907                     indicator
43908                 ];
43909             }
43910
43911             if(align == 'left') {
43912                 container = {
43913                     tag: 'div',
43914                     cn: [
43915                         container
43916                     ]
43917                 };
43918
43919                 if(this.labelWidth > 12){
43920                     label.style = "width: " + this.labelWidth + 'px';
43921                 }
43922                 if(this.labelWidth < 13 && this.labelmd == 0){
43923                     this.labelmd = this.labelWidth;
43924                 }
43925                 if(this.labellg > 0){
43926                     label.cls += ' col-lg-' + this.labellg;
43927                     input.cls += ' col-lg-' + (12 - this.labellg);
43928                 }
43929                 if(this.labelmd > 0){
43930                     label.cls += ' col-md-' + this.labelmd;
43931                     container.cls += ' col-md-' + (12 - this.labelmd);
43932                 }
43933                 if(this.labelsm > 0){
43934                     label.cls += ' col-sm-' + this.labelsm;
43935                     container.cls += ' col-sm-' + (12 - this.labelsm);
43936                 }
43937                 if(this.labelxs > 0){
43938                     label.cls += ' col-xs-' + this.labelxs;
43939                     container.cls += ' col-xs-' + (12 - this.labelxs);
43940                 }
43941             }
43942         }
43943
43944         cfg.cn = [
43945             label,
43946             container,
43947             hiddenInput
43948         ];
43949         
43950         var settings = this;
43951
43952         ['xs','sm','md','lg'].map(function(size){
43953             if (settings[size]) {
43954                 cfg.cls += ' col-' + size + '-' + settings[size];
43955             }
43956         });
43957         
43958         return cfg;
43959     },
43960     
43961     initEvents : function()
43962     {
43963         this.indicator = this.indicatorEl();
43964         
43965         this.initCurrencyEvent();
43966         
43967         this.initNumberEvent();
43968     },
43969     
43970     initCurrencyEvent : function()
43971     {
43972         if (!this.store) {
43973             throw "can not find store for combo";
43974         }
43975         
43976         this.store = Roo.factory(this.store, Roo.data);
43977         this.store.parent = this;
43978         
43979         this.createList();
43980         
43981         this.triggerEl = this.el.select('.input-group-addon', true).first();
43982         
43983         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43984         
43985         var _this = this;
43986         
43987         (function(){
43988             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43989             _this.list.setWidth(lw);
43990         }).defer(100);
43991         
43992         this.list.on('mouseover', this.onViewOver, this);
43993         this.list.on('mousemove', this.onViewMove, this);
43994         this.list.on('scroll', this.onViewScroll, this);
43995         
43996         if(!this.tpl){
43997             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43998         }
43999         
44000         this.view = new Roo.View(this.list, this.tpl, {
44001             singleSelect:true, store: this.store, selectedClass: this.selectedClass
44002         });
44003         
44004         this.view.on('click', this.onViewClick, this);
44005         
44006         this.store.on('beforeload', this.onBeforeLoad, this);
44007         this.store.on('load', this.onLoad, this);
44008         this.store.on('loadexception', this.onLoadException, this);
44009         
44010         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
44011             "up" : function(e){
44012                 this.inKeyMode = true;
44013                 this.selectPrev();
44014             },
44015
44016             "down" : function(e){
44017                 if(!this.isExpanded()){
44018                     this.onTriggerClick();
44019                 }else{
44020                     this.inKeyMode = true;
44021                     this.selectNext();
44022                 }
44023             },
44024
44025             "enter" : function(e){
44026                 this.collapse();
44027                 
44028                 if(this.fireEvent("specialkey", this, e)){
44029                     this.onViewClick(false);
44030                 }
44031                 
44032                 return true;
44033             },
44034
44035             "esc" : function(e){
44036                 this.collapse();
44037             },
44038
44039             "tab" : function(e){
44040                 this.collapse();
44041                 
44042                 if(this.fireEvent("specialkey", this, e)){
44043                     this.onViewClick(false);
44044                 }
44045                 
44046                 return true;
44047             },
44048
44049             scope : this,
44050
44051             doRelay : function(foo, bar, hname){
44052                 if(hname == 'down' || this.scope.isExpanded()){
44053                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
44054                 }
44055                 return true;
44056             },
44057
44058             forceKeyDown: true
44059         });
44060         
44061         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
44062         
44063     },
44064     
44065     initNumberEvent : function(e)
44066     {
44067         this.inputEl().on("keydown" , this.fireKey,  this);
44068         this.inputEl().on("focus", this.onFocus,  this);
44069         this.inputEl().on("blur", this.onBlur,  this);
44070         
44071         this.inputEl().relayEvent('keyup', this);
44072         
44073         if(this.indicator){
44074             this.indicator.addClass('invisible');
44075         }
44076  
44077         this.originalValue = this.getValue();
44078         
44079         if(this.validationEvent == 'keyup'){
44080             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44081             this.inputEl().on('keyup', this.filterValidation, this);
44082         }
44083         else if(this.validationEvent !== false){
44084             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44085         }
44086         
44087         if(this.selectOnFocus){
44088             this.on("focus", this.preFocus, this);
44089             
44090         }
44091         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44092             this.inputEl().on("keypress", this.filterKeys, this);
44093         } else {
44094             this.inputEl().relayEvent('keypress', this);
44095         }
44096         
44097         var allowed = "0123456789";
44098         
44099         if(this.allowDecimals){
44100             allowed += this.decimalSeparator;
44101         }
44102         
44103         if(this.allowNegative){
44104             allowed += "-";
44105         }
44106         
44107         if(this.thousandsDelimiter) {
44108             allowed += ",";
44109         }
44110         
44111         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44112         
44113         var keyPress = function(e){
44114             
44115             var k = e.getKey();
44116             
44117             var c = e.getCharCode();
44118             
44119             if(
44120                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44121                     allowed.indexOf(String.fromCharCode(c)) === -1
44122             ){
44123                 e.stopEvent();
44124                 return;
44125             }
44126             
44127             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44128                 return;
44129             }
44130             
44131             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44132                 e.stopEvent();
44133             }
44134         };
44135         
44136         this.inputEl().on("keypress", keyPress, this);
44137         
44138     },
44139     
44140     onTriggerClick : function(e)
44141     {   
44142         if(this.disabled){
44143             return;
44144         }
44145         
44146         this.page = 0;
44147         this.loadNext = false;
44148         
44149         if(this.isExpanded()){
44150             this.collapse();
44151             return;
44152         }
44153         
44154         this.hasFocus = true;
44155         
44156         if(this.triggerAction == 'all') {
44157             this.doQuery(this.allQuery, true);
44158             return;
44159         }
44160         
44161         this.doQuery(this.getRawValue());
44162     },
44163     
44164     getCurrency : function()
44165     {   
44166         var v = this.currencyEl().getValue();
44167         
44168         return v;
44169     },
44170     
44171     restrictHeight : function()
44172     {
44173         this.list.alignTo(this.currencyEl(), this.listAlign);
44174         this.list.alignTo(this.currencyEl(), this.listAlign);
44175     },
44176     
44177     onViewClick : function(view, doFocus, el, e)
44178     {
44179         var index = this.view.getSelectedIndexes()[0];
44180         
44181         var r = this.store.getAt(index);
44182         
44183         if(r){
44184             this.onSelect(r, index);
44185         }
44186     },
44187     
44188     onSelect : function(record, index){
44189         
44190         if(this.fireEvent('beforeselect', this, record, index) !== false){
44191         
44192             this.setFromCurrencyData(index > -1 ? record.data : false);
44193             
44194             this.collapse();
44195             
44196             this.fireEvent('select', this, record, index);
44197         }
44198     },
44199     
44200     setFromCurrencyData : function(o)
44201     {
44202         var currency = '';
44203         
44204         this.lastCurrency = o;
44205         
44206         if (this.currencyField) {
44207             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44208         } else {
44209             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44210         }
44211         
44212         this.lastSelectionText = currency;
44213         
44214         //setting default currency
44215         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44216             this.setCurrency(this.defaultCurrency);
44217             return;
44218         }
44219         
44220         this.setCurrency(currency);
44221     },
44222     
44223     setFromData : function(o)
44224     {
44225         var c = {};
44226         
44227         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44228         
44229         this.setFromCurrencyData(c);
44230         
44231         var value = '';
44232         
44233         if (this.name) {
44234             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44235         } else {
44236             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44237         }
44238         
44239         this.setValue(value);
44240         
44241     },
44242     
44243     setCurrency : function(v)
44244     {   
44245         this.currencyValue = v;
44246         
44247         if(this.rendered){
44248             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44249             this.validate();
44250         }
44251     },
44252     
44253     setValue : function(v)
44254     {
44255         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44256         
44257         this.value = v;
44258         
44259         if(this.rendered){
44260             
44261             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44262             
44263             this.inputEl().dom.value = (v == '') ? '' :
44264                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44265             
44266             if(!this.allowZero && v === '0') {
44267                 this.hiddenEl().dom.value = '';
44268                 this.inputEl().dom.value = '';
44269             }
44270             
44271             this.validate();
44272         }
44273     },
44274     
44275     getRawValue : function()
44276     {
44277         var v = this.inputEl().getValue();
44278         
44279         return v;
44280     },
44281     
44282     getValue : function()
44283     {
44284         return this.fixPrecision(this.parseValue(this.getRawValue()));
44285     },
44286     
44287     parseValue : function(value)
44288     {
44289         if(this.thousandsDelimiter) {
44290             value += "";
44291             r = new RegExp(",", "g");
44292             value = value.replace(r, "");
44293         }
44294         
44295         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44296         return isNaN(value) ? '' : value;
44297         
44298     },
44299     
44300     fixPrecision : function(value)
44301     {
44302         if(this.thousandsDelimiter) {
44303             value += "";
44304             r = new RegExp(",", "g");
44305             value = value.replace(r, "");
44306         }
44307         
44308         var nan = isNaN(value);
44309         
44310         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44311             return nan ? '' : value;
44312         }
44313         return parseFloat(value).toFixed(this.decimalPrecision);
44314     },
44315     
44316     decimalPrecisionFcn : function(v)
44317     {
44318         return Math.floor(v);
44319     },
44320     
44321     validateValue : function(value)
44322     {
44323         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44324             return false;
44325         }
44326         
44327         var num = this.parseValue(value);
44328         
44329         if(isNaN(num)){
44330             this.markInvalid(String.format(this.nanText, value));
44331             return false;
44332         }
44333         
44334         if(num < this.minValue){
44335             this.markInvalid(String.format(this.minText, this.minValue));
44336             return false;
44337         }
44338         
44339         if(num > this.maxValue){
44340             this.markInvalid(String.format(this.maxText, this.maxValue));
44341             return false;
44342         }
44343         
44344         return true;
44345     },
44346     
44347     validate : function()
44348     {
44349         if(this.disabled || this.allowBlank){
44350             this.markValid();
44351             return true;
44352         }
44353         
44354         var currency = this.getCurrency();
44355         
44356         if(this.validateValue(this.getRawValue()) && currency.length){
44357             this.markValid();
44358             return true;
44359         }
44360         
44361         this.markInvalid();
44362         return false;
44363     },
44364     
44365     getName: function()
44366     {
44367         return this.name;
44368     },
44369     
44370     beforeBlur : function()
44371     {
44372         if(!this.castInt){
44373             return;
44374         }
44375         
44376         var v = this.parseValue(this.getRawValue());
44377         
44378         if(v || v == 0){
44379             this.setValue(v);
44380         }
44381     },
44382     
44383     onBlur : function()
44384     {
44385         this.beforeBlur();
44386         
44387         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44388             //this.el.removeClass(this.focusClass);
44389         }
44390         
44391         this.hasFocus = false;
44392         
44393         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44394             this.validate();
44395         }
44396         
44397         var v = this.getValue();
44398         
44399         if(String(v) !== String(this.startValue)){
44400             this.fireEvent('change', this, v, this.startValue);
44401         }
44402         
44403         this.fireEvent("blur", this);
44404     },
44405     
44406     inputEl : function()
44407     {
44408         return this.el.select('.roo-money-amount-input', true).first();
44409     },
44410     
44411     currencyEl : function()
44412     {
44413         return this.el.select('.roo-money-currency-input', true).first();
44414     },
44415     
44416     hiddenEl : function()
44417     {
44418         return this.el.select('input.hidden-number-input',true).first();
44419     }
44420     
44421 });/**
44422  * @class Roo.bootstrap.BezierSignature
44423  * @extends Roo.bootstrap.Component
44424  * Bootstrap BezierSignature class
44425  * This script refer to:
44426  *    Title: Signature Pad
44427  *    Author: szimek
44428  *    Availability: https://github.com/szimek/signature_pad
44429  *
44430  * @constructor
44431  * Create a new BezierSignature
44432  * @param {Object} config The config object
44433  */
44434
44435 Roo.bootstrap.BezierSignature = function(config){
44436     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44437     this.addEvents({
44438         "resize" : true
44439     });
44440 };
44441
44442 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44443 {
44444      
44445     curve_data: [],
44446     
44447     is_empty: true,
44448     
44449     mouse_btn_down: true,
44450     
44451     /**
44452      * @cfg {int} canvas height
44453      */
44454     canvas_height: '200px',
44455     
44456     /**
44457      * @cfg {float|function} Radius of a single dot.
44458      */ 
44459     dot_size: false,
44460     
44461     /**
44462      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44463      */
44464     min_width: 0.5,
44465     
44466     /**
44467      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44468      */
44469     max_width: 2.5,
44470     
44471     /**
44472      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44473      */
44474     throttle: 16,
44475     
44476     /**
44477      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44478      */
44479     min_distance: 5,
44480     
44481     /**
44482      * @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.
44483      */
44484     bg_color: 'rgba(0, 0, 0, 0)',
44485     
44486     /**
44487      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44488      */
44489     dot_color: 'black',
44490     
44491     /**
44492      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44493      */ 
44494     velocity_filter_weight: 0.7,
44495     
44496     /**
44497      * @cfg {function} Callback when stroke begin. 
44498      */
44499     onBegin: false,
44500     
44501     /**
44502      * @cfg {function} Callback when stroke end.
44503      */
44504     onEnd: false,
44505     
44506     getAutoCreate : function()
44507     {
44508         var cls = 'roo-signature column';
44509         
44510         if(this.cls){
44511             cls += ' ' + this.cls;
44512         }
44513         
44514         var col_sizes = [
44515             'lg',
44516             'md',
44517             'sm',
44518             'xs'
44519         ];
44520         
44521         for(var i = 0; i < col_sizes.length; i++) {
44522             if(this[col_sizes[i]]) {
44523                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44524             }
44525         }
44526         
44527         var cfg = {
44528             tag: 'div',
44529             cls: cls,
44530             cn: [
44531                 {
44532                     tag: 'div',
44533                     cls: 'roo-signature-body',
44534                     cn: [
44535                         {
44536                             tag: 'canvas',
44537                             cls: 'roo-signature-body-canvas',
44538                             height: this.canvas_height,
44539                             width: this.canvas_width
44540                         }
44541                     ]
44542                 },
44543                 {
44544                     tag: 'input',
44545                     type: 'file',
44546                     style: 'display: none'
44547                 }
44548             ]
44549         };
44550         
44551         return cfg;
44552     },
44553     
44554     initEvents: function() 
44555     {
44556         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44557         
44558         var canvas = this.canvasEl();
44559         
44560         // mouse && touch event swapping...
44561         canvas.dom.style.touchAction = 'none';
44562         canvas.dom.style.msTouchAction = 'none';
44563         
44564         this.mouse_btn_down = false;
44565         canvas.on('mousedown', this._handleMouseDown, this);
44566         canvas.on('mousemove', this._handleMouseMove, this);
44567         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44568         
44569         if (window.PointerEvent) {
44570             canvas.on('pointerdown', this._handleMouseDown, this);
44571             canvas.on('pointermove', this._handleMouseMove, this);
44572             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44573         }
44574         
44575         if ('ontouchstart' in window) {
44576             canvas.on('touchstart', this._handleTouchStart, this);
44577             canvas.on('touchmove', this._handleTouchMove, this);
44578             canvas.on('touchend', this._handleTouchEnd, this);
44579         }
44580         
44581         Roo.EventManager.onWindowResize(this.resize, this, true);
44582         
44583         // file input event
44584         this.fileEl().on('change', this.uploadImage, this);
44585         
44586         this.clear();
44587         
44588         this.resize();
44589     },
44590     
44591     resize: function(){
44592         
44593         var canvas = this.canvasEl().dom;
44594         var ctx = this.canvasElCtx();
44595         var img_data = false;
44596         
44597         if(canvas.width > 0) {
44598             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44599         }
44600         // setting canvas width will clean img data
44601         canvas.width = 0;
44602         
44603         var style = window.getComputedStyle ? 
44604             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44605             
44606         var padding_left = parseInt(style.paddingLeft) || 0;
44607         var padding_right = parseInt(style.paddingRight) || 0;
44608         
44609         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44610         
44611         if(img_data) {
44612             ctx.putImageData(img_data, 0, 0);
44613         }
44614     },
44615     
44616     _handleMouseDown: function(e)
44617     {
44618         if (e.browserEvent.which === 1) {
44619             this.mouse_btn_down = true;
44620             this.strokeBegin(e);
44621         }
44622     },
44623     
44624     _handleMouseMove: function (e)
44625     {
44626         if (this.mouse_btn_down) {
44627             this.strokeMoveUpdate(e);
44628         }
44629     },
44630     
44631     _handleMouseUp: function (e)
44632     {
44633         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44634             this.mouse_btn_down = false;
44635             this.strokeEnd(e);
44636         }
44637     },
44638     
44639     _handleTouchStart: function (e) {
44640         
44641         e.preventDefault();
44642         if (e.browserEvent.targetTouches.length === 1) {
44643             // var touch = e.browserEvent.changedTouches[0];
44644             // this.strokeBegin(touch);
44645             
44646              this.strokeBegin(e); // assume e catching the correct xy...
44647         }
44648     },
44649     
44650     _handleTouchMove: function (e) {
44651         e.preventDefault();
44652         // var touch = event.targetTouches[0];
44653         // _this._strokeMoveUpdate(touch);
44654         this.strokeMoveUpdate(e);
44655     },
44656     
44657     _handleTouchEnd: function (e) {
44658         var wasCanvasTouched = e.target === this.canvasEl().dom;
44659         if (wasCanvasTouched) {
44660             e.preventDefault();
44661             // var touch = event.changedTouches[0];
44662             // _this._strokeEnd(touch);
44663             this.strokeEnd(e);
44664         }
44665     },
44666     
44667     reset: function () {
44668         this._lastPoints = [];
44669         this._lastVelocity = 0;
44670         this._lastWidth = (this.min_width + this.max_width) / 2;
44671         this.canvasElCtx().fillStyle = this.dot_color;
44672     },
44673     
44674     strokeMoveUpdate: function(e)
44675     {
44676         this.strokeUpdate(e);
44677         
44678         if (this.throttle) {
44679             this.throttleStroke(this.strokeUpdate, this.throttle);
44680         }
44681         else {
44682             this.strokeUpdate(e);
44683         }
44684     },
44685     
44686     strokeBegin: function(e)
44687     {
44688         var newPointGroup = {
44689             color: this.dot_color,
44690             points: []
44691         };
44692         
44693         if (typeof this.onBegin === 'function') {
44694             this.onBegin(e);
44695         }
44696         
44697         this.curve_data.push(newPointGroup);
44698         this.reset();
44699         this.strokeUpdate(e);
44700     },
44701     
44702     strokeUpdate: function(e)
44703     {
44704         var rect = this.canvasEl().dom.getBoundingClientRect();
44705         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44706         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44707         var lastPoints = lastPointGroup.points;
44708         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44709         var isLastPointTooClose = lastPoint
44710             ? point.distanceTo(lastPoint) <= this.min_distance
44711             : false;
44712         var color = lastPointGroup.color;
44713         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44714             var curve = this.addPoint(point);
44715             if (!lastPoint) {
44716                 this.drawDot({color: color, point: point});
44717             }
44718             else if (curve) {
44719                 this.drawCurve({color: color, curve: curve});
44720             }
44721             lastPoints.push({
44722                 time: point.time,
44723                 x: point.x,
44724                 y: point.y
44725             });
44726         }
44727     },
44728     
44729     strokeEnd: function(e)
44730     {
44731         this.strokeUpdate(e);
44732         if (typeof this.onEnd === 'function') {
44733             this.onEnd(e);
44734         }
44735     },
44736     
44737     addPoint:  function (point) {
44738         var _lastPoints = this._lastPoints;
44739         _lastPoints.push(point);
44740         if (_lastPoints.length > 2) {
44741             if (_lastPoints.length === 3) {
44742                 _lastPoints.unshift(_lastPoints[0]);
44743             }
44744             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44745             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44746             _lastPoints.shift();
44747             return curve;
44748         }
44749         return null;
44750     },
44751     
44752     calculateCurveWidths: function (startPoint, endPoint) {
44753         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44754             (1 - this.velocity_filter_weight) * this._lastVelocity;
44755
44756         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44757         var widths = {
44758             end: newWidth,
44759             start: this._lastWidth
44760         };
44761         
44762         this._lastVelocity = velocity;
44763         this._lastWidth = newWidth;
44764         return widths;
44765     },
44766     
44767     drawDot: function (_a) {
44768         var color = _a.color, point = _a.point;
44769         var ctx = this.canvasElCtx();
44770         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44771         ctx.beginPath();
44772         this.drawCurveSegment(point.x, point.y, width);
44773         ctx.closePath();
44774         ctx.fillStyle = color;
44775         ctx.fill();
44776     },
44777     
44778     drawCurve: function (_a) {
44779         var color = _a.color, curve = _a.curve;
44780         var ctx = this.canvasElCtx();
44781         var widthDelta = curve.endWidth - curve.startWidth;
44782         var drawSteps = Math.floor(curve.length()) * 2;
44783         ctx.beginPath();
44784         ctx.fillStyle = color;
44785         for (var i = 0; i < drawSteps; i += 1) {
44786         var t = i / drawSteps;
44787         var tt = t * t;
44788         var ttt = tt * t;
44789         var u = 1 - t;
44790         var uu = u * u;
44791         var uuu = uu * u;
44792         var x = uuu * curve.startPoint.x;
44793         x += 3 * uu * t * curve.control1.x;
44794         x += 3 * u * tt * curve.control2.x;
44795         x += ttt * curve.endPoint.x;
44796         var y = uuu * curve.startPoint.y;
44797         y += 3 * uu * t * curve.control1.y;
44798         y += 3 * u * tt * curve.control2.y;
44799         y += ttt * curve.endPoint.y;
44800         var width = curve.startWidth + ttt * widthDelta;
44801         this.drawCurveSegment(x, y, width);
44802         }
44803         ctx.closePath();
44804         ctx.fill();
44805     },
44806     
44807     drawCurveSegment: function (x, y, width) {
44808         var ctx = this.canvasElCtx();
44809         ctx.moveTo(x, y);
44810         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44811         this.is_empty = false;
44812     },
44813     
44814     clear: function()
44815     {
44816         var ctx = this.canvasElCtx();
44817         var canvas = this.canvasEl().dom;
44818         ctx.fillStyle = this.bg_color;
44819         ctx.clearRect(0, 0, canvas.width, canvas.height);
44820         ctx.fillRect(0, 0, canvas.width, canvas.height);
44821         this.curve_data = [];
44822         this.reset();
44823         this.is_empty = true;
44824     },
44825     
44826     fileEl: function()
44827     {
44828         return  this.el.select('input',true).first();
44829     },
44830     
44831     canvasEl: function()
44832     {
44833         return this.el.select('canvas',true).first();
44834     },
44835     
44836     canvasElCtx: function()
44837     {
44838         return this.el.select('canvas',true).first().dom.getContext('2d');
44839     },
44840     
44841     getImage: function(type)
44842     {
44843         if(this.is_empty) {
44844             return false;
44845         }
44846         
44847         // encryption ?
44848         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44849     },
44850     
44851     drawFromImage: function(img_src)
44852     {
44853         var img = new Image();
44854         
44855         img.onload = function(){
44856             this.canvasElCtx().drawImage(img, 0, 0);
44857         }.bind(this);
44858         
44859         img.src = img_src;
44860         
44861         this.is_empty = false;
44862     },
44863     
44864     selectImage: function()
44865     {
44866         this.fileEl().dom.click();
44867     },
44868     
44869     uploadImage: function(e)
44870     {
44871         var reader = new FileReader();
44872         
44873         reader.onload = function(e){
44874             var img = new Image();
44875             img.onload = function(){
44876                 this.reset();
44877                 this.canvasElCtx().drawImage(img, 0, 0);
44878             }.bind(this);
44879             img.src = e.target.result;
44880         }.bind(this);
44881         
44882         reader.readAsDataURL(e.target.files[0]);
44883     },
44884     
44885     // Bezier Point Constructor
44886     Point: (function () {
44887         function Point(x, y, time) {
44888             this.x = x;
44889             this.y = y;
44890             this.time = time || Date.now();
44891         }
44892         Point.prototype.distanceTo = function (start) {
44893             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44894         };
44895         Point.prototype.equals = function (other) {
44896             return this.x === other.x && this.y === other.y && this.time === other.time;
44897         };
44898         Point.prototype.velocityFrom = function (start) {
44899             return this.time !== start.time
44900             ? this.distanceTo(start) / (this.time - start.time)
44901             : 0;
44902         };
44903         return Point;
44904     }()),
44905     
44906     
44907     // Bezier Constructor
44908     Bezier: (function () {
44909         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44910             this.startPoint = startPoint;
44911             this.control2 = control2;
44912             this.control1 = control1;
44913             this.endPoint = endPoint;
44914             this.startWidth = startWidth;
44915             this.endWidth = endWidth;
44916         }
44917         Bezier.fromPoints = function (points, widths, scope) {
44918             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44919             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44920             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44921         };
44922         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44923             var dx1 = s1.x - s2.x;
44924             var dy1 = s1.y - s2.y;
44925             var dx2 = s2.x - s3.x;
44926             var dy2 = s2.y - s3.y;
44927             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44928             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44929             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44930             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44931             var dxm = m1.x - m2.x;
44932             var dym = m1.y - m2.y;
44933             var k = l2 / (l1 + l2);
44934             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44935             var tx = s2.x - cm.x;
44936             var ty = s2.y - cm.y;
44937             return {
44938                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44939                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44940             };
44941         };
44942         Bezier.prototype.length = function () {
44943             var steps = 10;
44944             var length = 0;
44945             var px;
44946             var py;
44947             for (var i = 0; i <= steps; i += 1) {
44948                 var t = i / steps;
44949                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44950                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44951                 if (i > 0) {
44952                     var xdiff = cx - px;
44953                     var ydiff = cy - py;
44954                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44955                 }
44956                 px = cx;
44957                 py = cy;
44958             }
44959             return length;
44960         };
44961         Bezier.prototype.point = function (t, start, c1, c2, end) {
44962             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44963             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44964             + (3.0 * c2 * (1.0 - t) * t * t)
44965             + (end * t * t * t);
44966         };
44967         return Bezier;
44968     }()),
44969     
44970     throttleStroke: function(fn, wait) {
44971       if (wait === void 0) { wait = 250; }
44972       var previous = 0;
44973       var timeout = null;
44974       var result;
44975       var storedContext;
44976       var storedArgs;
44977       var later = function () {
44978           previous = Date.now();
44979           timeout = null;
44980           result = fn.apply(storedContext, storedArgs);
44981           if (!timeout) {
44982               storedContext = null;
44983               storedArgs = [];
44984           }
44985       };
44986       return function wrapper() {
44987           var args = [];
44988           for (var _i = 0; _i < arguments.length; _i++) {
44989               args[_i] = arguments[_i];
44990           }
44991           var now = Date.now();
44992           var remaining = wait - (now - previous);
44993           storedContext = this;
44994           storedArgs = args;
44995           if (remaining <= 0 || remaining > wait) {
44996               if (timeout) {
44997                   clearTimeout(timeout);
44998                   timeout = null;
44999               }
45000               previous = now;
45001               result = fn.apply(storedContext, storedArgs);
45002               if (!timeout) {
45003                   storedContext = null;
45004                   storedArgs = [];
45005               }
45006           }
45007           else if (!timeout) {
45008               timeout = window.setTimeout(later, remaining);
45009           }
45010           return result;
45011       };
45012   }
45013   
45014 });
45015
45016  
45017
45018