support svg addclass hasclass and removeclass
[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 = 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  * 
3047  * @constructor
3048  * Create a new Input
3049  * @param {Object} config The config object
3050  */
3051
3052 Roo.bootstrap.Img = function(config){
3053     Roo.bootstrap.Img.superclass.constructor.call(this, config);
3054     
3055     this.addEvents({
3056         // img events
3057         /**
3058          * @event click
3059          * The img click event for the img.
3060          * @param {Roo.EventObject} e
3061          */
3062         "click" : true
3063     });
3064 };
3065
3066 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
3067     
3068     imgResponsive: true,
3069     border: '',
3070     src: 'about:blank',
3071     href: false,
3072     target: false,
3073     xsUrl: '',
3074     smUrl: '',
3075     mdUrl: '',
3076     lgUrl: '',
3077
3078     getAutoCreate : function()
3079     {   
3080         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3081             return this.createSingleImg();
3082         }
3083         
3084         var cfg = {
3085             tag: 'div',
3086             cls: 'roo-image-responsive-group',
3087             cn: []
3088         };
3089         var _this = this;
3090         
3091         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
3092             
3093             if(!_this[size + 'Url']){
3094                 return;
3095             }
3096             
3097             var img = {
3098                 tag: 'img',
3099                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
3100                 html: _this.html || cfg.html,
3101                 src: _this[size + 'Url']
3102             };
3103             
3104             img.cls += ' roo-image-responsive-' + size;
3105             
3106             var s = ['xs', 'sm', 'md', 'lg'];
3107             
3108             s.splice(s.indexOf(size), 1);
3109             
3110             Roo.each(s, function(ss){
3111                 img.cls += ' hidden-' + ss;
3112             });
3113             
3114             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
3115                 cfg.cls += ' img-' + _this.border;
3116             }
3117             
3118             if(_this.alt){
3119                 cfg.alt = _this.alt;
3120             }
3121             
3122             if(_this.href){
3123                 var a = {
3124                     tag: 'a',
3125                     href: _this.href,
3126                     cn: [
3127                         img
3128                     ]
3129                 };
3130
3131                 if(this.target){
3132                     a.target = _this.target;
3133                 }
3134             }
3135             
3136             cfg.cn.push((_this.href) ? a : img);
3137             
3138         });
3139         
3140         return cfg;
3141     },
3142     
3143     createSingleImg : function()
3144     {
3145         var cfg = {
3146             tag: 'img',
3147             cls: (this.imgResponsive) ? 'img-responsive' : '',
3148             html : null,
3149             src : 'about:blank'  // just incase src get's set to undefined?!?
3150         };
3151         
3152         cfg.html = this.html || cfg.html;
3153         
3154         cfg.src = this.src || cfg.src;
3155         
3156         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
3157             cfg.cls += ' img-' + this.border;
3158         }
3159         
3160         if(this.alt){
3161             cfg.alt = this.alt;
3162         }
3163         
3164         if(this.href){
3165             var a = {
3166                 tag: 'a',
3167                 href: this.href,
3168                 cn: [
3169                     cfg
3170                 ]
3171             };
3172             
3173             if(this.target){
3174                 a.target = this.target;
3175             }
3176             
3177         }
3178         
3179         return (this.href) ? a : cfg;
3180     },
3181     
3182     initEvents: function() 
3183     {
3184         if(!this.href){
3185             this.el.on('click', this.onClick, this);
3186         }
3187         
3188     },
3189     
3190     onClick : function(e)
3191     {
3192         Roo.log('img onclick');
3193         this.fireEvent('click', this, e);
3194     },
3195     /**
3196      * Sets the url of the image - used to update it
3197      * @param {String} url the url of the image
3198      */
3199     
3200     setSrc : function(url)
3201     {
3202         this.src =  url;
3203         
3204         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
3205             this.el.dom.src =  url;
3206             return;
3207         }
3208         
3209         this.el.select('img', true).first().dom.src =  url;
3210     }
3211     
3212     
3213    
3214 });
3215
3216  /*
3217  * - LGPL
3218  *
3219  * image
3220  * 
3221  */
3222
3223
3224 /**
3225  * @class Roo.bootstrap.Link
3226  * @extends Roo.bootstrap.Component
3227  * Bootstrap Link Class
3228  * @cfg {String} alt image alternative text
3229  * @cfg {String} href a tag href
3230  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3231  * @cfg {String} html the content of the link.
3232  * @cfg {String} anchor name for the anchor link
3233  * @cfg {String} fa - favicon
3234
3235  * @cfg {Boolean} preventDefault (true | false) default false
3236
3237  * 
3238  * @constructor
3239  * Create a new Input
3240  * @param {Object} config The config object
3241  */
3242
3243 Roo.bootstrap.Link = function(config){
3244     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3245     
3246     this.addEvents({
3247         // img events
3248         /**
3249          * @event click
3250          * The img click event for the img.
3251          * @param {Roo.EventObject} e
3252          */
3253         "click" : true
3254     });
3255 };
3256
3257 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3258     
3259     href: false,
3260     target: false,
3261     preventDefault: false,
3262     anchor : false,
3263     alt : false,
3264     fa: false,
3265
3266
3267     getAutoCreate : function()
3268     {
3269         var html = this.html || '';
3270         
3271         if (this.fa !== false) {
3272             html = '<i class="fa fa-' + this.fa + '"></i>';
3273         }
3274         var cfg = {
3275             tag: 'a'
3276         };
3277         // anchor's do not require html/href...
3278         if (this.anchor === false) {
3279             cfg.html = html;
3280             cfg.href = this.href || '#';
3281         } else {
3282             cfg.name = this.anchor;
3283             if (this.html !== false || this.fa !== false) {
3284                 cfg.html = html;
3285             }
3286             if (this.href !== false) {
3287                 cfg.href = this.href;
3288             }
3289         }
3290         
3291         if(this.alt !== false){
3292             cfg.alt = this.alt;
3293         }
3294         
3295         
3296         if(this.target !== false) {
3297             cfg.target = this.target;
3298         }
3299         
3300         return cfg;
3301     },
3302     
3303     initEvents: function() {
3304         
3305         if(!this.href || this.preventDefault){
3306             this.el.on('click', this.onClick, this);
3307         }
3308     },
3309     
3310     onClick : function(e)
3311     {
3312         if(this.preventDefault){
3313             e.preventDefault();
3314         }
3315         //Roo.log('img onclick');
3316         this.fireEvent('click', this, e);
3317     }
3318    
3319 });
3320
3321  /*
3322  * - LGPL
3323  *
3324  * header
3325  * 
3326  */
3327
3328 /**
3329  * @class Roo.bootstrap.Header
3330  * @extends Roo.bootstrap.Component
3331  * Bootstrap Header class
3332  * @cfg {String} html content of header
3333  * @cfg {Number} level (1|2|3|4|5|6) default 1
3334  * 
3335  * @constructor
3336  * Create a new Header
3337  * @param {Object} config The config object
3338  */
3339
3340
3341 Roo.bootstrap.Header  = function(config){
3342     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3343 };
3344
3345 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3346     
3347     //href : false,
3348     html : false,
3349     level : 1,
3350     
3351     
3352     
3353     getAutoCreate : function(){
3354         
3355         
3356         
3357         var cfg = {
3358             tag: 'h' + (1 *this.level),
3359             html: this.html || ''
3360         } ;
3361         
3362         return cfg;
3363     }
3364    
3365 });
3366
3367  
3368
3369  /*
3370  * Based on:
3371  * Ext JS Library 1.1.1
3372  * Copyright(c) 2006-2007, Ext JS, LLC.
3373  *
3374  * Originally Released Under LGPL - original licence link has changed is not relivant.
3375  *
3376  * Fork - LGPL
3377  * <script type="text/javascript">
3378  */
3379  
3380 /**
3381  * @class Roo.bootstrap.MenuMgr
3382  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3383  * @singleton
3384  */
3385 Roo.bootstrap.MenuMgr = function(){
3386    var menus, active, groups = {}, attached = false, lastShow = new Date();
3387
3388    // private - called when first menu is created
3389    function init(){
3390        menus = {};
3391        active = new Roo.util.MixedCollection();
3392        Roo.get(document).addKeyListener(27, function(){
3393            if(active.length > 0){
3394                hideAll();
3395            }
3396        });
3397    }
3398
3399    // private
3400    function hideAll(){
3401        if(active && active.length > 0){
3402            var c = active.clone();
3403            c.each(function(m){
3404                m.hide();
3405            });
3406        }
3407    }
3408
3409    // private
3410    function onHide(m){
3411        active.remove(m);
3412        if(active.length < 1){
3413            Roo.get(document).un("mouseup", onMouseDown);
3414             
3415            attached = false;
3416        }
3417    }
3418
3419    // private
3420    function onShow(m){
3421        var last = active.last();
3422        lastShow = new Date();
3423        active.add(m);
3424        if(!attached){
3425           Roo.get(document).on("mouseup", onMouseDown);
3426            
3427            attached = true;
3428        }
3429        if(m.parentMenu){
3430           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3431           m.parentMenu.activeChild = m;
3432        }else if(last && last.isVisible()){
3433           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3434        }
3435    }
3436
3437    // private
3438    function onBeforeHide(m){
3439        if(m.activeChild){
3440            m.activeChild.hide();
3441        }
3442        if(m.autoHideTimer){
3443            clearTimeout(m.autoHideTimer);
3444            delete m.autoHideTimer;
3445        }
3446    }
3447
3448    // private
3449    function onBeforeShow(m){
3450        var pm = m.parentMenu;
3451        if(!pm && !m.allowOtherMenus){
3452            hideAll();
3453        }else if(pm && pm.activeChild && active != m){
3454            pm.activeChild.hide();
3455        }
3456    }
3457
3458    // private this should really trigger on mouseup..
3459    function onMouseDown(e){
3460         Roo.log("on Mouse Up");
3461         
3462         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3463             Roo.log("MenuManager hideAll");
3464             hideAll();
3465             e.stopEvent();
3466         }
3467         
3468         
3469    }
3470
3471    // private
3472    function onBeforeCheck(mi, state){
3473        if(state){
3474            var g = groups[mi.group];
3475            for(var i = 0, l = g.length; i < l; i++){
3476                if(g[i] != mi){
3477                    g[i].setChecked(false);
3478                }
3479            }
3480        }
3481    }
3482
3483    return {
3484
3485        /**
3486         * Hides all menus that are currently visible
3487         */
3488        hideAll : function(){
3489             hideAll();  
3490        },
3491
3492        // private
3493        register : function(menu){
3494            if(!menus){
3495                init();
3496            }
3497            menus[menu.id] = menu;
3498            menu.on("beforehide", onBeforeHide);
3499            menu.on("hide", onHide);
3500            menu.on("beforeshow", onBeforeShow);
3501            menu.on("show", onShow);
3502            var g = menu.group;
3503            if(g && menu.events["checkchange"]){
3504                if(!groups[g]){
3505                    groups[g] = [];
3506                }
3507                groups[g].push(menu);
3508                menu.on("checkchange", onCheck);
3509            }
3510        },
3511
3512         /**
3513          * Returns a {@link Roo.menu.Menu} object
3514          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3515          * be used to generate and return a new Menu instance.
3516          */
3517        get : function(menu){
3518            if(typeof menu == "string"){ // menu id
3519                return menus[menu];
3520            }else if(menu.events){  // menu instance
3521                return menu;
3522            }
3523            /*else if(typeof menu.length == 'number'){ // array of menu items?
3524                return new Roo.bootstrap.Menu({items:menu});
3525            }else{ // otherwise, must be a config
3526                return new Roo.bootstrap.Menu(menu);
3527            }
3528            */
3529            return false;
3530        },
3531
3532        // private
3533        unregister : function(menu){
3534            delete menus[menu.id];
3535            menu.un("beforehide", onBeforeHide);
3536            menu.un("hide", onHide);
3537            menu.un("beforeshow", onBeforeShow);
3538            menu.un("show", onShow);
3539            var g = menu.group;
3540            if(g && menu.events["checkchange"]){
3541                groups[g].remove(menu);
3542                menu.un("checkchange", onCheck);
3543            }
3544        },
3545
3546        // private
3547        registerCheckable : function(menuItem){
3548            var g = menuItem.group;
3549            if(g){
3550                if(!groups[g]){
3551                    groups[g] = [];
3552                }
3553                groups[g].push(menuItem);
3554                menuItem.on("beforecheckchange", onBeforeCheck);
3555            }
3556        },
3557
3558        // private
3559        unregisterCheckable : function(menuItem){
3560            var g = menuItem.group;
3561            if(g){
3562                groups[g].remove(menuItem);
3563                menuItem.un("beforecheckchange", onBeforeCheck);
3564            }
3565        }
3566    };
3567 }();/*
3568  * - LGPL
3569  *
3570  * menu
3571  * 
3572  */
3573
3574 /**
3575  * @class Roo.bootstrap.Menu
3576  * @extends Roo.bootstrap.Component
3577  * Bootstrap Menu class - container for MenuItems
3578  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3579  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3580  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3581  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3582   * @cfg {bool} hideTrigger (true|false)  default false - hide the carret for trigger.
3583   * @cfg {String} align  default tl-bl? == below  - how the menu should be aligned. 
3584  
3585  * @constructor
3586  * Create a new Menu
3587  * @param {Object} config The config object
3588  */
3589
3590
3591 Roo.bootstrap.Menu = function(config){
3592     
3593     if (config.type == 'treeview') {
3594         // normally menu's are drawn attached to the document to handle layering etc..
3595         // however treeview (used by the docs menu is drawn into the parent element)
3596         this.container_method = 'getChildContainer'; 
3597     }
3598     
3599     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3600     if (this.registerMenu && this.type != 'treeview')  {
3601         Roo.bootstrap.MenuMgr.register(this);
3602     }
3603     
3604     
3605     this.addEvents({
3606         /**
3607          * @event beforeshow
3608          * Fires before this menu is displayed (return false to block)
3609          * @param {Roo.menu.Menu} this
3610          */
3611         beforeshow : true,
3612         /**
3613          * @event beforehide
3614          * Fires before this menu is hidden (return false to block)
3615          * @param {Roo.menu.Menu} this
3616          */
3617         beforehide : true,
3618         /**
3619          * @event show
3620          * Fires after this menu is displayed
3621          * @param {Roo.menu.Menu} this
3622          */
3623         show : true,
3624         /**
3625          * @event hide
3626          * Fires after this menu is hidden
3627          * @param {Roo.menu.Menu} this
3628          */
3629         hide : true,
3630         /**
3631          * @event click
3632          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3633          * @param {Roo.menu.Menu} this
3634          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3635          * @param {Roo.EventObject} e
3636          */
3637         click : true,
3638         /**
3639          * @event mouseover
3640          * Fires when the mouse is hovering over this menu
3641          * @param {Roo.menu.Menu} this
3642          * @param {Roo.EventObject} e
3643          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3644          */
3645         mouseover : true,
3646         /**
3647          * @event mouseout
3648          * Fires when the mouse exits this menu
3649          * @param {Roo.menu.Menu} this
3650          * @param {Roo.EventObject} e
3651          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3652          */
3653         mouseout : true,
3654         /**
3655          * @event itemclick
3656          * Fires when a menu item contained in this menu is clicked
3657          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3658          * @param {Roo.EventObject} e
3659          */
3660         itemclick: true
3661     });
3662     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3663 };
3664
3665 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3666     
3667    /// html : false,
3668    
3669     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3670     type: false,
3671     /**
3672      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3673      */
3674     registerMenu : true,
3675     
3676     menuItems :false, // stores the menu items..
3677     
3678     hidden:true,
3679         
3680     parentMenu : false,
3681     
3682     stopEvent : true,
3683     
3684     isLink : false,
3685     
3686     container_method : 'getDocumentBody', // so the menu is rendered on the body and zIndex works.
3687     
3688     hideTrigger : false,
3689     
3690     align : 'tl-bl?',
3691     
3692     
3693     getChildContainer : function() {
3694         return this.el;  
3695     },
3696     
3697     getAutoCreate : function(){
3698          
3699         //if (['right'].indexOf(this.align)!==-1) {
3700         //    cfg.cn[1].cls += ' pull-right'
3701         //}
3702          
3703         var cfg = {
3704             tag : 'ul',
3705             cls : 'dropdown-menu shadow' ,
3706             style : 'z-index:1000'
3707             
3708         };
3709         
3710         if (this.type === 'submenu') {
3711             cfg.cls = 'submenu active';
3712         }
3713         if (this.type === 'treeview') {
3714             cfg.cls = 'treeview-menu';
3715         }
3716         
3717         return cfg;
3718     },
3719     initEvents : function() {
3720         
3721        // Roo.log("ADD event");
3722        // Roo.log(this.triggerEl.dom);
3723         if (this.triggerEl) {
3724             
3725             this.triggerEl.on('click', this.onTriggerClick, this);
3726             
3727             this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3728             
3729             if (!this.hideTrigger) {
3730                 if (this.triggerEl.hasClass('nav-item') && this.triggerEl.select('.nav-link',true).length) {
3731                     // dropdown toggle on the 'a' in BS4?
3732                     this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3733                 } else {
3734                     this.triggerEl.addClass('dropdown-toggle');
3735                 }
3736             }
3737         }
3738         
3739         if (Roo.isTouch) {
3740             this.el.on('touchstart'  , this.onTouch, this);
3741         }
3742         this.el.on('click' , this.onClick, this);
3743
3744         this.el.on("mouseover", this.onMouseOver, this);
3745         this.el.on("mouseout", this.onMouseOut, this);
3746         
3747     },
3748     
3749     findTargetItem : function(e)
3750     {
3751         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3752         if(!t){
3753             return false;
3754         }
3755         //Roo.log(t);         Roo.log(t.id);
3756         if(t && t.id){
3757             //Roo.log(this.menuitems);
3758             return this.menuitems.get(t.id);
3759             
3760             //return this.items.get(t.menuItemId);
3761         }
3762         
3763         return false;
3764     },
3765     
3766     onTouch : function(e) 
3767     {
3768         Roo.log("menu.onTouch");
3769         //e.stopEvent(); this make the user popdown broken
3770         this.onClick(e);
3771     },
3772     
3773     onClick : function(e)
3774     {
3775         Roo.log("menu.onClick");
3776         
3777         var t = this.findTargetItem(e);
3778         if(!t || t.isContainer){
3779             return;
3780         }
3781         Roo.log(e);
3782         /*
3783         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3784             if(t == this.activeItem && t.shouldDeactivate(e)){
3785                 this.activeItem.deactivate();
3786                 delete this.activeItem;
3787                 return;
3788             }
3789             if(t.canActivate){
3790                 this.setActiveItem(t, true);
3791             }
3792             return;
3793             
3794             
3795         }
3796         */
3797        
3798         Roo.log('pass click event');
3799         
3800         t.onClick(e);
3801         
3802         this.fireEvent("click", this, t, e);
3803         
3804         var _this = this;
3805         
3806         if(!t.href.length || t.href == '#'){
3807             (function() { _this.hide(); }).defer(100);
3808         }
3809         
3810     },
3811     
3812     onMouseOver : function(e){
3813         var t  = this.findTargetItem(e);
3814         //Roo.log(t);
3815         //if(t){
3816         //    if(t.canActivate && !t.disabled){
3817         //        this.setActiveItem(t, true);
3818         //    }
3819         //}
3820         
3821         this.fireEvent("mouseover", this, e, t);
3822     },
3823     isVisible : function(){
3824         return !this.hidden;
3825     },
3826     onMouseOut : function(e){
3827         var t  = this.findTargetItem(e);
3828         
3829         //if(t ){
3830         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3831         //        this.activeItem.deactivate();
3832         //        delete this.activeItem;
3833         //    }
3834         //}
3835         this.fireEvent("mouseout", this, e, t);
3836     },
3837     
3838     
3839     /**
3840      * Displays this menu relative to another element
3841      * @param {String/HTMLElement/Roo.Element} element The element to align to
3842      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3843      * the element (defaults to this.defaultAlign)
3844      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3845      */
3846     show : function(el, pos, parentMenu)
3847     {
3848         if (false === this.fireEvent("beforeshow", this)) {
3849             Roo.log("show canceled");
3850             return;
3851         }
3852         this.parentMenu = parentMenu;
3853         if(!this.el){
3854             this.render();
3855         }
3856         this.el.addClass('show'); // show otherwise we do not know how big we are..
3857          
3858         var xy = this.el.getAlignToXY(el, pos);
3859         
3860         // bl-tl << left align  below
3861         // tl-bl << left align 
3862         
3863         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3864             // if it goes to far to the right.. -> align left.
3865             xy = this.el.getAlignToXY(el, this.align.replace('/l/g', 'r'))
3866         }
3867         if(xy[0] < 0){
3868             // was left align - go right?
3869             xy = this.el.getAlignToXY(el, this.align.replace('/r/g', 'l'))
3870         }
3871         
3872         // goes down the bottom
3873         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight() ||
3874            xy[1]  < 0 ){
3875             var a = this.align.replace('?', '').split('-');
3876             xy = this.el.getAlignToXY(el, a[1]  + '-' + a[0] + '?')
3877             
3878         }
3879         
3880         this.showAt(  xy , parentMenu, false);
3881     },
3882      /**
3883      * Displays this menu at a specific xy position
3884      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3885      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3886      */
3887     showAt : function(xy, parentMenu, /* private: */_e){
3888         this.parentMenu = parentMenu;
3889         if(!this.el){
3890             this.render();
3891         }
3892         if(_e !== false){
3893             this.fireEvent("beforeshow", this);
3894             //xy = this.el.adjustForConstraints(xy);
3895         }
3896         
3897         //this.el.show();
3898         this.hideMenuItems();
3899         this.hidden = false;
3900         if (this.triggerEl) {
3901             this.triggerEl.addClass('open');
3902         }
3903         
3904         this.el.addClass('show');
3905         
3906         
3907         
3908         // reassign x when hitting right
3909         
3910         // reassign y when hitting bottom
3911         
3912         // but the list may align on trigger left or trigger top... should it be a properity?
3913         
3914         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3915             this.el.setXY(xy);
3916         }
3917         
3918         this.focus();
3919         this.fireEvent("show", this);
3920     },
3921     
3922     focus : function(){
3923         return;
3924         if(!this.hidden){
3925             this.doFocus.defer(50, this);
3926         }
3927     },
3928
3929     doFocus : function(){
3930         if(!this.hidden){
3931             this.focusEl.focus();
3932         }
3933     },
3934
3935     /**
3936      * Hides this menu and optionally all parent menus
3937      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3938      */
3939     hide : function(deep)
3940     {
3941         if (false === this.fireEvent("beforehide", this)) {
3942             Roo.log("hide canceled");
3943             return;
3944         }
3945         this.hideMenuItems();
3946         if(this.el && this.isVisible()){
3947            
3948             if(this.activeItem){
3949                 this.activeItem.deactivate();
3950                 this.activeItem = null;
3951             }
3952             if (this.triggerEl) {
3953                 this.triggerEl.removeClass('open');
3954             }
3955             
3956             this.el.removeClass('show');
3957             this.hidden = true;
3958             this.fireEvent("hide", this);
3959         }
3960         if(deep === true && this.parentMenu){
3961             this.parentMenu.hide(true);
3962         }
3963     },
3964     
3965     onTriggerClick : function(e)
3966     {
3967         Roo.log('trigger click');
3968         
3969         var target = e.getTarget();
3970         
3971         Roo.log(target.nodeName.toLowerCase());
3972         
3973         if(target.nodeName.toLowerCase() === 'i'){
3974             e.preventDefault();
3975         }
3976         
3977     },
3978     
3979     onTriggerPress  : function(e)
3980     {
3981         Roo.log('trigger press');
3982         //Roo.log(e.getTarget());
3983        // Roo.log(this.triggerEl.dom);
3984        
3985         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3986         var pel = Roo.get(e.getTarget());
3987         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3988             Roo.log('is treeview or dropdown?');
3989             return;
3990         }
3991         
3992         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3993             return;
3994         }
3995         
3996         if (this.isVisible()) {
3997             Roo.log('hide');
3998             this.hide();
3999         } else {
4000             Roo.log('show');
4001             
4002             this.show(this.triggerEl, this.align, false);
4003         }
4004         
4005         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
4006             e.stopEvent();
4007         }
4008         
4009     },
4010        
4011     
4012     hideMenuItems : function()
4013     {
4014         Roo.log("hide Menu Items");
4015         if (!this.el) { 
4016             return;
4017         }
4018         
4019         this.el.select('.open',true).each(function(aa) {
4020             
4021             aa.removeClass('open');
4022          
4023         });
4024     },
4025     addxtypeChild : function (tree, cntr) {
4026         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
4027           
4028         this.menuitems.add(comp);
4029         return comp;
4030
4031     },
4032     getEl : function()
4033     {
4034         Roo.log(this.el);
4035         return this.el;
4036     },
4037     
4038     clear : function()
4039     {
4040         this.getEl().dom.innerHTML = '';
4041         this.menuitems.clear();
4042     }
4043 });
4044
4045  
4046  /*
4047  * - LGPL
4048  *
4049  * menu item
4050  * 
4051  */
4052
4053
4054 /**
4055  * @class Roo.bootstrap.MenuItem
4056  * @extends Roo.bootstrap.Component
4057  * Bootstrap MenuItem class
4058  * @cfg {String} html the menu label
4059  * @cfg {String} href the link
4060  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
4061  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
4062  * @cfg {Boolean} active  used on sidebars to highlight active itesm
4063  * @cfg {String} fa favicon to show on left of menu item.
4064  * @cfg {Roo.bootsrap.Menu} menu the child menu.
4065  * 
4066  * 
4067  * @constructor
4068  * Create a new MenuItem
4069  * @param {Object} config The config object
4070  */
4071
4072
4073 Roo.bootstrap.MenuItem = function(config){
4074     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
4075     this.addEvents({
4076         // raw events
4077         /**
4078          * @event click
4079          * The raw click event for the entire grid.
4080          * @param {Roo.bootstrap.MenuItem} this
4081          * @param {Roo.EventObject} e
4082          */
4083         "click" : true
4084     });
4085 };
4086
4087 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
4088     
4089     href : false,
4090     html : false,
4091     preventDefault: false,
4092     isContainer : false,
4093     active : false,
4094     fa: false,
4095     
4096     getAutoCreate : function(){
4097         
4098         if(this.isContainer){
4099             return {
4100                 tag: 'li',
4101                 cls: 'dropdown-menu-item '
4102             };
4103         }
4104         var ctag = {
4105             tag: 'span',
4106             html: 'Link'
4107         };
4108         
4109         var anc = {
4110             tag : 'a',
4111             cls : 'dropdown-item',
4112             href : '#',
4113             cn : [  ]
4114         };
4115         
4116         if (this.fa !== false) {
4117             anc.cn.push({
4118                 tag : 'i',
4119                 cls : 'fa fa-' + this.fa
4120             });
4121         }
4122         
4123         anc.cn.push(ctag);
4124         
4125         
4126         var cfg= {
4127             tag: 'li',
4128             cls: 'dropdown-menu-item',
4129             cn: [ anc ]
4130         };
4131         if (this.parent().type == 'treeview') {
4132             cfg.cls = 'treeview-menu';
4133         }
4134         if (this.active) {
4135             cfg.cls += ' active';
4136         }
4137         
4138         
4139         
4140         anc.href = this.href || cfg.cn[0].href ;
4141         ctag.html = this.html || cfg.cn[0].html ;
4142         return cfg;
4143     },
4144     
4145     initEvents: function()
4146     {
4147         if (this.parent().type == 'treeview') {
4148             this.el.select('a').on('click', this.onClick, this);
4149         }
4150         
4151         if (this.menu) {
4152             this.menu.parentType = this.xtype;
4153             this.menu.triggerEl = this.el;
4154             this.menu = this.addxtype(Roo.apply({}, this.menu));
4155         }
4156         
4157     },
4158     onClick : function(e)
4159     {
4160         Roo.log('item on click ');
4161         
4162         if(this.preventDefault){
4163             e.preventDefault();
4164         }
4165         //this.parent().hideMenuItems();
4166         
4167         this.fireEvent('click', this, e);
4168     },
4169     getEl : function()
4170     {
4171         return this.el;
4172     } 
4173 });
4174
4175  
4176
4177  /*
4178  * - LGPL
4179  *
4180  * menu separator
4181  * 
4182  */
4183
4184
4185 /**
4186  * @class Roo.bootstrap.MenuSeparator
4187  * @extends Roo.bootstrap.Component
4188  * Bootstrap MenuSeparator class
4189  * 
4190  * @constructor
4191  * Create a new MenuItem
4192  * @param {Object} config The config object
4193  */
4194
4195
4196 Roo.bootstrap.MenuSeparator = function(config){
4197     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
4198 };
4199
4200 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
4201     
4202     getAutoCreate : function(){
4203         var cfg = {
4204             cls: 'divider',
4205             tag : 'li'
4206         };
4207         
4208         return cfg;
4209     }
4210    
4211 });
4212
4213  
4214
4215  
4216 /*
4217 * Licence: LGPL
4218 */
4219
4220 /**
4221  * @class Roo.bootstrap.Modal
4222  * @extends Roo.bootstrap.Component
4223  * Bootstrap Modal class
4224  * @cfg {String} title Title of dialog
4225  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
4226  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
4227  * @cfg {Boolean} specificTitle default false
4228  * @cfg {Array} buttons Array of buttons or standard button set..
4229  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
4230  * @cfg {Boolean} animate default true
4231  * @cfg {Boolean} allow_close default true
4232  * @cfg {Boolean} fitwindow default false
4233  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
4234  * @cfg {Number} width fixed width - usefull for chrome extension only really.
4235  * @cfg {Number} height fixed height - usefull for chrome extension only really.
4236  * @cfg {String} size (sm|lg|xl) default empty
4237  * @cfg {Number} max_width set the max width of modal
4238  * @cfg {Boolean} editableTitle can the title be edited
4239
4240  *
4241  *
4242  * @constructor
4243  * Create a new Modal Dialog
4244  * @param {Object} config The config object
4245  */
4246
4247 Roo.bootstrap.Modal = function(config){
4248     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
4249     this.addEvents({
4250         // raw events
4251         /**
4252          * @event btnclick
4253          * The raw btnclick event for the button
4254          * @param {Roo.EventObject} e
4255          */
4256         "btnclick" : true,
4257         /**
4258          * @event resize
4259          * Fire when dialog resize
4260          * @param {Roo.bootstrap.Modal} this
4261          * @param {Roo.EventObject} e
4262          */
4263         "resize" : true,
4264         /**
4265          * @event titlechanged
4266          * Fire when the editable title has been changed
4267          * @param {Roo.bootstrap.Modal} this
4268          * @param {Roo.EventObject} value
4269          */
4270         "titlechanged" : true 
4271         
4272     });
4273     this.buttons = this.buttons || [];
4274
4275     if (this.tmpl) {
4276         this.tmpl = Roo.factory(this.tmpl);
4277     }
4278
4279 };
4280
4281 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4282
4283     title : 'test dialog',
4284
4285     buttons : false,
4286
4287     // set on load...
4288
4289     html: false,
4290
4291     tmp: false,
4292
4293     specificTitle: false,
4294
4295     buttonPosition: 'right',
4296
4297     allow_close : true,
4298
4299     animate : true,
4300
4301     fitwindow: false,
4302     
4303      // private
4304     dialogEl: false,
4305     bodyEl:  false,
4306     footerEl:  false,
4307     titleEl:  false,
4308     closeEl:  false,
4309
4310     size: '',
4311     
4312     max_width: 0,
4313     
4314     max_height: 0,
4315     
4316     fit_content: false,
4317     editableTitle  : false,
4318
4319     onRender : function(ct, position)
4320     {
4321         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4322
4323         if(!this.el){
4324             var cfg = Roo.apply({},  this.getAutoCreate());
4325             cfg.id = Roo.id();
4326             //if(!cfg.name){
4327             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4328             //}
4329             //if (!cfg.name.length) {
4330             //    delete cfg.name;
4331            // }
4332             if (this.cls) {
4333                 cfg.cls += ' ' + this.cls;
4334             }
4335             if (this.style) {
4336                 cfg.style = this.style;
4337             }
4338             this.el = Roo.get(document.body).createChild(cfg, position);
4339         }
4340         //var type = this.el.dom.type;
4341
4342
4343         if(this.tabIndex !== undefined){
4344             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4345         }
4346
4347         this.dialogEl = this.el.select('.modal-dialog',true).first();
4348         this.bodyEl = this.el.select('.modal-body',true).first();
4349         this.closeEl = this.el.select('.modal-header .close', true).first();
4350         this.headerEl = this.el.select('.modal-header',true).first();
4351         this.titleEl = this.el.select('.modal-title',true).first();
4352         this.footerEl = this.el.select('.modal-footer',true).first();
4353
4354         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4355         
4356         //this.el.addClass("x-dlg-modal");
4357
4358         if (this.buttons.length) {
4359             Roo.each(this.buttons, function(bb) {
4360                 var b = Roo.apply({}, bb);
4361                 b.xns = b.xns || Roo.bootstrap;
4362                 b.xtype = b.xtype || 'Button';
4363                 if (typeof(b.listeners) == 'undefined') {
4364                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4365                 }
4366
4367                 var btn = Roo.factory(b);
4368
4369                 btn.render(this.getButtonContainer());
4370
4371             },this);
4372         }
4373         // render the children.
4374         var nitems = [];
4375
4376         if(typeof(this.items) != 'undefined'){
4377             var items = this.items;
4378             delete this.items;
4379
4380             for(var i =0;i < items.length;i++) {
4381                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4382             }
4383         }
4384
4385         this.items = nitems;
4386
4387         // where are these used - they used to be body/close/footer
4388
4389
4390         this.initEvents();
4391         //this.el.addClass([this.fieldClass, this.cls]);
4392
4393     },
4394
4395     getAutoCreate : function()
4396     {
4397         // we will default to modal-body-overflow - might need to remove or make optional later.
4398         var bdy = {
4399                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4400                 html : this.html || ''
4401         };
4402
4403         var title = {
4404             tag: 'h5',
4405             cls : 'modal-title',
4406             html : this.title
4407         };
4408
4409         if(this.specificTitle){ // WTF is this?
4410             title = this.title;
4411         }
4412
4413         var header = [];
4414         if (this.allow_close && Roo.bootstrap.version == 3) {
4415             header.push({
4416                 tag: 'button',
4417                 cls : 'close',
4418                 html : '&times'
4419             });
4420         }
4421
4422         header.push(title);
4423
4424         if (this.editableTitle) {
4425             header.push({
4426                 cls: 'form-control roo-editable-title d-none',
4427                 tag: 'input',
4428                 type: 'text'
4429             });
4430         }
4431         
4432         if (this.allow_close && Roo.bootstrap.version == 4) {
4433             header.push({
4434                 tag: 'button',
4435                 cls : 'close',
4436                 html : '&times'
4437             });
4438         }
4439         
4440         var size = '';
4441
4442         if(this.size.length){
4443             size = 'modal-' + this.size;
4444         }
4445         
4446         var footer = Roo.bootstrap.version == 3 ?
4447             {
4448                 cls : 'modal-footer',
4449                 cn : [
4450                     {
4451                         tag: 'div',
4452                         cls: 'btn-' + this.buttonPosition
4453                     }
4454                 ]
4455
4456             } :
4457             {  // BS4 uses mr-auto on left buttons....
4458                 cls : 'modal-footer'
4459             };
4460
4461             
4462
4463         
4464         
4465         var modal = {
4466             cls: "modal",
4467              cn : [
4468                 {
4469                     cls: "modal-dialog " + size,
4470                     cn : [
4471                         {
4472                             cls : "modal-content",
4473                             cn : [
4474                                 {
4475                                     cls : 'modal-header',
4476                                     cn : header
4477                                 },
4478                                 bdy,
4479                                 footer
4480                             ]
4481
4482                         }
4483                     ]
4484
4485                 }
4486             ]
4487         };
4488
4489         if(this.animate){
4490             modal.cls += ' fade';
4491         }
4492
4493         return modal;
4494
4495     },
4496     getChildContainer : function() {
4497
4498          return this.bodyEl;
4499
4500     },
4501     getButtonContainer : function() {
4502         
4503          return Roo.bootstrap.version == 4 ?
4504             this.el.select('.modal-footer',true).first()
4505             : this.el.select('.modal-footer div',true).first();
4506
4507     },
4508     initEvents : function()
4509     {
4510         if (this.allow_close) {
4511             this.closeEl.on('click', this.hide, this);
4512         }
4513         Roo.EventManager.onWindowResize(this.resize, this, true);
4514         if (this.editableTitle) {
4515             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4516             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4517             this.headerEditEl.on('keyup', function(e) {
4518                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4519                         this.toggleHeaderInput(false)
4520                     }
4521                 }, this);
4522             this.headerEditEl.on('blur', function(e) {
4523                 this.toggleHeaderInput(false)
4524             },this);
4525         }
4526
4527     },
4528   
4529
4530     resize : function()
4531     {
4532         this.maskEl.setSize(
4533             Roo.lib.Dom.getViewWidth(true),
4534             Roo.lib.Dom.getViewHeight(true)
4535         );
4536         
4537         if (this.fitwindow) {
4538             
4539            this.dialogEl.setStyle( { 'max-width' : '100%' });
4540             this.setSize(
4541                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4542                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4543             );
4544             return;
4545         }
4546         
4547         if(this.max_width !== 0) {
4548             
4549             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4550             
4551             if(this.height) {
4552                 this.setSize(w, this.height);
4553                 return;
4554             }
4555             
4556             if(this.max_height) {
4557                 this.setSize(w,Math.min(
4558                     this.max_height,
4559                     Roo.lib.Dom.getViewportHeight(true) - 60
4560                 ));
4561                 
4562                 return;
4563             }
4564             
4565             if(!this.fit_content) {
4566                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4567                 return;
4568             }
4569             
4570             this.setSize(w, Math.min(
4571                 60 +
4572                 this.headerEl.getHeight() + 
4573                 this.footerEl.getHeight() + 
4574                 this.getChildHeight(this.bodyEl.dom.childNodes),
4575                 Roo.lib.Dom.getViewportHeight(true) - 60)
4576             );
4577         }
4578         
4579     },
4580
4581     setSize : function(w,h)
4582     {
4583         if (!w && !h) {
4584             return;
4585         }
4586         
4587         this.resizeTo(w,h);
4588     },
4589
4590     show : function() {
4591
4592         if (!this.rendered) {
4593             this.render();
4594         }
4595         this.toggleHeaderInput(false);
4596         //this.el.setStyle('display', 'block');
4597         this.el.removeClass('hideing');
4598         this.el.dom.style.display='block';
4599         
4600         Roo.get(document.body).addClass('modal-open');
4601  
4602         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4603             
4604             (function(){
4605                 this.el.addClass('show');
4606                 this.el.addClass('in');
4607             }).defer(50, this);
4608         }else{
4609             this.el.addClass('show');
4610             this.el.addClass('in');
4611         }
4612
4613         // not sure how we can show data in here..
4614         //if (this.tmpl) {
4615         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4616         //}
4617
4618         Roo.get(document.body).addClass("x-body-masked");
4619         
4620         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4621         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4622         this.maskEl.dom.style.display = 'block';
4623         this.maskEl.addClass('show');
4624         
4625         
4626         this.resize();
4627         
4628         this.fireEvent('show', this);
4629
4630         // set zindex here - otherwise it appears to be ignored...
4631         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4632
4633         (function () {
4634             this.items.forEach( function(e) {
4635                 e.layout ? e.layout() : false;
4636
4637             });
4638         }).defer(100,this);
4639
4640     },
4641     hide : function()
4642     {
4643         if(this.fireEvent("beforehide", this) !== false){
4644             
4645             this.maskEl.removeClass('show');
4646             
4647             this.maskEl.dom.style.display = '';
4648             Roo.get(document.body).removeClass("x-body-masked");
4649             this.el.removeClass('in');
4650             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4651
4652             if(this.animate){ // why
4653                 this.el.addClass('hideing');
4654                 this.el.removeClass('show');
4655                 (function(){
4656                     if (!this.el.hasClass('hideing')) {
4657                         return; // it's been shown again...
4658                     }
4659                     
4660                     this.el.dom.style.display='';
4661
4662                     Roo.get(document.body).removeClass('modal-open');
4663                     this.el.removeClass('hideing');
4664                 }).defer(150,this);
4665                 
4666             }else{
4667                 this.el.removeClass('show');
4668                 this.el.dom.style.display='';
4669                 Roo.get(document.body).removeClass('modal-open');
4670
4671             }
4672             this.fireEvent('hide', this);
4673         }
4674     },
4675     isVisible : function()
4676     {
4677         
4678         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4679         
4680     },
4681
4682     addButton : function(str, cb)
4683     {
4684
4685
4686         var b = Roo.apply({}, { html : str } );
4687         b.xns = b.xns || Roo.bootstrap;
4688         b.xtype = b.xtype || 'Button';
4689         if (typeof(b.listeners) == 'undefined') {
4690             b.listeners = { click : cb.createDelegate(this)  };
4691         }
4692
4693         var btn = Roo.factory(b);
4694
4695         btn.render(this.getButtonContainer());
4696
4697         return btn;
4698
4699     },
4700
4701     setDefaultButton : function(btn)
4702     {
4703         //this.el.select('.modal-footer').()
4704     },
4705
4706     resizeTo: function(w,h)
4707     {
4708         this.dialogEl.setWidth(w);
4709         
4710         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4711
4712         this.bodyEl.setHeight(h - diff);
4713         
4714         this.fireEvent('resize', this);
4715     },
4716     
4717     setContentSize  : function(w, h)
4718     {
4719
4720     },
4721     onButtonClick: function(btn,e)
4722     {
4723         //Roo.log([a,b,c]);
4724         this.fireEvent('btnclick', btn.name, e);
4725     },
4726      /**
4727      * Set the title of the Dialog
4728      * @param {String} str new Title
4729      */
4730     setTitle: function(str) {
4731         this.titleEl.dom.innerHTML = str;
4732         this.title = str;
4733     },
4734     /**
4735      * Set the body of the Dialog
4736      * @param {String} str new Title
4737      */
4738     setBody: function(str) {
4739         this.bodyEl.dom.innerHTML = str;
4740     },
4741     /**
4742      * Set the body of the Dialog using the template
4743      * @param {Obj} data - apply this data to the template and replace the body contents.
4744      */
4745     applyBody: function(obj)
4746     {
4747         if (!this.tmpl) {
4748             Roo.log("Error - using apply Body without a template");
4749             //code
4750         }
4751         this.tmpl.overwrite(this.bodyEl, obj);
4752     },
4753     
4754     getChildHeight : function(child_nodes)
4755     {
4756         if(
4757             !child_nodes ||
4758             child_nodes.length == 0
4759         ) {
4760             return 0;
4761         }
4762         
4763         var child_height = 0;
4764         
4765         for(var i = 0; i < child_nodes.length; i++) {
4766             
4767             /*
4768             * for modal with tabs...
4769             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4770                 
4771                 var layout_childs = child_nodes[i].childNodes;
4772                 
4773                 for(var j = 0; j < layout_childs.length; j++) {
4774                     
4775                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4776                         
4777                         var layout_body_childs = layout_childs[j].childNodes;
4778                         
4779                         for(var k = 0; k < layout_body_childs.length; k++) {
4780                             
4781                             if(layout_body_childs[k].classList.contains('navbar')) {
4782                                 child_height += layout_body_childs[k].offsetHeight;
4783                                 continue;
4784                             }
4785                             
4786                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4787                                 
4788                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4789                                 
4790                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4791                                     
4792                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4793                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4794                                         continue;
4795                                     }
4796                                     
4797                                 }
4798                                 
4799                             }
4800                             
4801                         }
4802                     }
4803                 }
4804                 continue;
4805             }
4806             */
4807             
4808             child_height += child_nodes[i].offsetHeight;
4809             // Roo.log(child_nodes[i].offsetHeight);
4810         }
4811         
4812         return child_height;
4813     },
4814     toggleHeaderInput : function(is_edit)
4815     {
4816         if (!this.editableTitle) {
4817             return; // not editable.
4818         }
4819         if (is_edit && this.is_header_editing) {
4820             return; // already editing..
4821         }
4822         if (is_edit) {
4823     
4824             this.headerEditEl.dom.value = this.title;
4825             this.headerEditEl.removeClass('d-none');
4826             this.headerEditEl.dom.focus();
4827             this.titleEl.addClass('d-none');
4828             
4829             this.is_header_editing = true;
4830             return
4831         }
4832         // flip back to not editing.
4833         this.title = this.headerEditEl.dom.value;
4834         this.headerEditEl.addClass('d-none');
4835         this.titleEl.removeClass('d-none');
4836         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4837         this.is_header_editing = false;
4838         this.fireEvent('titlechanged', this, this.title);
4839     
4840             
4841         
4842     }
4843
4844 });
4845
4846
4847 Roo.apply(Roo.bootstrap.Modal,  {
4848     /**
4849          * Button config that displays a single OK button
4850          * @type Object
4851          */
4852         OK :  [{
4853             name : 'ok',
4854             weight : 'primary',
4855             html : 'OK'
4856         }],
4857         /**
4858          * Button config that displays Yes and No buttons
4859          * @type Object
4860          */
4861         YESNO : [
4862             {
4863                 name  : 'no',
4864                 html : 'No'
4865             },
4866             {
4867                 name  :'yes',
4868                 weight : 'primary',
4869                 html : 'Yes'
4870             }
4871         ],
4872
4873         /**
4874          * Button config that displays OK and Cancel buttons
4875          * @type Object
4876          */
4877         OKCANCEL : [
4878             {
4879                name : 'cancel',
4880                 html : 'Cancel'
4881             },
4882             {
4883                 name : 'ok',
4884                 weight : 'primary',
4885                 html : 'OK'
4886             }
4887         ],
4888         /**
4889          * Button config that displays Yes, No and Cancel buttons
4890          * @type Object
4891          */
4892         YESNOCANCEL : [
4893             {
4894                 name : 'yes',
4895                 weight : 'primary',
4896                 html : 'Yes'
4897             },
4898             {
4899                 name : 'no',
4900                 html : 'No'
4901             },
4902             {
4903                 name : 'cancel',
4904                 html : 'Cancel'
4905             }
4906         ],
4907         
4908         zIndex : 10001
4909 });
4910
4911 /*
4912  * - LGPL
4913  *
4914  * messagebox - can be used as a replace
4915  * 
4916  */
4917 /**
4918  * @class Roo.MessageBox
4919  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4920  * Example usage:
4921  *<pre><code>
4922 // Basic alert:
4923 Roo.Msg.alert('Status', 'Changes saved successfully.');
4924
4925 // Prompt for user data:
4926 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4927     if (btn == 'ok'){
4928         // process text value...
4929     }
4930 });
4931
4932 // Show a dialog using config options:
4933 Roo.Msg.show({
4934    title:'Save Changes?',
4935    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4936    buttons: Roo.Msg.YESNOCANCEL,
4937    fn: processResult,
4938    animEl: 'elId'
4939 });
4940 </code></pre>
4941  * @singleton
4942  */
4943 Roo.bootstrap.MessageBox = function(){
4944     var dlg, opt, mask, waitTimer;
4945     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4946     var buttons, activeTextEl, bwidth;
4947
4948     
4949     // private
4950     var handleButton = function(button){
4951         dlg.hide();
4952         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4953     };
4954
4955     // private
4956     var handleHide = function(){
4957         if(opt && opt.cls){
4958             dlg.el.removeClass(opt.cls);
4959         }
4960         //if(waitTimer){
4961         //    Roo.TaskMgr.stop(waitTimer);
4962         //    waitTimer = null;
4963         //}
4964     };
4965
4966     // private
4967     var updateButtons = function(b){
4968         var width = 0;
4969         if(!b){
4970             buttons["ok"].hide();
4971             buttons["cancel"].hide();
4972             buttons["yes"].hide();
4973             buttons["no"].hide();
4974             dlg.footerEl.hide();
4975             
4976             return width;
4977         }
4978         dlg.footerEl.show();
4979         for(var k in buttons){
4980             if(typeof buttons[k] != "function"){
4981                 if(b[k]){
4982                     buttons[k].show();
4983                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4984                     width += buttons[k].el.getWidth()+15;
4985                 }else{
4986                     buttons[k].hide();
4987                 }
4988             }
4989         }
4990         return width;
4991     };
4992
4993     // private
4994     var handleEsc = function(d, k, e){
4995         if(opt && opt.closable !== false){
4996             dlg.hide();
4997         }
4998         if(e){
4999             e.stopEvent();
5000         }
5001     };
5002
5003     return {
5004         /**
5005          * Returns a reference to the underlying {@link Roo.BasicDialog} element
5006          * @return {Roo.BasicDialog} The BasicDialog element
5007          */
5008         getDialog : function(){
5009            if(!dlg){
5010                 dlg = new Roo.bootstrap.Modal( {
5011                     //draggable: true,
5012                     //resizable:false,
5013                     //constraintoviewport:false,
5014                     //fixedcenter:true,
5015                     //collapsible : false,
5016                     //shim:true,
5017                     //modal: true,
5018                 //    width: 'auto',
5019                   //  height:100,
5020                     //buttonAlign:"center",
5021                     closeClick : function(){
5022                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
5023                             handleButton("no");
5024                         }else{
5025                             handleButton("cancel");
5026                         }
5027                     }
5028                 });
5029                 dlg.render();
5030                 dlg.on("hide", handleHide);
5031                 mask = dlg.mask;
5032                 //dlg.addKeyListener(27, handleEsc);
5033                 buttons = {};
5034                 this.buttons = buttons;
5035                 var bt = this.buttonText;
5036                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
5037                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
5038                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
5039                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
5040                 //Roo.log(buttons);
5041                 bodyEl = dlg.bodyEl.createChild({
5042
5043                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
5044                         '<textarea class="roo-mb-textarea"></textarea>' +
5045                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
5046                 });
5047                 msgEl = bodyEl.dom.firstChild;
5048                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
5049                 textboxEl.enableDisplayMode();
5050                 textboxEl.addKeyListener([10,13], function(){
5051                     if(dlg.isVisible() && opt && opt.buttons){
5052                         if(opt.buttons.ok){
5053                             handleButton("ok");
5054                         }else if(opt.buttons.yes){
5055                             handleButton("yes");
5056                         }
5057                     }
5058                 });
5059                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
5060                 textareaEl.enableDisplayMode();
5061                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
5062                 progressEl.enableDisplayMode();
5063                 
5064                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
5065                 var pf = progressEl.dom.firstChild;
5066                 if (pf) {
5067                     pp = Roo.get(pf.firstChild);
5068                     pp.setHeight(pf.offsetHeight);
5069                 }
5070                 
5071             }
5072             return dlg;
5073         },
5074
5075         /**
5076          * Updates the message box body text
5077          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
5078          * the XHTML-compliant non-breaking space character '&amp;#160;')
5079          * @return {Roo.MessageBox} This message box
5080          */
5081         updateText : function(text)
5082         {
5083             if(!dlg.isVisible() && !opt.width){
5084                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
5085                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
5086             }
5087             msgEl.innerHTML = text || '&#160;';
5088       
5089             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
5090             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
5091             var w = Math.max(
5092                     Math.min(opt.width || cw , this.maxWidth), 
5093                     Math.max(opt.minWidth || this.minWidth, bwidth)
5094             );
5095             if(opt.prompt){
5096                 activeTextEl.setWidth(w);
5097             }
5098             if(dlg.isVisible()){
5099                 dlg.fixedcenter = false;
5100             }
5101             // to big, make it scroll. = But as usual stupid IE does not support
5102             // !important..
5103             
5104             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
5105                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
5106                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
5107             } else {
5108                 bodyEl.dom.style.height = '';
5109                 bodyEl.dom.style.overflowY = '';
5110             }
5111             if (cw > w) {
5112                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
5113             } else {
5114                 bodyEl.dom.style.overflowX = '';
5115             }
5116             
5117             dlg.setContentSize(w, bodyEl.getHeight());
5118             if(dlg.isVisible()){
5119                 dlg.fixedcenter = true;
5120             }
5121             return this;
5122         },
5123
5124         /**
5125          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
5126          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
5127          * @param {Number} value Any number between 0 and 1 (e.g., .5)
5128          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
5129          * @return {Roo.MessageBox} This message box
5130          */
5131         updateProgress : function(value, text){
5132             if(text){
5133                 this.updateText(text);
5134             }
5135             
5136             if (pp) { // weird bug on my firefox - for some reason this is not defined
5137                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
5138                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
5139             }
5140             return this;
5141         },        
5142
5143         /**
5144          * Returns true if the message box is currently displayed
5145          * @return {Boolean} True if the message box is visible, else false
5146          */
5147         isVisible : function(){
5148             return dlg && dlg.isVisible();  
5149         },
5150
5151         /**
5152          * Hides the message box if it is displayed
5153          */
5154         hide : function(){
5155             if(this.isVisible()){
5156                 dlg.hide();
5157             }  
5158         },
5159
5160         /**
5161          * Displays a new message box, or reinitializes an existing message box, based on the config options
5162          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
5163          * The following config object properties are supported:
5164          * <pre>
5165 Property    Type             Description
5166 ----------  ---------------  ------------------------------------------------------------------------------------
5167 animEl            String/Element   An id or Element from which the message box should animate as it opens and
5168                                    closes (defaults to undefined)
5169 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
5170                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
5171 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
5172                                    progress and wait dialogs will ignore this property and always hide the
5173                                    close button as they can only be closed programmatically.
5174 cls               String           A custom CSS class to apply to the message box element
5175 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
5176                                    displayed (defaults to 75)
5177 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
5178                                    function will be btn (the name of the button that was clicked, if applicable,
5179                                    e.g. "ok"), and text (the value of the active text field, if applicable).
5180                                    Progress and wait dialogs will ignore this option since they do not respond to
5181                                    user actions and can only be closed programmatically, so any required function
5182                                    should be called by the same code after it closes the dialog.
5183 icon              String           A CSS class that provides a background image to be used as an icon for
5184                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
5185 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
5186 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
5187 modal             Boolean          False to allow user interaction with the page while the message box is
5188                                    displayed (defaults to true)
5189 msg               String           A string that will replace the existing message box body text (defaults
5190                                    to the XHTML-compliant non-breaking space character '&#160;')
5191 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
5192 progress          Boolean          True to display a progress bar (defaults to false)
5193 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
5194 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
5195 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
5196 title             String           The title text
5197 value             String           The string value to set into the active textbox element if displayed
5198 wait              Boolean          True to display a progress bar (defaults to false)
5199 width             Number           The width of the dialog in pixels
5200 </pre>
5201          *
5202          * Example usage:
5203          * <pre><code>
5204 Roo.Msg.show({
5205    title: 'Address',
5206    msg: 'Please enter your address:',
5207    width: 300,
5208    buttons: Roo.MessageBox.OKCANCEL,
5209    multiline: true,
5210    fn: saveAddress,
5211    animEl: 'addAddressBtn'
5212 });
5213 </code></pre>
5214          * @param {Object} config Configuration options
5215          * @return {Roo.MessageBox} This message box
5216          */
5217         show : function(options)
5218         {
5219             
5220             // this causes nightmares if you show one dialog after another
5221             // especially on callbacks..
5222              
5223             if(this.isVisible()){
5224                 
5225                 this.hide();
5226                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
5227                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
5228                 Roo.log("New Dialog Message:" +  options.msg )
5229                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
5230                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
5231                 
5232             }
5233             var d = this.getDialog();
5234             opt = options;
5235             d.setTitle(opt.title || "&#160;");
5236             d.closeEl.setDisplayed(opt.closable !== false);
5237             activeTextEl = textboxEl;
5238             opt.prompt = opt.prompt || (opt.multiline ? true : false);
5239             if(opt.prompt){
5240                 if(opt.multiline){
5241                     textboxEl.hide();
5242                     textareaEl.show();
5243                     textareaEl.setHeight(typeof opt.multiline == "number" ?
5244                         opt.multiline : this.defaultTextHeight);
5245                     activeTextEl = textareaEl;
5246                 }else{
5247                     textboxEl.show();
5248                     textareaEl.hide();
5249                 }
5250             }else{
5251                 textboxEl.hide();
5252                 textareaEl.hide();
5253             }
5254             progressEl.setDisplayed(opt.progress === true);
5255             if (opt.progress) {
5256                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5257             }
5258             this.updateProgress(0);
5259             activeTextEl.dom.value = opt.value || "";
5260             if(opt.prompt){
5261                 dlg.setDefaultButton(activeTextEl);
5262             }else{
5263                 var bs = opt.buttons;
5264                 var db = null;
5265                 if(bs && bs.ok){
5266                     db = buttons["ok"];
5267                 }else if(bs && bs.yes){
5268                     db = buttons["yes"];
5269                 }
5270                 dlg.setDefaultButton(db);
5271             }
5272             bwidth = updateButtons(opt.buttons);
5273             this.updateText(opt.msg);
5274             if(opt.cls){
5275                 d.el.addClass(opt.cls);
5276             }
5277             d.proxyDrag = opt.proxyDrag === true;
5278             d.modal = opt.modal !== false;
5279             d.mask = opt.modal !== false ? mask : false;
5280             if(!d.isVisible()){
5281                 // force it to the end of the z-index stack so it gets a cursor in FF
5282                 document.body.appendChild(dlg.el.dom);
5283                 d.animateTarget = null;
5284                 d.show(options.animEl);
5285             }
5286             return this;
5287         },
5288
5289         /**
5290          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5291          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5292          * and closing the message box when the process is complete.
5293          * @param {String} title The title bar text
5294          * @param {String} msg The message box body text
5295          * @return {Roo.MessageBox} This message box
5296          */
5297         progress : function(title, msg){
5298             this.show({
5299                 title : title,
5300                 msg : msg,
5301                 buttons: false,
5302                 progress:true,
5303                 closable:false,
5304                 minWidth: this.minProgressWidth,
5305                 modal : true
5306             });
5307             return this;
5308         },
5309
5310         /**
5311          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5312          * If a callback function is passed it will be called after the user clicks the button, and the
5313          * id of the button that was clicked will be passed as the only parameter to the callback
5314          * (could also be the top-right close button).
5315          * @param {String} title The title bar text
5316          * @param {String} msg The message box body text
5317          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5318          * @param {Object} scope (optional) The scope of the callback function
5319          * @return {Roo.MessageBox} This message box
5320          */
5321         alert : function(title, msg, fn, scope)
5322         {
5323             this.show({
5324                 title : title,
5325                 msg : msg,
5326                 buttons: this.OK,
5327                 fn: fn,
5328                 closable : false,
5329                 scope : scope,
5330                 modal : true
5331             });
5332             return this;
5333         },
5334
5335         /**
5336          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5337          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5338          * You are responsible for closing the message box when the process is complete.
5339          * @param {String} msg The message box body text
5340          * @param {String} title (optional) The title bar text
5341          * @return {Roo.MessageBox} This message box
5342          */
5343         wait : function(msg, title){
5344             this.show({
5345                 title : title,
5346                 msg : msg,
5347                 buttons: false,
5348                 closable:false,
5349                 progress:true,
5350                 modal:true,
5351                 width:300,
5352                 wait:true
5353             });
5354             waitTimer = Roo.TaskMgr.start({
5355                 run: function(i){
5356                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5357                 },
5358                 interval: 1000
5359             });
5360             return this;
5361         },
5362
5363         /**
5364          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5365          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5366          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5367          * @param {String} title The title bar text
5368          * @param {String} msg The message box body text
5369          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5370          * @param {Object} scope (optional) The scope of the callback function
5371          * @return {Roo.MessageBox} This message box
5372          */
5373         confirm : function(title, msg, fn, scope){
5374             this.show({
5375                 title : title,
5376                 msg : msg,
5377                 buttons: this.YESNO,
5378                 fn: fn,
5379                 scope : scope,
5380                 modal : true
5381             });
5382             return this;
5383         },
5384
5385         /**
5386          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5387          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5388          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5389          * (could also be the top-right close button) and the text that was entered will be passed as the two
5390          * parameters to the callback.
5391          * @param {String} title The title bar text
5392          * @param {String} msg The message box body text
5393          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5394          * @param {Object} scope (optional) The scope of the callback function
5395          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5396          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5397          * @return {Roo.MessageBox} This message box
5398          */
5399         prompt : function(title, msg, fn, scope, multiline){
5400             this.show({
5401                 title : title,
5402                 msg : msg,
5403                 buttons: this.OKCANCEL,
5404                 fn: fn,
5405                 minWidth:250,
5406                 scope : scope,
5407                 prompt:true,
5408                 multiline: multiline,
5409                 modal : true
5410             });
5411             return this;
5412         },
5413
5414         /**
5415          * Button config that displays a single OK button
5416          * @type Object
5417          */
5418         OK : {ok:true},
5419         /**
5420          * Button config that displays Yes and No buttons
5421          * @type Object
5422          */
5423         YESNO : {yes:true, no:true},
5424         /**
5425          * Button config that displays OK and Cancel buttons
5426          * @type Object
5427          */
5428         OKCANCEL : {ok:true, cancel:true},
5429         /**
5430          * Button config that displays Yes, No and Cancel buttons
5431          * @type Object
5432          */
5433         YESNOCANCEL : {yes:true, no:true, cancel:true},
5434
5435         /**
5436          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5437          * @type Number
5438          */
5439         defaultTextHeight : 75,
5440         /**
5441          * The maximum width in pixels of the message box (defaults to 600)
5442          * @type Number
5443          */
5444         maxWidth : 600,
5445         /**
5446          * The minimum width in pixels of the message box (defaults to 100)
5447          * @type Number
5448          */
5449         minWidth : 100,
5450         /**
5451          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5452          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5453          * @type Number
5454          */
5455         minProgressWidth : 250,
5456         /**
5457          * An object containing the default button text strings that can be overriden for localized language support.
5458          * Supported properties are: ok, cancel, yes and no.
5459          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5460          * @type Object
5461          */
5462         buttonText : {
5463             ok : "OK",
5464             cancel : "Cancel",
5465             yes : "Yes",
5466             no : "No"
5467         }
5468     };
5469 }();
5470
5471 /**
5472  * Shorthand for {@link Roo.MessageBox}
5473  */
5474 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5475 Roo.Msg = Roo.Msg || Roo.MessageBox;
5476 /*
5477  * - LGPL
5478  *
5479  * navbar
5480  * 
5481  */
5482
5483 /**
5484  * @class Roo.bootstrap.Navbar
5485  * @extends Roo.bootstrap.Component
5486  * Bootstrap Navbar class
5487
5488  * @constructor
5489  * Create a new Navbar
5490  * @param {Object} config The config object
5491  */
5492
5493
5494 Roo.bootstrap.Navbar = function(config){
5495     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5496     this.addEvents({
5497         // raw events
5498         /**
5499          * @event beforetoggle
5500          * Fire before toggle the menu
5501          * @param {Roo.EventObject} e
5502          */
5503         "beforetoggle" : true
5504     });
5505 };
5506
5507 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5508     
5509     
5510    
5511     // private
5512     navItems : false,
5513     loadMask : false,
5514     
5515     
5516     getAutoCreate : function(){
5517         
5518         
5519         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5520         
5521     },
5522     
5523     initEvents :function ()
5524     {
5525         //Roo.log(this.el.select('.navbar-toggle',true));
5526         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5527         
5528         var mark = {
5529             tag: "div",
5530             cls:"x-dlg-mask"
5531         };
5532         
5533         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5534         
5535         var size = this.el.getSize();
5536         this.maskEl.setSize(size.width, size.height);
5537         this.maskEl.enableDisplayMode("block");
5538         this.maskEl.hide();
5539         
5540         if(this.loadMask){
5541             this.maskEl.show();
5542         }
5543     },
5544     
5545     
5546     getChildContainer : function()
5547     {
5548         if (this.el && this.el.select('.collapse').getCount()) {
5549             return this.el.select('.collapse',true).first();
5550         }
5551         
5552         return this.el;
5553     },
5554     
5555     mask : function()
5556     {
5557         this.maskEl.show();
5558     },
5559     
5560     unmask : function()
5561     {
5562         this.maskEl.hide();
5563     },
5564     onToggle : function()
5565     {
5566         
5567         if(this.fireEvent('beforetoggle', this) === false){
5568             return;
5569         }
5570         var ce = this.el.select('.navbar-collapse',true).first();
5571       
5572         if (!ce.hasClass('show')) {
5573            this.expand();
5574         } else {
5575             this.collapse();
5576         }
5577         
5578         
5579     
5580     },
5581     /**
5582      * Expand the navbar pulldown 
5583      */
5584     expand : function ()
5585     {
5586        
5587         var ce = this.el.select('.navbar-collapse',true).first();
5588         if (ce.hasClass('collapsing')) {
5589             return;
5590         }
5591         ce.dom.style.height = '';
5592                // show it...
5593         ce.addClass('in'); // old...
5594         ce.removeClass('collapse');
5595         ce.addClass('show');
5596         var h = ce.getHeight();
5597         Roo.log(h);
5598         ce.removeClass('show');
5599         // at this point we should be able to see it..
5600         ce.addClass('collapsing');
5601         
5602         ce.setHeight(0); // resize it ...
5603         ce.on('transitionend', function() {
5604             //Roo.log('done transition');
5605             ce.removeClass('collapsing');
5606             ce.addClass('show');
5607             ce.removeClass('collapse');
5608
5609             ce.dom.style.height = '';
5610         }, this, { single: true} );
5611         ce.setHeight(h);
5612         ce.dom.scrollTop = 0;
5613     },
5614     /**
5615      * Collapse the navbar pulldown 
5616      */
5617     collapse : function()
5618     {
5619          var ce = this.el.select('.navbar-collapse',true).first();
5620        
5621         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5622             // it's collapsed or collapsing..
5623             return;
5624         }
5625         ce.removeClass('in'); // old...
5626         ce.setHeight(ce.getHeight());
5627         ce.removeClass('show');
5628         ce.addClass('collapsing');
5629         
5630         ce.on('transitionend', function() {
5631             ce.dom.style.height = '';
5632             ce.removeClass('collapsing');
5633             ce.addClass('collapse');
5634         }, this, { single: true} );
5635         ce.setHeight(0);
5636     }
5637     
5638     
5639     
5640 });
5641
5642
5643
5644  
5645
5646  /*
5647  * - LGPL
5648  *
5649  * navbar
5650  * 
5651  */
5652
5653 /**
5654  * @class Roo.bootstrap.NavSimplebar
5655  * @extends Roo.bootstrap.Navbar
5656  * Bootstrap Sidebar class
5657  *
5658  * @cfg {Boolean} inverse is inverted color
5659  * 
5660  * @cfg {String} type (nav | pills | tabs)
5661  * @cfg {Boolean} arrangement stacked | justified
5662  * @cfg {String} align (left | right) alignment
5663  * 
5664  * @cfg {Boolean} main (true|false) main nav bar? default false
5665  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5666  * 
5667  * @cfg {String} tag (header|footer|nav|div) default is nav 
5668
5669  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5670  * 
5671  * 
5672  * @constructor
5673  * Create a new Sidebar
5674  * @param {Object} config The config object
5675  */
5676
5677
5678 Roo.bootstrap.NavSimplebar = function(config){
5679     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5680 };
5681
5682 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5683     
5684     inverse: false,
5685     
5686     type: false,
5687     arrangement: '',
5688     align : false,
5689     
5690     weight : 'light',
5691     
5692     main : false,
5693     
5694     
5695     tag : false,
5696     
5697     
5698     getAutoCreate : function(){
5699         
5700         
5701         var cfg = {
5702             tag : this.tag || 'div',
5703             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5704         };
5705         if (['light','white'].indexOf(this.weight) > -1) {
5706             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5707         }
5708         cfg.cls += ' bg-' + this.weight;
5709         
5710         if (this.inverse) {
5711             cfg.cls += ' navbar-inverse';
5712             
5713         }
5714         
5715         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5716         
5717         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5718             return cfg;
5719         }
5720         
5721         
5722     
5723         
5724         cfg.cn = [
5725             {
5726                 cls: 'nav nav-' + this.xtype,
5727                 tag : 'ul'
5728             }
5729         ];
5730         
5731          
5732         this.type = this.type || 'nav';
5733         if (['tabs','pills'].indexOf(this.type) != -1) {
5734             cfg.cn[0].cls += ' nav-' + this.type
5735         
5736         
5737         } else {
5738             if (this.type!=='nav') {
5739                 Roo.log('nav type must be nav/tabs/pills')
5740             }
5741             cfg.cn[0].cls += ' navbar-nav'
5742         }
5743         
5744         
5745         
5746         
5747         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5748             cfg.cn[0].cls += ' nav-' + this.arrangement;
5749         }
5750         
5751         
5752         if (this.align === 'right') {
5753             cfg.cn[0].cls += ' navbar-right';
5754         }
5755         
5756         
5757         
5758         
5759         return cfg;
5760     
5761         
5762     }
5763     
5764     
5765     
5766 });
5767
5768
5769
5770  
5771
5772  
5773        /*
5774  * - LGPL
5775  *
5776  * navbar
5777  * navbar-fixed-top
5778  * navbar-expand-md  fixed-top 
5779  */
5780
5781 /**
5782  * @class Roo.bootstrap.NavHeaderbar
5783  * @extends Roo.bootstrap.NavSimplebar
5784  * Bootstrap Sidebar class
5785  *
5786  * @cfg {String} brand what is brand
5787  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5788  * @cfg {String} brand_href href of the brand
5789  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5790  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5791  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5792  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5793  * 
5794  * @constructor
5795  * Create a new Sidebar
5796  * @param {Object} config The config object
5797  */
5798
5799
5800 Roo.bootstrap.NavHeaderbar = function(config){
5801     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5802       
5803 };
5804
5805 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5806     
5807     position: '',
5808     brand: '',
5809     brand_href: false,
5810     srButton : true,
5811     autohide : false,
5812     desktopCenter : false,
5813    
5814     
5815     getAutoCreate : function(){
5816         
5817         var   cfg = {
5818             tag: this.nav || 'nav',
5819             cls: 'navbar navbar-expand-md',
5820             role: 'navigation',
5821             cn: []
5822         };
5823         
5824         var cn = cfg.cn;
5825         if (this.desktopCenter) {
5826             cn.push({cls : 'container', cn : []});
5827             cn = cn[0].cn;
5828         }
5829         
5830         if(this.srButton){
5831             var btn = {
5832                 tag: 'button',
5833                 type: 'button',
5834                 cls: 'navbar-toggle navbar-toggler',
5835                 'data-toggle': 'collapse',
5836                 cn: [
5837                     {
5838                         tag: 'span',
5839                         cls: 'sr-only',
5840                         html: 'Toggle navigation'
5841                     },
5842                     {
5843                         tag: 'span',
5844                         cls: 'icon-bar navbar-toggler-icon'
5845                     },
5846                     {
5847                         tag: 'span',
5848                         cls: 'icon-bar'
5849                     },
5850                     {
5851                         tag: 'span',
5852                         cls: 'icon-bar'
5853                     }
5854                 ]
5855             };
5856             
5857             cn.push( Roo.bootstrap.version == 4 ? btn : {
5858                 tag: 'div',
5859                 cls: 'navbar-header',
5860                 cn: [
5861                     btn
5862                 ]
5863             });
5864         }
5865         
5866         cn.push({
5867             tag: 'div',
5868             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5869             cn : []
5870         });
5871         
5872         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5873         
5874         if (['light','white'].indexOf(this.weight) > -1) {
5875             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5876         }
5877         cfg.cls += ' bg-' + this.weight;
5878         
5879         
5880         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5881             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5882             
5883             // tag can override this..
5884             
5885             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5886         }
5887         
5888         if (this.brand !== '') {
5889             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5890             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5891                 tag: 'a',
5892                 href: this.brand_href ? this.brand_href : '#',
5893                 cls: 'navbar-brand',
5894                 cn: [
5895                 this.brand
5896                 ]
5897             });
5898         }
5899         
5900         if(this.main){
5901             cfg.cls += ' main-nav';
5902         }
5903         
5904         
5905         return cfg;
5906
5907         
5908     },
5909     getHeaderChildContainer : function()
5910     {
5911         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5912             return this.el.select('.navbar-header',true).first();
5913         }
5914         
5915         return this.getChildContainer();
5916     },
5917     
5918     getChildContainer : function()
5919     {
5920          
5921         return this.el.select('.roo-navbar-collapse',true).first();
5922          
5923         
5924     },
5925     
5926     initEvents : function()
5927     {
5928         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5929         
5930         if (this.autohide) {
5931             
5932             var prevScroll = 0;
5933             var ft = this.el;
5934             
5935             Roo.get(document).on('scroll',function(e) {
5936                 var ns = Roo.get(document).getScroll().top;
5937                 var os = prevScroll;
5938                 prevScroll = ns;
5939                 
5940                 if(ns > os){
5941                     ft.removeClass('slideDown');
5942                     ft.addClass('slideUp');
5943                     return;
5944                 }
5945                 ft.removeClass('slideUp');
5946                 ft.addClass('slideDown');
5947                  
5948               
5949           },this);
5950         }
5951     }    
5952     
5953 });
5954
5955
5956
5957  
5958
5959  /*
5960  * - LGPL
5961  *
5962  * navbar
5963  * 
5964  */
5965
5966 /**
5967  * @class Roo.bootstrap.NavSidebar
5968  * @extends Roo.bootstrap.Navbar
5969  * Bootstrap Sidebar class
5970  * 
5971  * @constructor
5972  * Create a new Sidebar
5973  * @param {Object} config The config object
5974  */
5975
5976
5977 Roo.bootstrap.NavSidebar = function(config){
5978     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5979 };
5980
5981 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5982     
5983     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5984     
5985     getAutoCreate : function(){
5986         
5987         
5988         return  {
5989             tag: 'div',
5990             cls: 'sidebar sidebar-nav'
5991         };
5992     
5993         
5994     }
5995     
5996     
5997     
5998 });
5999
6000
6001
6002  
6003
6004  /*
6005  * - LGPL
6006  *
6007  * nav group
6008  * 
6009  */
6010
6011 /**
6012  * @class Roo.bootstrap.NavGroup
6013  * @extends Roo.bootstrap.Component
6014  * Bootstrap NavGroup class
6015  * @cfg {String} align (left|right)
6016  * @cfg {Boolean} inverse
6017  * @cfg {String} type (nav|pills|tab) default nav
6018  * @cfg {String} navId - reference Id for navbar.
6019  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
6020  * 
6021  * @constructor
6022  * Create a new nav group
6023  * @param {Object} config The config object
6024  */
6025
6026 Roo.bootstrap.NavGroup = function(config){
6027     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
6028     this.navItems = [];
6029    
6030     Roo.bootstrap.NavGroup.register(this);
6031      this.addEvents({
6032         /**
6033              * @event changed
6034              * Fires when the active item changes
6035              * @param {Roo.bootstrap.NavGroup} this
6036              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
6037              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
6038          */
6039         'changed': true
6040      });
6041     
6042 };
6043
6044 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
6045     
6046     align: '',
6047     inverse: false,
6048     form: false,
6049     type: 'nav',
6050     navId : '',
6051     // private
6052     pilltype : true,
6053     
6054     navItems : false, 
6055     
6056     getAutoCreate : function()
6057     {
6058         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
6059         
6060         cfg = {
6061             tag : 'ul',
6062             cls: 'nav' 
6063         };
6064         if (Roo.bootstrap.version == 4) {
6065             if (['tabs','pills'].indexOf(this.type) != -1) {
6066                 cfg.cls += ' nav-' + this.type; 
6067             } else {
6068                 // trying to remove so header bar can right align top?
6069                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
6070                     // do not use on header bar... 
6071                     cfg.cls += ' navbar-nav';
6072                 }
6073             }
6074             
6075         } else {
6076             if (['tabs','pills'].indexOf(this.type) != -1) {
6077                 cfg.cls += ' nav-' + this.type
6078             } else {
6079                 if (this.type !== 'nav') {
6080                     Roo.log('nav type must be nav/tabs/pills')
6081                 }
6082                 cfg.cls += ' navbar-nav'
6083             }
6084         }
6085         
6086         if (this.parent() && this.parent().sidebar) {
6087             cfg = {
6088                 tag: 'ul',
6089                 cls: 'dashboard-menu sidebar-menu'
6090             };
6091             
6092             return cfg;
6093         }
6094         
6095         if (this.form === true) {
6096             cfg = {
6097                 tag: 'form',
6098                 cls: 'navbar-form form-inline'
6099             };
6100             //nav navbar-right ml-md-auto
6101             if (this.align === 'right') {
6102                 cfg.cls += ' navbar-right ml-md-auto';
6103             } else {
6104                 cfg.cls += ' navbar-left';
6105             }
6106         }
6107         
6108         if (this.align === 'right') {
6109             cfg.cls += ' navbar-right ml-md-auto';
6110         } else {
6111             cfg.cls += ' mr-auto';
6112         }
6113         
6114         if (this.inverse) {
6115             cfg.cls += ' navbar-inverse';
6116             
6117         }
6118         
6119         
6120         return cfg;
6121     },
6122     /**
6123     * sets the active Navigation item
6124     * @param {Roo.bootstrap.NavItem} the new current navitem
6125     */
6126     setActiveItem : function(item)
6127     {
6128         var prev = false;
6129         Roo.each(this.navItems, function(v){
6130             if (v == item) {
6131                 return ;
6132             }
6133             if (v.isActive()) {
6134                 v.setActive(false, true);
6135                 prev = v;
6136                 
6137             }
6138             
6139         });
6140
6141         item.setActive(true, true);
6142         this.fireEvent('changed', this, item, prev);
6143         
6144         
6145     },
6146     /**
6147     * gets the active Navigation item
6148     * @return {Roo.bootstrap.NavItem} the current navitem
6149     */
6150     getActive : function()
6151     {
6152         
6153         var prev = false;
6154         Roo.each(this.navItems, function(v){
6155             
6156             if (v.isActive()) {
6157                 prev = v;
6158                 
6159             }
6160             
6161         });
6162         return prev;
6163     },
6164     
6165     indexOfNav : function()
6166     {
6167         
6168         var prev = false;
6169         Roo.each(this.navItems, function(v,i){
6170             
6171             if (v.isActive()) {
6172                 prev = i;
6173                 
6174             }
6175             
6176         });
6177         return prev;
6178     },
6179     /**
6180     * adds a Navigation item
6181     * @param {Roo.bootstrap.NavItem} the navitem to add
6182     */
6183     addItem : function(cfg)
6184     {
6185         if (this.form && Roo.bootstrap.version == 4) {
6186             cfg.tag = 'div';
6187         }
6188         var cn = new Roo.bootstrap.NavItem(cfg);
6189         this.register(cn);
6190         cn.parentId = this.id;
6191         cn.onRender(this.el, null);
6192         return cn;
6193     },
6194     /**
6195     * register a Navigation item
6196     * @param {Roo.bootstrap.NavItem} the navitem to add
6197     */
6198     register : function(item)
6199     {
6200         this.navItems.push( item);
6201         item.navId = this.navId;
6202     
6203     },
6204     
6205     /**
6206     * clear all the Navigation item
6207     */
6208    
6209     clearAll : function()
6210     {
6211         this.navItems = [];
6212         this.el.dom.innerHTML = '';
6213     },
6214     
6215     getNavItem: function(tabId)
6216     {
6217         var ret = false;
6218         Roo.each(this.navItems, function(e) {
6219             if (e.tabId == tabId) {
6220                ret =  e;
6221                return false;
6222             }
6223             return true;
6224             
6225         });
6226         return ret;
6227     },
6228     
6229     setActiveNext : function()
6230     {
6231         var i = this.indexOfNav(this.getActive());
6232         if (i > this.navItems.length) {
6233             return;
6234         }
6235         this.setActiveItem(this.navItems[i+1]);
6236     },
6237     setActivePrev : function()
6238     {
6239         var i = this.indexOfNav(this.getActive());
6240         if (i  < 1) {
6241             return;
6242         }
6243         this.setActiveItem(this.navItems[i-1]);
6244     },
6245     clearWasActive : function(except) {
6246         Roo.each(this.navItems, function(e) {
6247             if (e.tabId != except.tabId && e.was_active) {
6248                e.was_active = false;
6249                return false;
6250             }
6251             return true;
6252             
6253         });
6254     },
6255     getWasActive : function ()
6256     {
6257         var r = false;
6258         Roo.each(this.navItems, function(e) {
6259             if (e.was_active) {
6260                r = e;
6261                return false;
6262             }
6263             return true;
6264             
6265         });
6266         return r;
6267     }
6268     
6269     
6270 });
6271
6272  
6273 Roo.apply(Roo.bootstrap.NavGroup, {
6274     
6275     groups: {},
6276      /**
6277     * register a Navigation Group
6278     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6279     */
6280     register : function(navgrp)
6281     {
6282         this.groups[navgrp.navId] = navgrp;
6283         
6284     },
6285     /**
6286     * fetch a Navigation Group based on the navigation ID
6287     * @param {string} the navgroup to add
6288     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6289     */
6290     get: function(navId) {
6291         if (typeof(this.groups[navId]) == 'undefined') {
6292             return false;
6293             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6294         }
6295         return this.groups[navId] ;
6296     }
6297     
6298     
6299     
6300 });
6301
6302  /*
6303  * - LGPL
6304  *
6305  * row
6306  * 
6307  */
6308
6309 /**
6310  * @class Roo.bootstrap.NavItem
6311  * @extends Roo.bootstrap.Component
6312  * Bootstrap Navbar.NavItem class
6313  * @cfg {String} href  link to
6314  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6315  * @cfg {Boolean} button_outline show and outlined button
6316  * @cfg {String} html content of button
6317  * @cfg {String} badge text inside badge
6318  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6319  * @cfg {String} glyphicon DEPRICATED - use fa
6320  * @cfg {String} icon DEPRICATED - use fa
6321  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6322  * @cfg {Boolean} active Is item active
6323  * @cfg {Boolean} disabled Is item disabled
6324  * @cfg {String} linkcls  Link Class
6325  * @cfg {Boolean} preventDefault (true | false) default false
6326  * @cfg {String} tabId the tab that this item activates.
6327  * @cfg {String} tagtype (a|span) render as a href or span?
6328  * @cfg {Boolean} animateRef (true|false) link to element default false  
6329   
6330  * @constructor
6331  * Create a new Navbar Item
6332  * @param {Object} config The config object
6333  */
6334 Roo.bootstrap.NavItem = function(config){
6335     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6336     this.addEvents({
6337         // raw events
6338         /**
6339          * @event click
6340          * The raw click event for the entire grid.
6341          * @param {Roo.EventObject} e
6342          */
6343         "click" : true,
6344          /**
6345             * @event changed
6346             * Fires when the active item active state changes
6347             * @param {Roo.bootstrap.NavItem} this
6348             * @param {boolean} state the new state
6349              
6350          */
6351         'changed': true,
6352         /**
6353             * @event scrollto
6354             * Fires when scroll to element
6355             * @param {Roo.bootstrap.NavItem} this
6356             * @param {Object} options
6357             * @param {Roo.EventObject} e
6358              
6359          */
6360         'scrollto': true
6361     });
6362    
6363 };
6364
6365 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6366     
6367     href: false,
6368     html: '',
6369     badge: '',
6370     icon: false,
6371     fa : false,
6372     glyphicon: false,
6373     active: false,
6374     preventDefault : false,
6375     tabId : false,
6376     tagtype : 'a',
6377     tag: 'li',
6378     disabled : false,
6379     animateRef : false,
6380     was_active : false,
6381     button_weight : '',
6382     button_outline : false,
6383     linkcls : '',
6384     navLink: false,
6385     
6386     getAutoCreate : function(){
6387          
6388         var cfg = {
6389             tag: this.tag,
6390             cls: 'nav-item'
6391         };
6392         
6393         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6394         
6395         if (this.active) {
6396             cfg.cls +=  ' active' ;
6397         }
6398         if (this.disabled) {
6399             cfg.cls += ' disabled';
6400         }
6401         
6402         // BS4 only?
6403         if (this.button_weight.length) {
6404             cfg.tag = this.href ? 'a' : 'button';
6405             cfg.html = this.html || '';
6406             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6407             if (this.href) {
6408                 cfg.href = this.href;
6409             }
6410             if (this.fa) {
6411                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + this.html + '</span>';
6412             } else {
6413                 cfg.cls += " nav-html";
6414             }
6415             
6416             // menu .. should add dropdown-menu class - so no need for carat..
6417             
6418             if (this.badge !== '') {
6419                  
6420                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6421             }
6422             return cfg;
6423         }
6424         
6425         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6426             cfg.cn = [
6427                 {
6428                     tag: this.tagtype,
6429                     href : this.href || "#",
6430                     html: this.html || '',
6431                     cls : ''
6432                 }
6433             ];
6434             if (this.tagtype == 'a') {
6435                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6436         
6437             }
6438             if (this.icon) {
6439                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6440             } else  if (this.fa) {
6441                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span class="nav-html">' + cfg.cn[0].html + '</span>';
6442             } else if(this.glyphicon) {
6443                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6444             } else {
6445                 cfg.cn[0].cls += " nav-html";
6446             }
6447             
6448             if (this.menu) {
6449                 cfg.cn[0].html += " <span class='caret'></span>";
6450              
6451             }
6452             
6453             if (this.badge !== '') {
6454                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6455             }
6456         }
6457         
6458         
6459         
6460         return cfg;
6461     },
6462     onRender : function(ct, position)
6463     {
6464        // Roo.log("Call onRender: " + this.xtype);
6465         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6466             this.tag = 'div';
6467         }
6468         
6469         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6470         this.navLink = this.el.select('.nav-link',true).first();
6471         this.htmlEl = this.el.hasClass('nav-html') ? this.el : this.el.select('.nav-html',true).first();
6472         return ret;
6473     },
6474       
6475     
6476     initEvents: function() 
6477     {
6478         if (typeof (this.menu) != 'undefined') {
6479             this.menu.parentType = this.xtype;
6480             this.menu.triggerEl = this.el;
6481             this.menu = this.addxtype(Roo.apply({}, this.menu));
6482         }
6483         
6484         this.el.on('click', this.onClick, this);
6485         
6486         //if(this.tagtype == 'span'){
6487         //    this.el.select('span',true).on('click', this.onClick, this);
6488         //}
6489        
6490         // at this point parent should be available..
6491         this.parent().register(this);
6492     },
6493     
6494     onClick : function(e)
6495     {
6496         if (e.getTarget('.dropdown-menu-item')) {
6497             // did you click on a menu itemm.... - then don't trigger onclick..
6498             return;
6499         }
6500         
6501         if(
6502                 this.preventDefault || 
6503                 this.href == '#' 
6504         ){
6505             Roo.log("NavItem - prevent Default?");
6506             e.preventDefault();
6507         }
6508         
6509         if (this.disabled) {
6510             return;
6511         }
6512         
6513         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6514         if (tg && tg.transition) {
6515             Roo.log("waiting for the transitionend");
6516             return;
6517         }
6518         
6519         
6520         
6521         //Roo.log("fire event clicked");
6522         if(this.fireEvent('click', this, e) === false){
6523             return;
6524         };
6525         
6526         if(this.tagtype == 'span'){
6527             return;
6528         }
6529         
6530         //Roo.log(this.href);
6531         var ael = this.el.select('a',true).first();
6532         //Roo.log(ael);
6533         
6534         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6535             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6536             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6537                 return; // ignore... - it's a 'hash' to another page.
6538             }
6539             Roo.log("NavItem - prevent Default?");
6540             e.preventDefault();
6541             this.scrollToElement(e);
6542         }
6543         
6544         
6545         var p =  this.parent();
6546    
6547         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6548             if (typeof(p.setActiveItem) !== 'undefined') {
6549                 p.setActiveItem(this);
6550             }
6551         }
6552         
6553         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6554         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6555             // remove the collapsed menu expand...
6556             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6557         }
6558     },
6559     
6560     isActive: function () {
6561         return this.active
6562     },
6563     setActive : function(state, fire, is_was_active)
6564     {
6565         if (this.active && !state && this.navId) {
6566             this.was_active = true;
6567             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6568             if (nv) {
6569                 nv.clearWasActive(this);
6570             }
6571             
6572         }
6573         this.active = state;
6574         
6575         if (!state ) {
6576             this.el.removeClass('active');
6577             this.navLink ? this.navLink.removeClass('active') : false;
6578         } else if (!this.el.hasClass('active')) {
6579             
6580             this.el.addClass('active');
6581             if (Roo.bootstrap.version == 4 && this.navLink ) {
6582                 this.navLink.addClass('active');
6583             }
6584             
6585         }
6586         if (fire) {
6587             this.fireEvent('changed', this, state);
6588         }
6589         
6590         // show a panel if it's registered and related..
6591         
6592         if (!this.navId || !this.tabId || !state || is_was_active) {
6593             return;
6594         }
6595         
6596         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6597         if (!tg) {
6598             return;
6599         }
6600         var pan = tg.getPanelByName(this.tabId);
6601         if (!pan) {
6602             return;
6603         }
6604         // if we can not flip to new panel - go back to old nav highlight..
6605         if (false == tg.showPanel(pan)) {
6606             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6607             if (nv) {
6608                 var onav = nv.getWasActive();
6609                 if (onav) {
6610                     onav.setActive(true, false, true);
6611                 }
6612             }
6613             
6614         }
6615         
6616         
6617         
6618     },
6619      // this should not be here...
6620     setDisabled : function(state)
6621     {
6622         this.disabled = state;
6623         if (!state ) {
6624             this.el.removeClass('disabled');
6625         } else if (!this.el.hasClass('disabled')) {
6626             this.el.addClass('disabled');
6627         }
6628         
6629     },
6630     
6631     /**
6632      * Fetch the element to display the tooltip on.
6633      * @return {Roo.Element} defaults to this.el
6634      */
6635     tooltipEl : function()
6636     {
6637         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6638     },
6639     
6640     scrollToElement : function(e)
6641     {
6642         var c = document.body;
6643         
6644         /*
6645          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6646          */
6647         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6648             c = document.documentElement;
6649         }
6650         
6651         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6652         
6653         if(!target){
6654             return;
6655         }
6656
6657         var o = target.calcOffsetsTo(c);
6658         
6659         var options = {
6660             target : target,
6661             value : o[1]
6662         };
6663         
6664         this.fireEvent('scrollto', this, options, e);
6665         
6666         Roo.get(c).scrollTo('top', options.value, true);
6667         
6668         return;
6669     },
6670     /**
6671      * Set the HTML (text content) of the item
6672      * @param {string} html  content for the nav item
6673      */
6674     setHtml : function(html)
6675     {
6676         this.html = html;
6677         this.htmlEl.dom.innerHTML = html;
6678         
6679     } 
6680 });
6681  
6682
6683  /*
6684  * - LGPL
6685  *
6686  * sidebar item
6687  *
6688  *  li
6689  *    <span> icon </span>
6690  *    <span> text </span>
6691  *    <span>badge </span>
6692  */
6693
6694 /**
6695  * @class Roo.bootstrap.NavSidebarItem
6696  * @extends Roo.bootstrap.NavItem
6697  * Bootstrap Navbar.NavSidebarItem class
6698  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6699  * {Boolean} open is the menu open
6700  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6701  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6702  * {String} buttonSize (sm|md|lg)the extra classes for the button
6703  * {Boolean} showArrow show arrow next to the text (default true)
6704  * @constructor
6705  * Create a new Navbar Button
6706  * @param {Object} config The config object
6707  */
6708 Roo.bootstrap.NavSidebarItem = function(config){
6709     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6710     this.addEvents({
6711         // raw events
6712         /**
6713          * @event click
6714          * The raw click event for the entire grid.
6715          * @param {Roo.EventObject} e
6716          */
6717         "click" : true,
6718          /**
6719             * @event changed
6720             * Fires when the active item active state changes
6721             * @param {Roo.bootstrap.NavSidebarItem} this
6722             * @param {boolean} state the new state
6723              
6724          */
6725         'changed': true
6726     });
6727    
6728 };
6729
6730 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6731     
6732     badgeWeight : 'default',
6733     
6734     open: false,
6735     
6736     buttonView : false,
6737     
6738     buttonWeight : 'default',
6739     
6740     buttonSize : 'md',
6741     
6742     showArrow : true,
6743     
6744     getAutoCreate : function(){
6745         
6746         
6747         var a = {
6748                 tag: 'a',
6749                 href : this.href || '#',
6750                 cls: '',
6751                 html : '',
6752                 cn : []
6753         };
6754         
6755         if(this.buttonView){
6756             a = {
6757                 tag: 'button',
6758                 href : this.href || '#',
6759                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6760                 html : this.html,
6761                 cn : []
6762             };
6763         }
6764         
6765         var cfg = {
6766             tag: 'li',
6767             cls: '',
6768             cn: [ a ]
6769         };
6770         
6771         if (this.active) {
6772             cfg.cls += ' active';
6773         }
6774         
6775         if (this.disabled) {
6776             cfg.cls += ' disabled';
6777         }
6778         if (this.open) {
6779             cfg.cls += ' open x-open';
6780         }
6781         // left icon..
6782         if (this.glyphicon || this.icon) {
6783             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6784             a.cn.push({ tag : 'i', cls : c }) ;
6785         }
6786         
6787         if(!this.buttonView){
6788             var span = {
6789                 tag: 'span',
6790                 html : this.html || ''
6791             };
6792
6793             a.cn.push(span);
6794             
6795         }
6796         
6797         if (this.badge !== '') {
6798             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6799         }
6800         
6801         if (this.menu) {
6802             
6803             if(this.showArrow){
6804                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6805             }
6806             
6807             a.cls += ' dropdown-toggle treeview' ;
6808         }
6809         
6810         return cfg;
6811     },
6812     
6813     initEvents : function()
6814     { 
6815         if (typeof (this.menu) != 'undefined') {
6816             this.menu.parentType = this.xtype;
6817             this.menu.triggerEl = this.el;
6818             this.menu = this.addxtype(Roo.apply({}, this.menu));
6819         }
6820         
6821         this.el.on('click', this.onClick, this);
6822         
6823         if(this.badge !== ''){
6824             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6825         }
6826         
6827     },
6828     
6829     onClick : function(e)
6830     {
6831         if(this.disabled){
6832             e.preventDefault();
6833             return;
6834         }
6835         
6836         if(this.preventDefault){
6837             e.preventDefault();
6838         }
6839         
6840         this.fireEvent('click', this, e);
6841     },
6842     
6843     disable : function()
6844     {
6845         this.setDisabled(true);
6846     },
6847     
6848     enable : function()
6849     {
6850         this.setDisabled(false);
6851     },
6852     
6853     setDisabled : function(state)
6854     {
6855         if(this.disabled == state){
6856             return;
6857         }
6858         
6859         this.disabled = state;
6860         
6861         if (state) {
6862             this.el.addClass('disabled');
6863             return;
6864         }
6865         
6866         this.el.removeClass('disabled');
6867         
6868         return;
6869     },
6870     
6871     setActive : function(state)
6872     {
6873         if(this.active == state){
6874             return;
6875         }
6876         
6877         this.active = state;
6878         
6879         if (state) {
6880             this.el.addClass('active');
6881             return;
6882         }
6883         
6884         this.el.removeClass('active');
6885         
6886         return;
6887     },
6888     
6889     isActive: function () 
6890     {
6891         return this.active;
6892     },
6893     
6894     setBadge : function(str)
6895     {
6896         if(!this.badgeEl){
6897             return;
6898         }
6899         
6900         this.badgeEl.dom.innerHTML = str;
6901     }
6902     
6903    
6904      
6905  
6906 });
6907  
6908
6909  /*
6910  * - LGPL
6911  *
6912  *  Breadcrumb Nav
6913  * 
6914  */
6915 Roo.namespace('Roo.bootstrap.breadcrumb');
6916
6917
6918 /**
6919  * @class Roo.bootstrap.breadcrumb.Nav
6920  * @extends Roo.bootstrap.Component
6921  * Bootstrap Breadcrumb Nav Class
6922  *  
6923  * @children Roo.bootstrap.breadcrumb.Item
6924  * 
6925  * @constructor
6926  * Create a new breadcrumb.Nav
6927  * @param {Object} config The config object
6928  */
6929
6930
6931 Roo.bootstrap.breadcrumb.Nav = function(config){
6932     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6933     
6934     
6935 };
6936
6937 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6938     
6939     getAutoCreate : function()
6940     {
6941
6942         var cfg = {
6943             tag: 'nav',
6944             cn : [
6945                 {
6946                     tag : 'ol',
6947                     cls : 'breadcrumb'
6948                 }
6949             ]
6950             
6951         };
6952           
6953         return cfg;
6954     },
6955     
6956     initEvents: function()
6957     {
6958         this.olEl = this.el.select('ol',true).first();    
6959     },
6960     getChildContainer : function()
6961     {
6962         return this.olEl;  
6963     }
6964     
6965 });
6966
6967  /*
6968  * - LGPL
6969  *
6970  *  Breadcrumb Item
6971  * 
6972  */
6973
6974
6975 /**
6976  * @class Roo.bootstrap.breadcrumb.Nav
6977  * @extends Roo.bootstrap.Component
6978  * Bootstrap Breadcrumb Nav Class
6979  *  
6980  * @children Roo.bootstrap.breadcrumb.Component
6981  * @cfg {String} html the content of the link.
6982  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6983  * @cfg {Boolean} active is it active
6984
6985  * 
6986  * @constructor
6987  * Create a new breadcrumb.Nav
6988  * @param {Object} config The config object
6989  */
6990
6991 Roo.bootstrap.breadcrumb.Item = function(config){
6992     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6993     this.addEvents({
6994         // img events
6995         /**
6996          * @event click
6997          * The img click event for the img.
6998          * @param {Roo.EventObject} e
6999          */
7000         "click" : true
7001     });
7002     
7003 };
7004
7005 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
7006     
7007     href: false,
7008     html : '',
7009     
7010     getAutoCreate : function()
7011     {
7012
7013         var cfg = {
7014             tag: 'li',
7015             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
7016         };
7017         if (this.href !== false) {
7018             cfg.cn = [{
7019                 tag : 'a',
7020                 href : this.href,
7021                 html : this.html
7022             }];
7023         } else {
7024             cfg.html = this.html;
7025         }
7026         
7027         return cfg;
7028     },
7029     
7030     initEvents: function()
7031     {
7032         if (this.href) {
7033             this.el.select('a', true).first().on('click',this.onClick, this)
7034         }
7035         
7036     },
7037     onClick : function(e)
7038     {
7039         e.preventDefault();
7040         this.fireEvent('click',this,  e);
7041     }
7042     
7043 });
7044
7045  /*
7046  * - LGPL
7047  *
7048  * row
7049  * 
7050  */
7051
7052 /**
7053  * @class Roo.bootstrap.Row
7054  * @extends Roo.bootstrap.Component
7055  * Bootstrap Row class (contains columns...)
7056  * 
7057  * @constructor
7058  * Create a new Row
7059  * @param {Object} config The config object
7060  */
7061
7062 Roo.bootstrap.Row = function(config){
7063     Roo.bootstrap.Row.superclass.constructor.call(this, config);
7064 };
7065
7066 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
7067     
7068     getAutoCreate : function(){
7069        return {
7070             cls: 'row clearfix'
7071        };
7072     }
7073     
7074     
7075 });
7076
7077  
7078
7079  /*
7080  * - LGPL
7081  *
7082  * pagination
7083  * 
7084  */
7085
7086 /**
7087  * @class Roo.bootstrap.Pagination
7088  * @extends Roo.bootstrap.Component
7089  * Bootstrap Pagination class
7090  * @cfg {String} size xs | sm | md | lg
7091  * @cfg {Boolean} inverse false | true
7092  * 
7093  * @constructor
7094  * Create a new Pagination
7095  * @param {Object} config The config object
7096  */
7097
7098 Roo.bootstrap.Pagination = function(config){
7099     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
7100 };
7101
7102 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
7103     
7104     cls: false,
7105     size: false,
7106     inverse: false,
7107     
7108     getAutoCreate : function(){
7109         var cfg = {
7110             tag: 'ul',
7111                 cls: 'pagination'
7112         };
7113         if (this.inverse) {
7114             cfg.cls += ' inverse';
7115         }
7116         if (this.html) {
7117             cfg.html=this.html;
7118         }
7119         if (this.cls) {
7120             cfg.cls += " " + this.cls;
7121         }
7122         return cfg;
7123     }
7124    
7125 });
7126
7127  
7128
7129  /*
7130  * - LGPL
7131  *
7132  * Pagination item
7133  * 
7134  */
7135
7136
7137 /**
7138  * @class Roo.bootstrap.PaginationItem
7139  * @extends Roo.bootstrap.Component
7140  * Bootstrap PaginationItem class
7141  * @cfg {String} html text
7142  * @cfg {String} href the link
7143  * @cfg {Boolean} preventDefault (true | false) default true
7144  * @cfg {Boolean} active (true | false) default false
7145  * @cfg {Boolean} disabled default false
7146  * 
7147  * 
7148  * @constructor
7149  * Create a new PaginationItem
7150  * @param {Object} config The config object
7151  */
7152
7153
7154 Roo.bootstrap.PaginationItem = function(config){
7155     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
7156     this.addEvents({
7157         // raw events
7158         /**
7159          * @event click
7160          * The raw click event for the entire grid.
7161          * @param {Roo.EventObject} e
7162          */
7163         "click" : true
7164     });
7165 };
7166
7167 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
7168     
7169     href : false,
7170     html : false,
7171     preventDefault: true,
7172     active : false,
7173     cls : false,
7174     disabled: false,
7175     
7176     getAutoCreate : function(){
7177         var cfg= {
7178             tag: 'li',
7179             cn: [
7180                 {
7181                     tag : 'a',
7182                     href : this.href ? this.href : '#',
7183                     html : this.html ? this.html : ''
7184                 }
7185             ]
7186         };
7187         
7188         if(this.cls){
7189             cfg.cls = this.cls;
7190         }
7191         
7192         if(this.disabled){
7193             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
7194         }
7195         
7196         if(this.active){
7197             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
7198         }
7199         
7200         return cfg;
7201     },
7202     
7203     initEvents: function() {
7204         
7205         this.el.on('click', this.onClick, this);
7206         
7207     },
7208     onClick : function(e)
7209     {
7210         Roo.log('PaginationItem on click ');
7211         if(this.preventDefault){
7212             e.preventDefault();
7213         }
7214         
7215         if(this.disabled){
7216             return;
7217         }
7218         
7219         this.fireEvent('click', this, e);
7220     }
7221    
7222 });
7223
7224  
7225
7226  /*
7227  * - LGPL
7228  *
7229  * slider
7230  * 
7231  */
7232
7233
7234 /**
7235  * @class Roo.bootstrap.Slider
7236  * @extends Roo.bootstrap.Component
7237  * Bootstrap Slider class
7238  *    
7239  * @constructor
7240  * Create a new Slider
7241  * @param {Object} config The config object
7242  */
7243
7244 Roo.bootstrap.Slider = function(config){
7245     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
7246 };
7247
7248 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
7249     
7250     getAutoCreate : function(){
7251         
7252         var cfg = {
7253             tag: 'div',
7254             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
7255             cn: [
7256                 {
7257                     tag: 'a',
7258                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
7259                 }
7260             ]
7261         };
7262         
7263         return cfg;
7264     }
7265    
7266 });
7267
7268  /*
7269  * Based on:
7270  * Ext JS Library 1.1.1
7271  * Copyright(c) 2006-2007, Ext JS, LLC.
7272  *
7273  * Originally Released Under LGPL - original licence link has changed is not relivant.
7274  *
7275  * Fork - LGPL
7276  * <script type="text/javascript">
7277  */
7278  
7279
7280 /**
7281  * @class Roo.grid.ColumnModel
7282  * @extends Roo.util.Observable
7283  * This is the default implementation of a ColumnModel used by the Grid. It defines
7284  * the columns in the grid.
7285  * <br>Usage:<br>
7286  <pre><code>
7287  var colModel = new Roo.grid.ColumnModel([
7288         {header: "Ticker", width: 60, sortable: true, locked: true},
7289         {header: "Company Name", width: 150, sortable: true},
7290         {header: "Market Cap.", width: 100, sortable: true},
7291         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7292         {header: "Employees", width: 100, sortable: true, resizable: false}
7293  ]);
7294  </code></pre>
7295  * <p>
7296  
7297  * The config options listed for this class are options which may appear in each
7298  * individual column definition.
7299  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7300  * @constructor
7301  * @param {Object} config An Array of column config objects. See this class's
7302  * config objects for details.
7303 */
7304 Roo.grid.ColumnModel = function(config){
7305         /**
7306      * The config passed into the constructor
7307      */
7308     this.config = config;
7309     this.lookup = {};
7310
7311     // if no id, create one
7312     // if the column does not have a dataIndex mapping,
7313     // map it to the order it is in the config
7314     for(var i = 0, len = config.length; i < len; i++){
7315         var c = config[i];
7316         if(typeof c.dataIndex == "undefined"){
7317             c.dataIndex = i;
7318         }
7319         if(typeof c.renderer == "string"){
7320             c.renderer = Roo.util.Format[c.renderer];
7321         }
7322         if(typeof c.id == "undefined"){
7323             c.id = Roo.id();
7324         }
7325         if(c.editor && c.editor.xtype){
7326             c.editor  = Roo.factory(c.editor, Roo.grid);
7327         }
7328         if(c.editor && c.editor.isFormField){
7329             c.editor = new Roo.grid.GridEditor(c.editor);
7330         }
7331         this.lookup[c.id] = c;
7332     }
7333
7334     /**
7335      * The width of columns which have no width specified (defaults to 100)
7336      * @type Number
7337      */
7338     this.defaultWidth = 100;
7339
7340     /**
7341      * Default sortable of columns which have no sortable specified (defaults to false)
7342      * @type Boolean
7343      */
7344     this.defaultSortable = false;
7345
7346     this.addEvents({
7347         /**
7348              * @event widthchange
7349              * Fires when the width of a column changes.
7350              * @param {ColumnModel} this
7351              * @param {Number} columnIndex The column index
7352              * @param {Number} newWidth The new width
7353              */
7354             "widthchange": true,
7355         /**
7356              * @event headerchange
7357              * Fires when the text of a header changes.
7358              * @param {ColumnModel} this
7359              * @param {Number} columnIndex The column index
7360              * @param {Number} newText The new header text
7361              */
7362             "headerchange": true,
7363         /**
7364              * @event hiddenchange
7365              * Fires when a column is hidden or "unhidden".
7366              * @param {ColumnModel} this
7367              * @param {Number} columnIndex The column index
7368              * @param {Boolean} hidden true if hidden, false otherwise
7369              */
7370             "hiddenchange": true,
7371             /**
7372          * @event columnmoved
7373          * Fires when a column is moved.
7374          * @param {ColumnModel} this
7375          * @param {Number} oldIndex
7376          * @param {Number} newIndex
7377          */
7378         "columnmoved" : true,
7379         /**
7380          * @event columlockchange
7381          * Fires when a column's locked state is changed
7382          * @param {ColumnModel} this
7383          * @param {Number} colIndex
7384          * @param {Boolean} locked true if locked
7385          */
7386         "columnlockchange" : true
7387     });
7388     Roo.grid.ColumnModel.superclass.constructor.call(this);
7389 };
7390 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7391     /**
7392      * @cfg {String} header The header text to display in the Grid view.
7393      */
7394     /**
7395      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7396      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7397      * specified, the column's index is used as an index into the Record's data Array.
7398      */
7399     /**
7400      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7401      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7402      */
7403     /**
7404      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7405      * Defaults to the value of the {@link #defaultSortable} property.
7406      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7407      */
7408     /**
7409      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7410      */
7411     /**
7412      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7413      */
7414     /**
7415      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7416      */
7417     /**
7418      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7419      */
7420     /**
7421      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7422      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7423      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7424      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7425      */
7426        /**
7427      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7428      */
7429     /**
7430      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7431      */
7432     /**
7433      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7434      */
7435     /**
7436      * @cfg {String} cursor (Optional)
7437      */
7438     /**
7439      * @cfg {String} tooltip (Optional)
7440      */
7441     /**
7442      * @cfg {Number} xs (Optional)
7443      */
7444     /**
7445      * @cfg {Number} sm (Optional)
7446      */
7447     /**
7448      * @cfg {Number} md (Optional)
7449      */
7450     /**
7451      * @cfg {Number} lg (Optional)
7452      */
7453     /**
7454      * Returns the id of the column at the specified index.
7455      * @param {Number} index The column index
7456      * @return {String} the id
7457      */
7458     getColumnId : function(index){
7459         return this.config[index].id;
7460     },
7461
7462     /**
7463      * Returns the column for a specified id.
7464      * @param {String} id The column id
7465      * @return {Object} the column
7466      */
7467     getColumnById : function(id){
7468         return this.lookup[id];
7469     },
7470
7471     
7472     /**
7473      * Returns the column for a specified dataIndex.
7474      * @param {String} dataIndex The column dataIndex
7475      * @return {Object|Boolean} the column or false if not found
7476      */
7477     getColumnByDataIndex: function(dataIndex){
7478         var index = this.findColumnIndex(dataIndex);
7479         return index > -1 ? this.config[index] : false;
7480     },
7481     
7482     /**
7483      * Returns the index for a specified column id.
7484      * @param {String} id The column id
7485      * @return {Number} the index, or -1 if not found
7486      */
7487     getIndexById : function(id){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].id == id){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     /**
7497      * Returns the index for a specified column dataIndex.
7498      * @param {String} dataIndex The column dataIndex
7499      * @return {Number} the index, or -1 if not found
7500      */
7501     
7502     findColumnIndex : function(dataIndex){
7503         for(var i = 0, len = this.config.length; i < len; i++){
7504             if(this.config[i].dataIndex == dataIndex){
7505                 return i;
7506             }
7507         }
7508         return -1;
7509     },
7510     
7511     
7512     moveColumn : function(oldIndex, newIndex){
7513         var c = this.config[oldIndex];
7514         this.config.splice(oldIndex, 1);
7515         this.config.splice(newIndex, 0, c);
7516         this.dataMap = null;
7517         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7518     },
7519
7520     isLocked : function(colIndex){
7521         return this.config[colIndex].locked === true;
7522     },
7523
7524     setLocked : function(colIndex, value, suppressEvent){
7525         if(this.isLocked(colIndex) == value){
7526             return;
7527         }
7528         this.config[colIndex].locked = value;
7529         if(!suppressEvent){
7530             this.fireEvent("columnlockchange", this, colIndex, value);
7531         }
7532     },
7533
7534     getTotalLockedWidth : function(){
7535         var totalWidth = 0;
7536         for(var i = 0; i < this.config.length; i++){
7537             if(this.isLocked(i) && !this.isHidden(i)){
7538                 this.totalWidth += this.getColumnWidth(i);
7539             }
7540         }
7541         return totalWidth;
7542     },
7543
7544     getLockedCount : function(){
7545         for(var i = 0, len = this.config.length; i < len; i++){
7546             if(!this.isLocked(i)){
7547                 return i;
7548             }
7549         }
7550         
7551         return this.config.length;
7552     },
7553
7554     /**
7555      * Returns the number of columns.
7556      * @return {Number}
7557      */
7558     getColumnCount : function(visibleOnly){
7559         if(visibleOnly === true){
7560             var c = 0;
7561             for(var i = 0, len = this.config.length; i < len; i++){
7562                 if(!this.isHidden(i)){
7563                     c++;
7564                 }
7565             }
7566             return c;
7567         }
7568         return this.config.length;
7569     },
7570
7571     /**
7572      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7573      * @param {Function} fn
7574      * @param {Object} scope (optional)
7575      * @return {Array} result
7576      */
7577     getColumnsBy : function(fn, scope){
7578         var r = [];
7579         for(var i = 0, len = this.config.length; i < len; i++){
7580             var c = this.config[i];
7581             if(fn.call(scope||this, c, i) === true){
7582                 r[r.length] = c;
7583             }
7584         }
7585         return r;
7586     },
7587
7588     /**
7589      * Returns true if the specified column is sortable.
7590      * @param {Number} col The column index
7591      * @return {Boolean}
7592      */
7593     isSortable : function(col){
7594         if(typeof this.config[col].sortable == "undefined"){
7595             return this.defaultSortable;
7596         }
7597         return this.config[col].sortable;
7598     },
7599
7600     /**
7601      * Returns the rendering (formatting) function defined for the column.
7602      * @param {Number} col The column index.
7603      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7604      */
7605     getRenderer : function(col){
7606         if(!this.config[col].renderer){
7607             return Roo.grid.ColumnModel.defaultRenderer;
7608         }
7609         return this.config[col].renderer;
7610     },
7611
7612     /**
7613      * Sets the rendering (formatting) function for a column.
7614      * @param {Number} col The column index
7615      * @param {Function} fn The function to use to process the cell's raw data
7616      * to return HTML markup for the grid view. The render function is called with
7617      * the following parameters:<ul>
7618      * <li>Data value.</li>
7619      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7620      * <li>css A CSS style string to apply to the table cell.</li>
7621      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7622      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7623      * <li>Row index</li>
7624      * <li>Column index</li>
7625      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7626      */
7627     setRenderer : function(col, fn){
7628         this.config[col].renderer = fn;
7629     },
7630
7631     /**
7632      * Returns the width for the specified column.
7633      * @param {Number} col The column index
7634      * @return {Number}
7635      */
7636     getColumnWidth : function(col){
7637         return this.config[col].width * 1 || this.defaultWidth;
7638     },
7639
7640     /**
7641      * Sets the width for a column.
7642      * @param {Number} col The column index
7643      * @param {Number} width The new width
7644      */
7645     setColumnWidth : function(col, width, suppressEvent){
7646         this.config[col].width = width;
7647         this.totalWidth = null;
7648         if(!suppressEvent){
7649              this.fireEvent("widthchange", this, col, width);
7650         }
7651     },
7652
7653     /**
7654      * Returns the total width of all columns.
7655      * @param {Boolean} includeHidden True to include hidden column widths
7656      * @return {Number}
7657      */
7658     getTotalWidth : function(includeHidden){
7659         if(!this.totalWidth){
7660             this.totalWidth = 0;
7661             for(var i = 0, len = this.config.length; i < len; i++){
7662                 if(includeHidden || !this.isHidden(i)){
7663                     this.totalWidth += this.getColumnWidth(i);
7664                 }
7665             }
7666         }
7667         return this.totalWidth;
7668     },
7669
7670     /**
7671      * Returns the header for the specified column.
7672      * @param {Number} col The column index
7673      * @return {String}
7674      */
7675     getColumnHeader : function(col){
7676         return this.config[col].header;
7677     },
7678
7679     /**
7680      * Sets the header for a column.
7681      * @param {Number} col The column index
7682      * @param {String} header The new header
7683      */
7684     setColumnHeader : function(col, header){
7685         this.config[col].header = header;
7686         this.fireEvent("headerchange", this, col, header);
7687     },
7688
7689     /**
7690      * Returns the tooltip for the specified column.
7691      * @param {Number} col The column index
7692      * @return {String}
7693      */
7694     getColumnTooltip : function(col){
7695             return this.config[col].tooltip;
7696     },
7697     /**
7698      * Sets the tooltip for a column.
7699      * @param {Number} col The column index
7700      * @param {String} tooltip The new tooltip
7701      */
7702     setColumnTooltip : function(col, tooltip){
7703             this.config[col].tooltip = tooltip;
7704     },
7705
7706     /**
7707      * Returns the dataIndex for the specified column.
7708      * @param {Number} col The column index
7709      * @return {Number}
7710      */
7711     getDataIndex : function(col){
7712         return this.config[col].dataIndex;
7713     },
7714
7715     /**
7716      * Sets the dataIndex for a column.
7717      * @param {Number} col The column index
7718      * @param {Number} dataIndex The new dataIndex
7719      */
7720     setDataIndex : function(col, dataIndex){
7721         this.config[col].dataIndex = dataIndex;
7722     },
7723
7724     
7725     
7726     /**
7727      * Returns true if the cell is editable.
7728      * @param {Number} colIndex The column index
7729      * @param {Number} rowIndex The row index - this is nto actually used..?
7730      * @return {Boolean}
7731      */
7732     isCellEditable : function(colIndex, rowIndex){
7733         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7734     },
7735
7736     /**
7737      * Returns the editor defined for the cell/column.
7738      * return false or null to disable editing.
7739      * @param {Number} colIndex The column index
7740      * @param {Number} rowIndex The row index
7741      * @return {Object}
7742      */
7743     getCellEditor : function(colIndex, rowIndex){
7744         return this.config[colIndex].editor;
7745     },
7746
7747     /**
7748      * Sets if a column is editable.
7749      * @param {Number} col The column index
7750      * @param {Boolean} editable True if the column is editable
7751      */
7752     setEditable : function(col, editable){
7753         this.config[col].editable = editable;
7754     },
7755
7756
7757     /**
7758      * Returns true if the column is hidden.
7759      * @param {Number} colIndex The column index
7760      * @return {Boolean}
7761      */
7762     isHidden : function(colIndex){
7763         return this.config[colIndex].hidden;
7764     },
7765
7766
7767     /**
7768      * Returns true if the column width cannot be changed
7769      */
7770     isFixed : function(colIndex){
7771         return this.config[colIndex].fixed;
7772     },
7773
7774     /**
7775      * Returns true if the column can be resized
7776      * @return {Boolean}
7777      */
7778     isResizable : function(colIndex){
7779         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7780     },
7781     /**
7782      * Sets if a column is hidden.
7783      * @param {Number} colIndex The column index
7784      * @param {Boolean} hidden True if the column is hidden
7785      */
7786     setHidden : function(colIndex, hidden){
7787         this.config[colIndex].hidden = hidden;
7788         this.totalWidth = null;
7789         this.fireEvent("hiddenchange", this, colIndex, hidden);
7790     },
7791
7792     /**
7793      * Sets the editor for a column.
7794      * @param {Number} col The column index
7795      * @param {Object} editor The editor object
7796      */
7797     setEditor : function(col, editor){
7798         this.config[col].editor = editor;
7799     }
7800 });
7801
7802 Roo.grid.ColumnModel.defaultRenderer = function(value)
7803 {
7804     if(typeof value == "object") {
7805         return value;
7806     }
7807         if(typeof value == "string" && value.length < 1){
7808             return "&#160;";
7809         }
7810     
7811         return String.format("{0}", value);
7812 };
7813
7814 // Alias for backwards compatibility
7815 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7816 /*
7817  * Based on:
7818  * Ext JS Library 1.1.1
7819  * Copyright(c) 2006-2007, Ext JS, LLC.
7820  *
7821  * Originally Released Under LGPL - original licence link has changed is not relivant.
7822  *
7823  * Fork - LGPL
7824  * <script type="text/javascript">
7825  */
7826  
7827 /**
7828  * @class Roo.LoadMask
7829  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7830  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7831  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7832  * element's UpdateManager load indicator and will be destroyed after the initial load.
7833  * @constructor
7834  * Create a new LoadMask
7835  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7836  * @param {Object} config The config object
7837  */
7838 Roo.LoadMask = function(el, config){
7839     this.el = Roo.get(el);
7840     Roo.apply(this, config);
7841     if(this.store){
7842         this.store.on('beforeload', this.onBeforeLoad, this);
7843         this.store.on('load', this.onLoad, this);
7844         this.store.on('loadexception', this.onLoadException, this);
7845         this.removeMask = false;
7846     }else{
7847         var um = this.el.getUpdateManager();
7848         um.showLoadIndicator = false; // disable the default indicator
7849         um.on('beforeupdate', this.onBeforeLoad, this);
7850         um.on('update', this.onLoad, this);
7851         um.on('failure', this.onLoad, this);
7852         this.removeMask = true;
7853     }
7854 };
7855
7856 Roo.LoadMask.prototype = {
7857     /**
7858      * @cfg {Boolean} removeMask
7859      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7860      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7861      */
7862     /**
7863      * @cfg {String} msg
7864      * The text to display in a centered loading message box (defaults to 'Loading...')
7865      */
7866     msg : 'Loading...',
7867     /**
7868      * @cfg {String} msgCls
7869      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7870      */
7871     msgCls : 'x-mask-loading',
7872
7873     /**
7874      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7875      * @type Boolean
7876      */
7877     disabled: false,
7878
7879     /**
7880      * Disables the mask to prevent it from being displayed
7881      */
7882     disable : function(){
7883        this.disabled = true;
7884     },
7885
7886     /**
7887      * Enables the mask so that it can be displayed
7888      */
7889     enable : function(){
7890         this.disabled = false;
7891     },
7892     
7893     onLoadException : function()
7894     {
7895         Roo.log(arguments);
7896         
7897         if (typeof(arguments[3]) != 'undefined') {
7898             Roo.MessageBox.alert("Error loading",arguments[3]);
7899         } 
7900         /*
7901         try {
7902             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7903                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7904             }   
7905         } catch(e) {
7906             
7907         }
7908         */
7909     
7910         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7911     },
7912     // private
7913     onLoad : function()
7914     {
7915         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7916     },
7917
7918     // private
7919     onBeforeLoad : function(){
7920         if(!this.disabled){
7921             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7922         }
7923     },
7924
7925     // private
7926     destroy : function(){
7927         if(this.store){
7928             this.store.un('beforeload', this.onBeforeLoad, this);
7929             this.store.un('load', this.onLoad, this);
7930             this.store.un('loadexception', this.onLoadException, this);
7931         }else{
7932             var um = this.el.getUpdateManager();
7933             um.un('beforeupdate', this.onBeforeLoad, this);
7934             um.un('update', this.onLoad, this);
7935             um.un('failure', this.onLoad, this);
7936         }
7937     }
7938 };/*
7939  * - LGPL
7940  *
7941  * table
7942  * 
7943  */
7944
7945 /**
7946  * @class Roo.bootstrap.Table
7947  * @extends Roo.bootstrap.Component
7948  * Bootstrap Table class
7949  * @cfg {String} cls table class
7950  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7951  * @cfg {String} bgcolor Specifies the background color for a table
7952  * @cfg {Number} border Specifies whether the table cells should have borders or not
7953  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7954  * @cfg {Number} cellspacing Specifies the space between cells
7955  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7956  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7957  * @cfg {String} sortable Specifies that the table should be sortable
7958  * @cfg {String} summary Specifies a summary of the content of a table
7959  * @cfg {Number} width Specifies the width of a table
7960  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7961  * 
7962  * @cfg {boolean} striped Should the rows be alternative striped
7963  * @cfg {boolean} bordered Add borders to the table
7964  * @cfg {boolean} hover Add hover highlighting
7965  * @cfg {boolean} condensed Format condensed
7966  * @cfg {boolean} responsive Format condensed
7967  * @cfg {Boolean} loadMask (true|false) default false
7968  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7969  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7970  * @cfg {Boolean} rowSelection (true|false) default false
7971  * @cfg {Boolean} cellSelection (true|false) default false
7972  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7973  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7974  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7975  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7976  
7977  * 
7978  * @constructor
7979  * Create a new Table
7980  * @param {Object} config The config object
7981  */
7982
7983 Roo.bootstrap.Table = function(config){
7984     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7985     
7986   
7987     
7988     // BC...
7989     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7990     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7991     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7992     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7993     
7994     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7995     if (this.sm) {
7996         this.sm.grid = this;
7997         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7998         this.sm = this.selModel;
7999         this.sm.xmodule = this.xmodule || false;
8000     }
8001     
8002     if (this.cm && typeof(this.cm.config) == 'undefined') {
8003         this.colModel = new Roo.grid.ColumnModel(this.cm);
8004         this.cm = this.colModel;
8005         this.cm.xmodule = this.xmodule || false;
8006     }
8007     if (this.store) {
8008         this.store= Roo.factory(this.store, Roo.data);
8009         this.ds = this.store;
8010         this.ds.xmodule = this.xmodule || false;
8011          
8012     }
8013     if (this.footer && this.store) {
8014         this.footer.dataSource = this.ds;
8015         this.footer = Roo.factory(this.footer);
8016     }
8017     
8018     /** @private */
8019     this.addEvents({
8020         /**
8021          * @event cellclick
8022          * Fires when a cell is clicked
8023          * @param {Roo.bootstrap.Table} this
8024          * @param {Roo.Element} el
8025          * @param {Number} rowIndex
8026          * @param {Number} columnIndex
8027          * @param {Roo.EventObject} e
8028          */
8029         "cellclick" : true,
8030         /**
8031          * @event celldblclick
8032          * Fires when a cell is double clicked
8033          * @param {Roo.bootstrap.Table} this
8034          * @param {Roo.Element} el
8035          * @param {Number} rowIndex
8036          * @param {Number} columnIndex
8037          * @param {Roo.EventObject} e
8038          */
8039         "celldblclick" : true,
8040         /**
8041          * @event rowclick
8042          * Fires when a row is clicked
8043          * @param {Roo.bootstrap.Table} this
8044          * @param {Roo.Element} el
8045          * @param {Number} rowIndex
8046          * @param {Roo.EventObject} e
8047          */
8048         "rowclick" : true,
8049         /**
8050          * @event rowdblclick
8051          * Fires when a row is double clicked
8052          * @param {Roo.bootstrap.Table} this
8053          * @param {Roo.Element} el
8054          * @param {Number} rowIndex
8055          * @param {Roo.EventObject} e
8056          */
8057         "rowdblclick" : true,
8058         /**
8059          * @event mouseover
8060          * Fires when a mouseover occur
8061          * @param {Roo.bootstrap.Table} this
8062          * @param {Roo.Element} el
8063          * @param {Number} rowIndex
8064          * @param {Number} columnIndex
8065          * @param {Roo.EventObject} e
8066          */
8067         "mouseover" : true,
8068         /**
8069          * @event mouseout
8070          * Fires when a mouseout occur
8071          * @param {Roo.bootstrap.Table} this
8072          * @param {Roo.Element} el
8073          * @param {Number} rowIndex
8074          * @param {Number} columnIndex
8075          * @param {Roo.EventObject} e
8076          */
8077         "mouseout" : true,
8078         /**
8079          * @event rowclass
8080          * Fires when a row is rendered, so you can change add a style to it.
8081          * @param {Roo.bootstrap.Table} this
8082          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8083          */
8084         'rowclass' : true,
8085           /**
8086          * @event rowsrendered
8087          * Fires when all the  rows have been rendered
8088          * @param {Roo.bootstrap.Table} this
8089          */
8090         'rowsrendered' : true,
8091         /**
8092          * @event contextmenu
8093          * The raw contextmenu event for the entire grid.
8094          * @param {Roo.EventObject} e
8095          */
8096         "contextmenu" : true,
8097         /**
8098          * @event rowcontextmenu
8099          * Fires when a row is right clicked
8100          * @param {Roo.bootstrap.Table} this
8101          * @param {Number} rowIndex
8102          * @param {Roo.EventObject} e
8103          */
8104         "rowcontextmenu" : true,
8105         /**
8106          * @event cellcontextmenu
8107          * Fires when a cell is right clicked
8108          * @param {Roo.bootstrap.Table} this
8109          * @param {Number} rowIndex
8110          * @param {Number} cellIndex
8111          * @param {Roo.EventObject} e
8112          */
8113          "cellcontextmenu" : true,
8114          /**
8115          * @event headercontextmenu
8116          * Fires when a header is right clicked
8117          * @param {Roo.bootstrap.Table} this
8118          * @param {Number} columnIndex
8119          * @param {Roo.EventObject} e
8120          */
8121         "headercontextmenu" : true
8122     });
8123 };
8124
8125 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8126     
8127     cls: false,
8128     align: false,
8129     bgcolor: false,
8130     border: false,
8131     cellpadding: false,
8132     cellspacing: false,
8133     frame: false,
8134     rules: false,
8135     sortable: false,
8136     summary: false,
8137     width: false,
8138     striped : false,
8139     scrollBody : false,
8140     bordered: false,
8141     hover:  false,
8142     condensed : false,
8143     responsive : false,
8144     sm : false,
8145     cm : false,
8146     store : false,
8147     loadMask : false,
8148     footerShow : true,
8149     headerShow : true,
8150   
8151     rowSelection : false,
8152     cellSelection : false,
8153     layout : false,
8154     
8155     // Roo.Element - the tbody
8156     mainBody: false,
8157     // Roo.Element - thead element
8158     mainHead: false,
8159     
8160     container: false, // used by gridpanel...
8161     
8162     lazyLoad : false,
8163     
8164     CSS : Roo.util.CSS,
8165     
8166     auto_hide_footer : false,
8167     
8168     getAutoCreate : function()
8169     {
8170         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8171         
8172         cfg = {
8173             tag: 'table',
8174             cls : 'table',
8175             cn : []
8176         };
8177         if (this.scrollBody) {
8178             cfg.cls += ' table-body-fixed';
8179         }    
8180         if (this.striped) {
8181             cfg.cls += ' table-striped';
8182         }
8183         
8184         if (this.hover) {
8185             cfg.cls += ' table-hover';
8186         }
8187         if (this.bordered) {
8188             cfg.cls += ' table-bordered';
8189         }
8190         if (this.condensed) {
8191             cfg.cls += ' table-condensed';
8192         }
8193         if (this.responsive) {
8194             cfg.cls += ' table-responsive';
8195         }
8196         
8197         if (this.cls) {
8198             cfg.cls+=  ' ' +this.cls;
8199         }
8200         
8201         // this lot should be simplifed...
8202         var _t = this;
8203         var cp = [
8204             'align',
8205             'bgcolor',
8206             'border',
8207             'cellpadding',
8208             'cellspacing',
8209             'frame',
8210             'rules',
8211             'sortable',
8212             'summary',
8213             'width'
8214         ].forEach(function(k) {
8215             if (_t[k]) {
8216                 cfg[k] = _t[k];
8217             }
8218         });
8219         
8220         
8221         if (this.layout) {
8222             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8223         }
8224         
8225         if(this.store || this.cm){
8226             if(this.headerShow){
8227                 cfg.cn.push(this.renderHeader());
8228             }
8229             
8230             cfg.cn.push(this.renderBody());
8231             
8232             if(this.footerShow){
8233                 cfg.cn.push(this.renderFooter());
8234             }
8235             // where does this come from?
8236             //cfg.cls+=  ' TableGrid';
8237         }
8238         
8239         return { cn : [ cfg ] };
8240     },
8241     
8242     initEvents : function()
8243     {   
8244         if(!this.store || !this.cm){
8245             return;
8246         }
8247         if (this.selModel) {
8248             this.selModel.initEvents();
8249         }
8250         
8251         
8252         //Roo.log('initEvents with ds!!!!');
8253         
8254         this.mainBody = this.el.select('tbody', true).first();
8255         this.mainHead = this.el.select('thead', true).first();
8256         this.mainFoot = this.el.select('tfoot', true).first();
8257         
8258         
8259         
8260         var _this = this;
8261         
8262         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8263             e.on('click', _this.sort, _this);
8264         });
8265         
8266         this.mainBody.on("click", this.onClick, this);
8267         this.mainBody.on("dblclick", this.onDblClick, this);
8268         
8269         // why is this done????? = it breaks dialogs??
8270         //this.parent().el.setStyle('position', 'relative');
8271         
8272         
8273         if (this.footer) {
8274             this.footer.parentId = this.id;
8275             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8276             
8277             if(this.lazyLoad){
8278                 this.el.select('tfoot tr td').first().addClass('hide');
8279             }
8280         } 
8281         
8282         if(this.loadMask) {
8283             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8284         }
8285         
8286         this.store.on('load', this.onLoad, this);
8287         this.store.on('beforeload', this.onBeforeLoad, this);
8288         this.store.on('update', this.onUpdate, this);
8289         this.store.on('add', this.onAdd, this);
8290         this.store.on("clear", this.clear, this);
8291         
8292         this.el.on("contextmenu", this.onContextMenu, this);
8293         
8294         this.mainBody.on('scroll', this.onBodyScroll, this);
8295         
8296         this.cm.on("headerchange", this.onHeaderChange, this);
8297         
8298         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8299         
8300     },
8301     
8302     onContextMenu : function(e, t)
8303     {
8304         this.processEvent("contextmenu", e);
8305     },
8306     
8307     processEvent : function(name, e)
8308     {
8309         if (name != 'touchstart' ) {
8310             this.fireEvent(name, e);    
8311         }
8312         
8313         var t = e.getTarget();
8314         
8315         var cell = Roo.get(t);
8316         
8317         if(!cell){
8318             return;
8319         }
8320         
8321         if(cell.findParent('tfoot', false, true)){
8322             return;
8323         }
8324         
8325         if(cell.findParent('thead', false, true)){
8326             
8327             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8328                 cell = Roo.get(t).findParent('th', false, true);
8329                 if (!cell) {
8330                     Roo.log("failed to find th in thead?");
8331                     Roo.log(e.getTarget());
8332                     return;
8333                 }
8334             }
8335             
8336             var cellIndex = cell.dom.cellIndex;
8337             
8338             var ename = name == 'touchstart' ? 'click' : name;
8339             this.fireEvent("header" + ename, this, cellIndex, e);
8340             
8341             return;
8342         }
8343         
8344         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8345             cell = Roo.get(t).findParent('td', false, true);
8346             if (!cell) {
8347                 Roo.log("failed to find th in tbody?");
8348                 Roo.log(e.getTarget());
8349                 return;
8350             }
8351         }
8352         
8353         var row = cell.findParent('tr', false, true);
8354         var cellIndex = cell.dom.cellIndex;
8355         var rowIndex = row.dom.rowIndex - 1;
8356         
8357         if(row !== false){
8358             
8359             this.fireEvent("row" + name, this, rowIndex, e);
8360             
8361             if(cell !== false){
8362             
8363                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8364             }
8365         }
8366         
8367     },
8368     
8369     onMouseover : function(e, el)
8370     {
8371         var cell = Roo.get(el);
8372         
8373         if(!cell){
8374             return;
8375         }
8376         
8377         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8378             cell = cell.findParent('td', false, true);
8379         }
8380         
8381         var row = cell.findParent('tr', false, true);
8382         var cellIndex = cell.dom.cellIndex;
8383         var rowIndex = row.dom.rowIndex - 1; // start from 0
8384         
8385         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8386         
8387     },
8388     
8389     onMouseout : function(e, el)
8390     {
8391         var cell = Roo.get(el);
8392         
8393         if(!cell){
8394             return;
8395         }
8396         
8397         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8398             cell = cell.findParent('td', false, true);
8399         }
8400         
8401         var row = cell.findParent('tr', false, true);
8402         var cellIndex = cell.dom.cellIndex;
8403         var rowIndex = row.dom.rowIndex - 1; // start from 0
8404         
8405         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8406         
8407     },
8408     
8409     onClick : function(e, el)
8410     {
8411         var cell = Roo.get(el);
8412         
8413         if(!cell || (!this.cellSelection && !this.rowSelection)){
8414             return;
8415         }
8416         
8417         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8418             cell = cell.findParent('td', false, true);
8419         }
8420         
8421         if(!cell || typeof(cell) == 'undefined'){
8422             return;
8423         }
8424         
8425         var row = cell.findParent('tr', false, true);
8426         
8427         if(!row || typeof(row) == 'undefined'){
8428             return;
8429         }
8430         
8431         var cellIndex = cell.dom.cellIndex;
8432         var rowIndex = this.getRowIndex(row);
8433         
8434         // why??? - should these not be based on SelectionModel?
8435         if(this.cellSelection){
8436             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8437         }
8438         
8439         if(this.rowSelection){
8440             this.fireEvent('rowclick', this, row, rowIndex, e);
8441         }
8442         
8443         
8444     },
8445         
8446     onDblClick : function(e,el)
8447     {
8448         var cell = Roo.get(el);
8449         
8450         if(!cell || (!this.cellSelection && !this.rowSelection)){
8451             return;
8452         }
8453         
8454         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8455             cell = cell.findParent('td', false, true);
8456         }
8457         
8458         if(!cell || typeof(cell) == 'undefined'){
8459             return;
8460         }
8461         
8462         var row = cell.findParent('tr', false, true);
8463         
8464         if(!row || typeof(row) == 'undefined'){
8465             return;
8466         }
8467         
8468         var cellIndex = cell.dom.cellIndex;
8469         var rowIndex = this.getRowIndex(row);
8470         
8471         if(this.cellSelection){
8472             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8473         }
8474         
8475         if(this.rowSelection){
8476             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8477         }
8478     },
8479     
8480     sort : function(e,el)
8481     {
8482         var col = Roo.get(el);
8483         
8484         if(!col.hasClass('sortable')){
8485             return;
8486         }
8487         
8488         var sort = col.attr('sort');
8489         var dir = 'ASC';
8490         
8491         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8492             dir = 'DESC';
8493         }
8494         
8495         this.store.sortInfo = {field : sort, direction : dir};
8496         
8497         if (this.footer) {
8498             Roo.log("calling footer first");
8499             this.footer.onClick('first');
8500         } else {
8501         
8502             this.store.load({ params : { start : 0 } });
8503         }
8504     },
8505     
8506     renderHeader : function()
8507     {
8508         var header = {
8509             tag: 'thead',
8510             cn : []
8511         };
8512         
8513         var cm = this.cm;
8514         this.totalWidth = 0;
8515         
8516         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8517             
8518             var config = cm.config[i];
8519             
8520             var c = {
8521                 tag: 'th',
8522                 cls : 'x-hcol-' + i,
8523                 style : '',
8524                 html: cm.getColumnHeader(i)
8525             };
8526             
8527             var hh = '';
8528             
8529             if(typeof(config.sortable) != 'undefined' && config.sortable){
8530                 c.cls = 'sortable';
8531                 c.html = '<i class="glyphicon"></i>' + c.html;
8532             }
8533             
8534             // could use BS4 hidden-..-down 
8535             
8536             if(typeof(config.lgHeader) != 'undefined'){
8537                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8538             }
8539             
8540             if(typeof(config.mdHeader) != 'undefined'){
8541                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8542             }
8543             
8544             if(typeof(config.smHeader) != 'undefined'){
8545                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8546             }
8547             
8548             if(typeof(config.xsHeader) != 'undefined'){
8549                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8550             }
8551             
8552             if(hh.length){
8553                 c.html = hh;
8554             }
8555             
8556             if(typeof(config.tooltip) != 'undefined'){
8557                 c.tooltip = config.tooltip;
8558             }
8559             
8560             if(typeof(config.colspan) != 'undefined'){
8561                 c.colspan = config.colspan;
8562             }
8563             
8564             if(typeof(config.hidden) != 'undefined' && config.hidden){
8565                 c.style += ' display:none;';
8566             }
8567             
8568             if(typeof(config.dataIndex) != 'undefined'){
8569                 c.sort = config.dataIndex;
8570             }
8571             
8572            
8573             
8574             if(typeof(config.align) != 'undefined' && config.align.length){
8575                 c.style += ' text-align:' + config.align + ';';
8576             }
8577             
8578             if(typeof(config.width) != 'undefined'){
8579                 c.style += ' width:' + config.width + 'px;';
8580                 this.totalWidth += config.width;
8581             } else {
8582                 this.totalWidth += 100; // assume minimum of 100 per column?
8583             }
8584             
8585             if(typeof(config.cls) != 'undefined'){
8586                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8587             }
8588             
8589             ['xs','sm','md','lg'].map(function(size){
8590                 
8591                 if(typeof(config[size]) == 'undefined'){
8592                     return;
8593                 }
8594                  
8595                 if (!config[size]) { // 0 = hidden
8596                     // BS 4 '0' is treated as hide that column and below.
8597                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8598                     return;
8599                 }
8600                 
8601                 c.cls += ' col-' + size + '-' + config[size] + (
8602                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8603                 );
8604                 
8605                 
8606             });
8607             
8608             header.cn.push(c)
8609         }
8610         
8611         return header;
8612     },
8613     
8614     renderBody : function()
8615     {
8616         var body = {
8617             tag: 'tbody',
8618             cn : [
8619                 {
8620                     tag: 'tr',
8621                     cn : [
8622                         {
8623                             tag : 'td',
8624                             colspan :  this.cm.getColumnCount()
8625                         }
8626                     ]
8627                 }
8628             ]
8629         };
8630         
8631         return body;
8632     },
8633     
8634     renderFooter : function()
8635     {
8636         var footer = {
8637             tag: 'tfoot',
8638             cn : [
8639                 {
8640                     tag: 'tr',
8641                     cn : [
8642                         {
8643                             tag : 'td',
8644                             colspan :  this.cm.getColumnCount()
8645                         }
8646                     ]
8647                 }
8648             ]
8649         };
8650         
8651         return footer;
8652     },
8653     
8654     
8655     
8656     onLoad : function()
8657     {
8658 //        Roo.log('ds onload');
8659         this.clear();
8660         
8661         var _this = this;
8662         var cm = this.cm;
8663         var ds = this.store;
8664         
8665         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8666             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8667             if (_this.store.sortInfo) {
8668                     
8669                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8670                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8671                 }
8672                 
8673                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8674                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8675                 }
8676             }
8677         });
8678         
8679         var tbody =  this.mainBody;
8680               
8681         if(ds.getCount() > 0){
8682             ds.data.each(function(d,rowIndex){
8683                 var row =  this.renderRow(cm, ds, rowIndex);
8684                 
8685                 tbody.createChild(row);
8686                 
8687                 var _this = this;
8688                 
8689                 if(row.cellObjects.length){
8690                     Roo.each(row.cellObjects, function(r){
8691                         _this.renderCellObject(r);
8692                     })
8693                 }
8694                 
8695             }, this);
8696         }
8697         
8698         var tfoot = this.el.select('tfoot', true).first();
8699         
8700         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8701             
8702             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8703             
8704             var total = this.ds.getTotalCount();
8705             
8706             if(this.footer.pageSize < total){
8707                 this.mainFoot.show();
8708             }
8709         }
8710         
8711         Roo.each(this.el.select('tbody td', true).elements, function(e){
8712             e.on('mouseover', _this.onMouseover, _this);
8713         });
8714         
8715         Roo.each(this.el.select('tbody td', true).elements, function(e){
8716             e.on('mouseout', _this.onMouseout, _this);
8717         });
8718         this.fireEvent('rowsrendered', this);
8719         
8720         this.autoSize();
8721     },
8722     
8723     
8724     onUpdate : function(ds,record)
8725     {
8726         this.refreshRow(record);
8727         this.autoSize();
8728     },
8729     
8730     onRemove : function(ds, record, index, isUpdate){
8731         if(isUpdate !== true){
8732             this.fireEvent("beforerowremoved", this, index, record);
8733         }
8734         var bt = this.mainBody.dom;
8735         
8736         var rows = this.el.select('tbody > tr', true).elements;
8737         
8738         if(typeof(rows[index]) != 'undefined'){
8739             bt.removeChild(rows[index].dom);
8740         }
8741         
8742 //        if(bt.rows[index]){
8743 //            bt.removeChild(bt.rows[index]);
8744 //        }
8745         
8746         if(isUpdate !== true){
8747             //this.stripeRows(index);
8748             //this.syncRowHeights(index, index);
8749             //this.layout();
8750             this.fireEvent("rowremoved", this, index, record);
8751         }
8752     },
8753     
8754     onAdd : function(ds, records, rowIndex)
8755     {
8756         //Roo.log('on Add called');
8757         // - note this does not handle multiple adding very well..
8758         var bt = this.mainBody.dom;
8759         for (var i =0 ; i < records.length;i++) {
8760             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8761             //Roo.log(records[i]);
8762             //Roo.log(this.store.getAt(rowIndex+i));
8763             this.insertRow(this.store, rowIndex + i, false);
8764             return;
8765         }
8766         
8767     },
8768     
8769     
8770     refreshRow : function(record){
8771         var ds = this.store, index;
8772         if(typeof record == 'number'){
8773             index = record;
8774             record = ds.getAt(index);
8775         }else{
8776             index = ds.indexOf(record);
8777             if (index < 0) {
8778                 return; // should not happen - but seems to 
8779             }
8780         }
8781         this.insertRow(ds, index, true);
8782         this.autoSize();
8783         this.onRemove(ds, record, index+1, true);
8784         this.autoSize();
8785         //this.syncRowHeights(index, index);
8786         //this.layout();
8787         this.fireEvent("rowupdated", this, index, record);
8788     },
8789     
8790     insertRow : function(dm, rowIndex, isUpdate){
8791         
8792         if(!isUpdate){
8793             this.fireEvent("beforerowsinserted", this, rowIndex);
8794         }
8795             //var s = this.getScrollState();
8796         var row = this.renderRow(this.cm, this.store, rowIndex);
8797         // insert before rowIndex..
8798         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8799         
8800         var _this = this;
8801                 
8802         if(row.cellObjects.length){
8803             Roo.each(row.cellObjects, function(r){
8804                 _this.renderCellObject(r);
8805             })
8806         }
8807             
8808         if(!isUpdate){
8809             this.fireEvent("rowsinserted", this, rowIndex);
8810             //this.syncRowHeights(firstRow, lastRow);
8811             //this.stripeRows(firstRow);
8812             //this.layout();
8813         }
8814         
8815     },
8816     
8817     
8818     getRowDom : function(rowIndex)
8819     {
8820         var rows = this.el.select('tbody > tr', true).elements;
8821         
8822         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8823         
8824     },
8825     // returns the object tree for a tr..
8826   
8827     
8828     renderRow : function(cm, ds, rowIndex) 
8829     {
8830         var d = ds.getAt(rowIndex);
8831         
8832         var row = {
8833             tag : 'tr',
8834             cls : 'x-row-' + rowIndex,
8835             cn : []
8836         };
8837             
8838         var cellObjects = [];
8839         
8840         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8841             var config = cm.config[i];
8842             
8843             var renderer = cm.getRenderer(i);
8844             var value = '';
8845             var id = false;
8846             
8847             if(typeof(renderer) !== 'undefined'){
8848                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8849             }
8850             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8851             // and are rendered into the cells after the row is rendered - using the id for the element.
8852             
8853             if(typeof(value) === 'object'){
8854                 id = Roo.id();
8855                 cellObjects.push({
8856                     container : id,
8857                     cfg : value 
8858                 })
8859             }
8860             
8861             var rowcfg = {
8862                 record: d,
8863                 rowIndex : rowIndex,
8864                 colIndex : i,
8865                 rowClass : ''
8866             };
8867
8868             this.fireEvent('rowclass', this, rowcfg);
8869             
8870             var td = {
8871                 tag: 'td',
8872                 cls : rowcfg.rowClass + ' x-col-' + i,
8873                 style: '',
8874                 html: (typeof(value) === 'object') ? '' : value
8875             };
8876             
8877             if (id) {
8878                 td.id = id;
8879             }
8880             
8881             if(typeof(config.colspan) != 'undefined'){
8882                 td.colspan = config.colspan;
8883             }
8884             
8885             if(typeof(config.hidden) != 'undefined' && config.hidden){
8886                 td.style += ' display:none;';
8887             }
8888             
8889             if(typeof(config.align) != 'undefined' && config.align.length){
8890                 td.style += ' text-align:' + config.align + ';';
8891             }
8892             if(typeof(config.valign) != 'undefined' && config.valign.length){
8893                 td.style += ' vertical-align:' + config.valign + ';';
8894             }
8895             
8896             if(typeof(config.width) != 'undefined'){
8897                 td.style += ' width:' +  config.width + 'px;';
8898             }
8899             
8900             if(typeof(config.cursor) != 'undefined'){
8901                 td.style += ' cursor:' +  config.cursor + ';';
8902             }
8903             
8904             if(typeof(config.cls) != 'undefined'){
8905                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8906             }
8907             
8908             ['xs','sm','md','lg'].map(function(size){
8909                 
8910                 if(typeof(config[size]) == 'undefined'){
8911                     return;
8912                 }
8913                 
8914                 
8915                   
8916                 if (!config[size]) { // 0 = hidden
8917                     // BS 4 '0' is treated as hide that column and below.
8918                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8919                     return;
8920                 }
8921                 
8922                 td.cls += ' col-' + size + '-' + config[size] + (
8923                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8924                 );
8925                  
8926
8927             });
8928             
8929             row.cn.push(td);
8930            
8931         }
8932         
8933         row.cellObjects = cellObjects;
8934         
8935         return row;
8936           
8937     },
8938     
8939     
8940     
8941     onBeforeLoad : function()
8942     {
8943         
8944     },
8945      /**
8946      * Remove all rows
8947      */
8948     clear : function()
8949     {
8950         this.el.select('tbody', true).first().dom.innerHTML = '';
8951     },
8952     /**
8953      * Show or hide a row.
8954      * @param {Number} rowIndex to show or hide
8955      * @param {Boolean} state hide
8956      */
8957     setRowVisibility : function(rowIndex, state)
8958     {
8959         var bt = this.mainBody.dom;
8960         
8961         var rows = this.el.select('tbody > tr', true).elements;
8962         
8963         if(typeof(rows[rowIndex]) == 'undefined'){
8964             return;
8965         }
8966         rows[rowIndex].dom.style.display = state ? '' : 'none';
8967     },
8968     
8969     
8970     getSelectionModel : function(){
8971         if(!this.selModel){
8972             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8973         }
8974         return this.selModel;
8975     },
8976     /*
8977      * Render the Roo.bootstrap object from renderder
8978      */
8979     renderCellObject : function(r)
8980     {
8981         var _this = this;
8982         
8983         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8984         
8985         var t = r.cfg.render(r.container);
8986         
8987         if(r.cfg.cn){
8988             Roo.each(r.cfg.cn, function(c){
8989                 var child = {
8990                     container: t.getChildContainer(),
8991                     cfg: c
8992                 };
8993                 _this.renderCellObject(child);
8994             })
8995         }
8996     },
8997     
8998     getRowIndex : function(row)
8999     {
9000         var rowIndex = -1;
9001         
9002         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9003             if(el != row){
9004                 return;
9005             }
9006             
9007             rowIndex = index;
9008         });
9009         
9010         return rowIndex;
9011     },
9012      /**
9013      * Returns the grid's underlying element = used by panel.Grid
9014      * @return {Element} The element
9015      */
9016     getGridEl : function(){
9017         return this.el;
9018     },
9019      /**
9020      * Forces a resize - used by panel.Grid
9021      * @return {Element} The element
9022      */
9023     autoSize : function()
9024     {
9025         //var ctr = Roo.get(this.container.dom.parentElement);
9026         var ctr = Roo.get(this.el.dom);
9027         
9028         var thd = this.getGridEl().select('thead',true).first();
9029         var tbd = this.getGridEl().select('tbody', true).first();
9030         var tfd = this.getGridEl().select('tfoot', true).first();
9031         
9032         var cw = ctr.getWidth();
9033         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9034         
9035         if (tbd) {
9036             
9037             tbd.setWidth(ctr.getWidth());
9038             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9039             // this needs fixing for various usage - currently only hydra job advers I think..
9040             //tdb.setHeight(
9041             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9042             //); 
9043             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9044             cw -= barsize;
9045         }
9046         cw = Math.max(cw, this.totalWidth);
9047         this.getGridEl().select('tbody tr',true).setWidth(cw);
9048         
9049         // resize 'expandable coloumn?
9050         
9051         return; // we doe not have a view in this design..
9052         
9053     },
9054     onBodyScroll: function()
9055     {
9056         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9057         if(this.mainHead){
9058             this.mainHead.setStyle({
9059                 'position' : 'relative',
9060                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9061             });
9062         }
9063         
9064         if(this.lazyLoad){
9065             
9066             var scrollHeight = this.mainBody.dom.scrollHeight;
9067             
9068             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9069             
9070             var height = this.mainBody.getHeight();
9071             
9072             if(scrollHeight - height == scrollTop) {
9073                 
9074                 var total = this.ds.getTotalCount();
9075                 
9076                 if(this.footer.cursor + this.footer.pageSize < total){
9077                     
9078                     this.footer.ds.load({
9079                         params : {
9080                             start : this.footer.cursor + this.footer.pageSize,
9081                             limit : this.footer.pageSize
9082                         },
9083                         add : true
9084                     });
9085                 }
9086             }
9087             
9088         }
9089     },
9090     
9091     onHeaderChange : function()
9092     {
9093         var header = this.renderHeader();
9094         var table = this.el.select('table', true).first();
9095         
9096         this.mainHead.remove();
9097         this.mainHead = table.createChild(header, this.mainBody, false);
9098     },
9099     
9100     onHiddenChange : function(colModel, colIndex, hidden)
9101     {
9102         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9103         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9104         
9105         this.CSS.updateRule(thSelector, "display", "");
9106         this.CSS.updateRule(tdSelector, "display", "");
9107         
9108         if(hidden){
9109             this.CSS.updateRule(thSelector, "display", "none");
9110             this.CSS.updateRule(tdSelector, "display", "none");
9111         }
9112         
9113         this.onHeaderChange();
9114         this.onLoad();
9115     },
9116     
9117     setColumnWidth: function(col_index, width)
9118     {
9119         // width = "md-2 xs-2..."
9120         if(!this.colModel.config[col_index]) {
9121             return;
9122         }
9123         
9124         var w = width.split(" ");
9125         
9126         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9127         
9128         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9129         
9130         
9131         for(var j = 0; j < w.length; j++) {
9132             
9133             if(!w[j]) {
9134                 continue;
9135             }
9136             
9137             var size_cls = w[j].split("-");
9138             
9139             if(!Number.isInteger(size_cls[1] * 1)) {
9140                 continue;
9141             }
9142             
9143             if(!this.colModel.config[col_index][size_cls[0]]) {
9144                 continue;
9145             }
9146             
9147             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9148                 continue;
9149             }
9150             
9151             h_row[0].classList.replace(
9152                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9153                 "col-"+size_cls[0]+"-"+size_cls[1]
9154             );
9155             
9156             for(var i = 0; i < rows.length; i++) {
9157                 
9158                 var size_cls = w[j].split("-");
9159                 
9160                 if(!Number.isInteger(size_cls[1] * 1)) {
9161                     continue;
9162                 }
9163                 
9164                 if(!this.colModel.config[col_index][size_cls[0]]) {
9165                     continue;
9166                 }
9167                 
9168                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9169                     continue;
9170                 }
9171                 
9172                 rows[i].classList.replace(
9173                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9174                     "col-"+size_cls[0]+"-"+size_cls[1]
9175                 );
9176             }
9177             
9178             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9179         }
9180     }
9181 });
9182
9183  
9184
9185  /*
9186  * - LGPL
9187  *
9188  * table cell
9189  * 
9190  */
9191
9192 /**
9193  * @class Roo.bootstrap.TableCell
9194  * @extends Roo.bootstrap.Component
9195  * Bootstrap TableCell class
9196  * @cfg {String} html cell contain text
9197  * @cfg {String} cls cell class
9198  * @cfg {String} tag cell tag (td|th) default td
9199  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9200  * @cfg {String} align Aligns the content in a cell
9201  * @cfg {String} axis Categorizes cells
9202  * @cfg {String} bgcolor Specifies the background color of a cell
9203  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9204  * @cfg {Number} colspan Specifies the number of columns a cell should span
9205  * @cfg {String} headers Specifies one or more header cells a cell is related to
9206  * @cfg {Number} height Sets the height of a cell
9207  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9208  * @cfg {Number} rowspan Sets the number of rows a cell should span
9209  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9210  * @cfg {String} valign Vertical aligns the content in a cell
9211  * @cfg {Number} width Specifies the width of a cell
9212  * 
9213  * @constructor
9214  * Create a new TableCell
9215  * @param {Object} config The config object
9216  */
9217
9218 Roo.bootstrap.TableCell = function(config){
9219     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9220 };
9221
9222 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9223     
9224     html: false,
9225     cls: false,
9226     tag: false,
9227     abbr: false,
9228     align: false,
9229     axis: false,
9230     bgcolor: false,
9231     charoff: false,
9232     colspan: false,
9233     headers: false,
9234     height: false,
9235     nowrap: false,
9236     rowspan: false,
9237     scope: false,
9238     valign: false,
9239     width: false,
9240     
9241     
9242     getAutoCreate : function(){
9243         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9244         
9245         cfg = {
9246             tag: 'td'
9247         };
9248         
9249         if(this.tag){
9250             cfg.tag = this.tag;
9251         }
9252         
9253         if (this.html) {
9254             cfg.html=this.html
9255         }
9256         if (this.cls) {
9257             cfg.cls=this.cls
9258         }
9259         if (this.abbr) {
9260             cfg.abbr=this.abbr
9261         }
9262         if (this.align) {
9263             cfg.align=this.align
9264         }
9265         if (this.axis) {
9266             cfg.axis=this.axis
9267         }
9268         if (this.bgcolor) {
9269             cfg.bgcolor=this.bgcolor
9270         }
9271         if (this.charoff) {
9272             cfg.charoff=this.charoff
9273         }
9274         if (this.colspan) {
9275             cfg.colspan=this.colspan
9276         }
9277         if (this.headers) {
9278             cfg.headers=this.headers
9279         }
9280         if (this.height) {
9281             cfg.height=this.height
9282         }
9283         if (this.nowrap) {
9284             cfg.nowrap=this.nowrap
9285         }
9286         if (this.rowspan) {
9287             cfg.rowspan=this.rowspan
9288         }
9289         if (this.scope) {
9290             cfg.scope=this.scope
9291         }
9292         if (this.valign) {
9293             cfg.valign=this.valign
9294         }
9295         if (this.width) {
9296             cfg.width=this.width
9297         }
9298         
9299         
9300         return cfg;
9301     }
9302    
9303 });
9304
9305  
9306
9307  /*
9308  * - LGPL
9309  *
9310  * table row
9311  * 
9312  */
9313
9314 /**
9315  * @class Roo.bootstrap.TableRow
9316  * @extends Roo.bootstrap.Component
9317  * Bootstrap TableRow class
9318  * @cfg {String} cls row class
9319  * @cfg {String} align Aligns the content in a table row
9320  * @cfg {String} bgcolor Specifies a background color for a table row
9321  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9322  * @cfg {String} valign Vertical aligns the content in a table row
9323  * 
9324  * @constructor
9325  * Create a new TableRow
9326  * @param {Object} config The config object
9327  */
9328
9329 Roo.bootstrap.TableRow = function(config){
9330     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9331 };
9332
9333 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9334     
9335     cls: false,
9336     align: false,
9337     bgcolor: false,
9338     charoff: false,
9339     valign: false,
9340     
9341     getAutoCreate : function(){
9342         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9343         
9344         cfg = {
9345             tag: 'tr'
9346         };
9347             
9348         if(this.cls){
9349             cfg.cls = this.cls;
9350         }
9351         if(this.align){
9352             cfg.align = this.align;
9353         }
9354         if(this.bgcolor){
9355             cfg.bgcolor = this.bgcolor;
9356         }
9357         if(this.charoff){
9358             cfg.charoff = this.charoff;
9359         }
9360         if(this.valign){
9361             cfg.valign = this.valign;
9362         }
9363         
9364         return cfg;
9365     }
9366    
9367 });
9368
9369  
9370
9371  /*
9372  * - LGPL
9373  *
9374  * table body
9375  * 
9376  */
9377
9378 /**
9379  * @class Roo.bootstrap.TableBody
9380  * @extends Roo.bootstrap.Component
9381  * Bootstrap TableBody class
9382  * @cfg {String} cls element class
9383  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9384  * @cfg {String} align Aligns the content inside the element
9385  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9386  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9387  * 
9388  * @constructor
9389  * Create a new TableBody
9390  * @param {Object} config The config object
9391  */
9392
9393 Roo.bootstrap.TableBody = function(config){
9394     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9395 };
9396
9397 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9398     
9399     cls: false,
9400     tag: false,
9401     align: false,
9402     charoff: false,
9403     valign: false,
9404     
9405     getAutoCreate : function(){
9406         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9407         
9408         cfg = {
9409             tag: 'tbody'
9410         };
9411             
9412         if (this.cls) {
9413             cfg.cls=this.cls
9414         }
9415         if(this.tag){
9416             cfg.tag = this.tag;
9417         }
9418         
9419         if(this.align){
9420             cfg.align = this.align;
9421         }
9422         if(this.charoff){
9423             cfg.charoff = this.charoff;
9424         }
9425         if(this.valign){
9426             cfg.valign = this.valign;
9427         }
9428         
9429         return cfg;
9430     }
9431     
9432     
9433 //    initEvents : function()
9434 //    {
9435 //        
9436 //        if(!this.store){
9437 //            return;
9438 //        }
9439 //        
9440 //        this.store = Roo.factory(this.store, Roo.data);
9441 //        this.store.on('load', this.onLoad, this);
9442 //        
9443 //        this.store.load();
9444 //        
9445 //    },
9446 //    
9447 //    onLoad: function () 
9448 //    {   
9449 //        this.fireEvent('load', this);
9450 //    }
9451 //    
9452 //   
9453 });
9454
9455  
9456
9457  /*
9458  * Based on:
9459  * Ext JS Library 1.1.1
9460  * Copyright(c) 2006-2007, Ext JS, LLC.
9461  *
9462  * Originally Released Under LGPL - original licence link has changed is not relivant.
9463  *
9464  * Fork - LGPL
9465  * <script type="text/javascript">
9466  */
9467
9468 // as we use this in bootstrap.
9469 Roo.namespace('Roo.form');
9470  /**
9471  * @class Roo.form.Action
9472  * Internal Class used to handle form actions
9473  * @constructor
9474  * @param {Roo.form.BasicForm} el The form element or its id
9475  * @param {Object} config Configuration options
9476  */
9477
9478  
9479  
9480 // define the action interface
9481 Roo.form.Action = function(form, options){
9482     this.form = form;
9483     this.options = options || {};
9484 };
9485 /**
9486  * Client Validation Failed
9487  * @const 
9488  */
9489 Roo.form.Action.CLIENT_INVALID = 'client';
9490 /**
9491  * Server Validation Failed
9492  * @const 
9493  */
9494 Roo.form.Action.SERVER_INVALID = 'server';
9495  /**
9496  * Connect to Server Failed
9497  * @const 
9498  */
9499 Roo.form.Action.CONNECT_FAILURE = 'connect';
9500 /**
9501  * Reading Data from Server Failed
9502  * @const 
9503  */
9504 Roo.form.Action.LOAD_FAILURE = 'load';
9505
9506 Roo.form.Action.prototype = {
9507     type : 'default',
9508     failureType : undefined,
9509     response : undefined,
9510     result : undefined,
9511
9512     // interface method
9513     run : function(options){
9514
9515     },
9516
9517     // interface method
9518     success : function(response){
9519
9520     },
9521
9522     // interface method
9523     handleResponse : function(response){
9524
9525     },
9526
9527     // default connection failure
9528     failure : function(response){
9529         
9530         this.response = response;
9531         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9532         this.form.afterAction(this, false);
9533     },
9534
9535     processResponse : function(response){
9536         this.response = response;
9537         if(!response.responseText){
9538             return true;
9539         }
9540         this.result = this.handleResponse(response);
9541         return this.result;
9542     },
9543
9544     // utility functions used internally
9545     getUrl : function(appendParams){
9546         var url = this.options.url || this.form.url || this.form.el.dom.action;
9547         if(appendParams){
9548             var p = this.getParams();
9549             if(p){
9550                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9551             }
9552         }
9553         return url;
9554     },
9555
9556     getMethod : function(){
9557         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9558     },
9559
9560     getParams : function(){
9561         var bp = this.form.baseParams;
9562         var p = this.options.params;
9563         if(p){
9564             if(typeof p == "object"){
9565                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9566             }else if(typeof p == 'string' && bp){
9567                 p += '&' + Roo.urlEncode(bp);
9568             }
9569         }else if(bp){
9570             p = Roo.urlEncode(bp);
9571         }
9572         return p;
9573     },
9574
9575     createCallback : function(){
9576         return {
9577             success: this.success,
9578             failure: this.failure,
9579             scope: this,
9580             timeout: (this.form.timeout*1000),
9581             upload: this.form.fileUpload ? this.success : undefined
9582         };
9583     }
9584 };
9585
9586 Roo.form.Action.Submit = function(form, options){
9587     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9588 };
9589
9590 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9591     type : 'submit',
9592
9593     haveProgress : false,
9594     uploadComplete : false,
9595     
9596     // uploadProgress indicator.
9597     uploadProgress : function()
9598     {
9599         if (!this.form.progressUrl) {
9600             return;
9601         }
9602         
9603         if (!this.haveProgress) {
9604             Roo.MessageBox.progress("Uploading", "Uploading");
9605         }
9606         if (this.uploadComplete) {
9607            Roo.MessageBox.hide();
9608            return;
9609         }
9610         
9611         this.haveProgress = true;
9612    
9613         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9614         
9615         var c = new Roo.data.Connection();
9616         c.request({
9617             url : this.form.progressUrl,
9618             params: {
9619                 id : uid
9620             },
9621             method: 'GET',
9622             success : function(req){
9623                //console.log(data);
9624                 var rdata = false;
9625                 var edata;
9626                 try  {
9627                    rdata = Roo.decode(req.responseText)
9628                 } catch (e) {
9629                     Roo.log("Invalid data from server..");
9630                     Roo.log(edata);
9631                     return;
9632                 }
9633                 if (!rdata || !rdata.success) {
9634                     Roo.log(rdata);
9635                     Roo.MessageBox.alert(Roo.encode(rdata));
9636                     return;
9637                 }
9638                 var data = rdata.data;
9639                 
9640                 if (this.uploadComplete) {
9641                    Roo.MessageBox.hide();
9642                    return;
9643                 }
9644                    
9645                 if (data){
9646                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9647                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9648                     );
9649                 }
9650                 this.uploadProgress.defer(2000,this);
9651             },
9652        
9653             failure: function(data) {
9654                 Roo.log('progress url failed ');
9655                 Roo.log(data);
9656             },
9657             scope : this
9658         });
9659            
9660     },
9661     
9662     
9663     run : function()
9664     {
9665         // run get Values on the form, so it syncs any secondary forms.
9666         this.form.getValues();
9667         
9668         var o = this.options;
9669         var method = this.getMethod();
9670         var isPost = method == 'POST';
9671         if(o.clientValidation === false || this.form.isValid()){
9672             
9673             if (this.form.progressUrl) {
9674                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9675                     (new Date() * 1) + '' + Math.random());
9676                     
9677             } 
9678             
9679             
9680             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9681                 form:this.form.el.dom,
9682                 url:this.getUrl(!isPost),
9683                 method: method,
9684                 params:isPost ? this.getParams() : null,
9685                 isUpload: this.form.fileUpload,
9686                 formData : this.form.formData
9687             }));
9688             
9689             this.uploadProgress();
9690
9691         }else if (o.clientValidation !== false){ // client validation failed
9692             this.failureType = Roo.form.Action.CLIENT_INVALID;
9693             this.form.afterAction(this, false);
9694         }
9695     },
9696
9697     success : function(response)
9698     {
9699         this.uploadComplete= true;
9700         if (this.haveProgress) {
9701             Roo.MessageBox.hide();
9702         }
9703         
9704         
9705         var result = this.processResponse(response);
9706         if(result === true || result.success){
9707             this.form.afterAction(this, true);
9708             return;
9709         }
9710         if(result.errors){
9711             this.form.markInvalid(result.errors);
9712             this.failureType = Roo.form.Action.SERVER_INVALID;
9713         }
9714         this.form.afterAction(this, false);
9715     },
9716     failure : function(response)
9717     {
9718         this.uploadComplete= true;
9719         if (this.haveProgress) {
9720             Roo.MessageBox.hide();
9721         }
9722         
9723         this.response = response;
9724         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9725         this.form.afterAction(this, false);
9726     },
9727     
9728     handleResponse : function(response){
9729         if(this.form.errorReader){
9730             var rs = this.form.errorReader.read(response);
9731             var errors = [];
9732             if(rs.records){
9733                 for(var i = 0, len = rs.records.length; i < len; i++) {
9734                     var r = rs.records[i];
9735                     errors[i] = r.data;
9736                 }
9737             }
9738             if(errors.length < 1){
9739                 errors = null;
9740             }
9741             return {
9742                 success : rs.success,
9743                 errors : errors
9744             };
9745         }
9746         var ret = false;
9747         try {
9748             ret = Roo.decode(response.responseText);
9749         } catch (e) {
9750             ret = {
9751                 success: false,
9752                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9753                 errors : []
9754             };
9755         }
9756         return ret;
9757         
9758     }
9759 });
9760
9761
9762 Roo.form.Action.Load = function(form, options){
9763     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9764     this.reader = this.form.reader;
9765 };
9766
9767 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9768     type : 'load',
9769
9770     run : function(){
9771         
9772         Roo.Ajax.request(Roo.apply(
9773                 this.createCallback(), {
9774                     method:this.getMethod(),
9775                     url:this.getUrl(false),
9776                     params:this.getParams()
9777         }));
9778     },
9779
9780     success : function(response){
9781         
9782         var result = this.processResponse(response);
9783         if(result === true || !result.success || !result.data){
9784             this.failureType = Roo.form.Action.LOAD_FAILURE;
9785             this.form.afterAction(this, false);
9786             return;
9787         }
9788         this.form.clearInvalid();
9789         this.form.setValues(result.data);
9790         this.form.afterAction(this, true);
9791     },
9792
9793     handleResponse : function(response){
9794         if(this.form.reader){
9795             var rs = this.form.reader.read(response);
9796             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9797             return {
9798                 success : rs.success,
9799                 data : data
9800             };
9801         }
9802         return Roo.decode(response.responseText);
9803     }
9804 });
9805
9806 Roo.form.Action.ACTION_TYPES = {
9807     'load' : Roo.form.Action.Load,
9808     'submit' : Roo.form.Action.Submit
9809 };/*
9810  * - LGPL
9811  *
9812  * form
9813  *
9814  */
9815
9816 /**
9817  * @class Roo.bootstrap.Form
9818  * @extends Roo.bootstrap.Component
9819  * Bootstrap Form class
9820  * @cfg {String} method  GET | POST (default POST)
9821  * @cfg {String} labelAlign top | left (default top)
9822  * @cfg {String} align left  | right - for navbars
9823  * @cfg {Boolean} loadMask load mask when submit (default true)
9824
9825  *
9826  * @constructor
9827  * Create a new Form
9828  * @param {Object} config The config object
9829  */
9830
9831
9832 Roo.bootstrap.Form = function(config){
9833     
9834     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9835     
9836     Roo.bootstrap.Form.popover.apply();
9837     
9838     this.addEvents({
9839         /**
9840          * @event clientvalidation
9841          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9842          * @param {Form} this
9843          * @param {Boolean} valid true if the form has passed client-side validation
9844          */
9845         clientvalidation: true,
9846         /**
9847          * @event beforeaction
9848          * Fires before any action is performed. Return false to cancel the action.
9849          * @param {Form} this
9850          * @param {Action} action The action to be performed
9851          */
9852         beforeaction: true,
9853         /**
9854          * @event actionfailed
9855          * Fires when an action fails.
9856          * @param {Form} this
9857          * @param {Action} action The action that failed
9858          */
9859         actionfailed : true,
9860         /**
9861          * @event actioncomplete
9862          * Fires when an action is completed.
9863          * @param {Form} this
9864          * @param {Action} action The action that completed
9865          */
9866         actioncomplete : true
9867     });
9868 };
9869
9870 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9871
9872      /**
9873      * @cfg {String} method
9874      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9875      */
9876     method : 'POST',
9877     /**
9878      * @cfg {String} url
9879      * The URL to use for form actions if one isn't supplied in the action options.
9880      */
9881     /**
9882      * @cfg {Boolean} fileUpload
9883      * Set to true if this form is a file upload.
9884      */
9885
9886     /**
9887      * @cfg {Object} baseParams
9888      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9889      */
9890
9891     /**
9892      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9893      */
9894     timeout: 30,
9895     /**
9896      * @cfg {Sting} align (left|right) for navbar forms
9897      */
9898     align : 'left',
9899
9900     // private
9901     activeAction : null,
9902
9903     /**
9904      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9905      * element by passing it or its id or mask the form itself by passing in true.
9906      * @type Mixed
9907      */
9908     waitMsgTarget : false,
9909
9910     loadMask : true,
9911     
9912     /**
9913      * @cfg {Boolean} errorMask (true|false) default false
9914      */
9915     errorMask : false,
9916     
9917     /**
9918      * @cfg {Number} maskOffset Default 100
9919      */
9920     maskOffset : 100,
9921     
9922     /**
9923      * @cfg {Boolean} maskBody
9924      */
9925     maskBody : false,
9926
9927     getAutoCreate : function(){
9928
9929         var cfg = {
9930             tag: 'form',
9931             method : this.method || 'POST',
9932             id : this.id || Roo.id(),
9933             cls : ''
9934         };
9935         if (this.parent().xtype.match(/^Nav/)) {
9936             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9937
9938         }
9939
9940         if (this.labelAlign == 'left' ) {
9941             cfg.cls += ' form-horizontal';
9942         }
9943
9944
9945         return cfg;
9946     },
9947     initEvents : function()
9948     {
9949         this.el.on('submit', this.onSubmit, this);
9950         // this was added as random key presses on the form where triggering form submit.
9951         this.el.on('keypress', function(e) {
9952             if (e.getCharCode() != 13) {
9953                 return true;
9954             }
9955             // we might need to allow it for textareas.. and some other items.
9956             // check e.getTarget().
9957
9958             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9959                 return true;
9960             }
9961
9962             Roo.log("keypress blocked");
9963
9964             e.preventDefault();
9965             return false;
9966         });
9967         
9968     },
9969     // private
9970     onSubmit : function(e){
9971         e.stopEvent();
9972     },
9973
9974      /**
9975      * Returns true if client-side validation on the form is successful.
9976      * @return Boolean
9977      */
9978     isValid : function(){
9979         var items = this.getItems();
9980         var valid = true;
9981         var target = false;
9982         
9983         items.each(function(f){
9984             
9985             if(f.validate()){
9986                 return;
9987             }
9988             
9989             Roo.log('invalid field: ' + f.name);
9990             
9991             valid = false;
9992
9993             if(!target && f.el.isVisible(true)){
9994                 target = f;
9995             }
9996            
9997         });
9998         
9999         if(this.errorMask && !valid){
10000             Roo.bootstrap.Form.popover.mask(this, target);
10001         }
10002         
10003         return valid;
10004     },
10005     
10006     /**
10007      * Returns true if any fields in this form have changed since their original load.
10008      * @return Boolean
10009      */
10010     isDirty : function(){
10011         var dirty = false;
10012         var items = this.getItems();
10013         items.each(function(f){
10014            if(f.isDirty()){
10015                dirty = true;
10016                return false;
10017            }
10018            return true;
10019         });
10020         return dirty;
10021     },
10022      /**
10023      * Performs a predefined action (submit or load) or custom actions you define on this form.
10024      * @param {String} actionName The name of the action type
10025      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10026      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10027      * accept other config options):
10028      * <pre>
10029 Property          Type             Description
10030 ----------------  ---------------  ----------------------------------------------------------------------------------
10031 url               String           The url for the action (defaults to the form's url)
10032 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10033 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10034 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10035                                    validate the form on the client (defaults to false)
10036      * </pre>
10037      * @return {BasicForm} this
10038      */
10039     doAction : function(action, options){
10040         if(typeof action == 'string'){
10041             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10042         }
10043         if(this.fireEvent('beforeaction', this, action) !== false){
10044             this.beforeAction(action);
10045             action.run.defer(100, action);
10046         }
10047         return this;
10048     },
10049
10050     // private
10051     beforeAction : function(action){
10052         var o = action.options;
10053         
10054         if(this.loadMask){
10055             
10056             if(this.maskBody){
10057                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10058             } else {
10059                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10060             }
10061         }
10062         // not really supported yet.. ??
10063
10064         //if(this.waitMsgTarget === true){
10065         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10066         //}else if(this.waitMsgTarget){
10067         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10068         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10069         //}else {
10070         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10071        // }
10072
10073     },
10074
10075     // private
10076     afterAction : function(action, success){
10077         this.activeAction = null;
10078         var o = action.options;
10079
10080         if(this.loadMask){
10081             
10082             if(this.maskBody){
10083                 Roo.get(document.body).unmask();
10084             } else {
10085                 this.el.unmask();
10086             }
10087         }
10088         
10089         //if(this.waitMsgTarget === true){
10090 //            this.el.unmask();
10091         //}else if(this.waitMsgTarget){
10092         //    this.waitMsgTarget.unmask();
10093         //}else{
10094         //    Roo.MessageBox.updateProgress(1);
10095         //    Roo.MessageBox.hide();
10096        // }
10097         //
10098         if(success){
10099             if(o.reset){
10100                 this.reset();
10101             }
10102             Roo.callback(o.success, o.scope, [this, action]);
10103             this.fireEvent('actioncomplete', this, action);
10104
10105         }else{
10106
10107             // failure condition..
10108             // we have a scenario where updates need confirming.
10109             // eg. if a locking scenario exists..
10110             // we look for { errors : { needs_confirm : true }} in the response.
10111             if (
10112                 (typeof(action.result) != 'undefined')  &&
10113                 (typeof(action.result.errors) != 'undefined')  &&
10114                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10115            ){
10116                 var _t = this;
10117                 Roo.log("not supported yet");
10118                  /*
10119
10120                 Roo.MessageBox.confirm(
10121                     "Change requires confirmation",
10122                     action.result.errorMsg,
10123                     function(r) {
10124                         if (r != 'yes') {
10125                             return;
10126                         }
10127                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10128                     }
10129
10130                 );
10131                 */
10132
10133
10134                 return;
10135             }
10136
10137             Roo.callback(o.failure, o.scope, [this, action]);
10138             // show an error message if no failed handler is set..
10139             if (!this.hasListener('actionfailed')) {
10140                 Roo.log("need to add dialog support");
10141                 /*
10142                 Roo.MessageBox.alert("Error",
10143                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10144                         action.result.errorMsg :
10145                         "Saving Failed, please check your entries or try again"
10146                 );
10147                 */
10148             }
10149
10150             this.fireEvent('actionfailed', this, action);
10151         }
10152
10153     },
10154     /**
10155      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10156      * @param {String} id The value to search for
10157      * @return Field
10158      */
10159     findField : function(id){
10160         var items = this.getItems();
10161         var field = items.get(id);
10162         if(!field){
10163              items.each(function(f){
10164                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10165                     field = f;
10166                     return false;
10167                 }
10168                 return true;
10169             });
10170         }
10171         return field || null;
10172     },
10173      /**
10174      * Mark fields in this form invalid in bulk.
10175      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10176      * @return {BasicForm} this
10177      */
10178     markInvalid : function(errors){
10179         if(errors instanceof Array){
10180             for(var i = 0, len = errors.length; i < len; i++){
10181                 var fieldError = errors[i];
10182                 var f = this.findField(fieldError.id);
10183                 if(f){
10184                     f.markInvalid(fieldError.msg);
10185                 }
10186             }
10187         }else{
10188             var field, id;
10189             for(id in errors){
10190                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10191                     field.markInvalid(errors[id]);
10192                 }
10193             }
10194         }
10195         //Roo.each(this.childForms || [], function (f) {
10196         //    f.markInvalid(errors);
10197         //});
10198
10199         return this;
10200     },
10201
10202     /**
10203      * Set values for fields in this form in bulk.
10204      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10205      * @return {BasicForm} this
10206      */
10207     setValues : function(values){
10208         if(values instanceof Array){ // array of objects
10209             for(var i = 0, len = values.length; i < len; i++){
10210                 var v = values[i];
10211                 var f = this.findField(v.id);
10212                 if(f){
10213                     f.setValue(v.value);
10214                     if(this.trackResetOnLoad){
10215                         f.originalValue = f.getValue();
10216                     }
10217                 }
10218             }
10219         }else{ // object hash
10220             var field, id;
10221             for(id in values){
10222                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10223
10224                     if (field.setFromData &&
10225                         field.valueField &&
10226                         field.displayField &&
10227                         // combos' with local stores can
10228                         // be queried via setValue()
10229                         // to set their value..
10230                         (field.store && !field.store.isLocal)
10231                         ) {
10232                         // it's a combo
10233                         var sd = { };
10234                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10235                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10236                         field.setFromData(sd);
10237
10238                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10239                         
10240                         field.setFromData(values);
10241                         
10242                     } else {
10243                         field.setValue(values[id]);
10244                     }
10245
10246
10247                     if(this.trackResetOnLoad){
10248                         field.originalValue = field.getValue();
10249                     }
10250                 }
10251             }
10252         }
10253
10254         //Roo.each(this.childForms || [], function (f) {
10255         //    f.setValues(values);
10256         //});
10257
10258         return this;
10259     },
10260
10261     /**
10262      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10263      * they are returned as an array.
10264      * @param {Boolean} asString
10265      * @return {Object}
10266      */
10267     getValues : function(asString){
10268         //if (this.childForms) {
10269             // copy values from the child forms
10270         //    Roo.each(this.childForms, function (f) {
10271         //        this.setValues(f.getValues());
10272         //    }, this);
10273         //}
10274
10275
10276
10277         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10278         if(asString === true){
10279             return fs;
10280         }
10281         return Roo.urlDecode(fs);
10282     },
10283
10284     /**
10285      * Returns the fields in this form as an object with key/value pairs.
10286      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10287      * @return {Object}
10288      */
10289     getFieldValues : function(with_hidden)
10290     {
10291         var items = this.getItems();
10292         var ret = {};
10293         items.each(function(f){
10294             
10295             if (!f.getName()) {
10296                 return;
10297             }
10298             
10299             var v = f.getValue();
10300             
10301             if (f.inputType =='radio') {
10302                 if (typeof(ret[f.getName()]) == 'undefined') {
10303                     ret[f.getName()] = ''; // empty..
10304                 }
10305
10306                 if (!f.el.dom.checked) {
10307                     return;
10308
10309                 }
10310                 v = f.el.dom.value;
10311
10312             }
10313             
10314             if(f.xtype == 'MoneyField'){
10315                 ret[f.currencyName] = f.getCurrency();
10316             }
10317
10318             // not sure if this supported any more..
10319             if ((typeof(v) == 'object') && f.getRawValue) {
10320                 v = f.getRawValue() ; // dates..
10321             }
10322             // combo boxes where name != hiddenName...
10323             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10324                 ret[f.name] = f.getRawValue();
10325             }
10326             ret[f.getName()] = v;
10327         });
10328
10329         return ret;
10330     },
10331
10332     /**
10333      * Clears all invalid messages in this form.
10334      * @return {BasicForm} this
10335      */
10336     clearInvalid : function(){
10337         var items = this.getItems();
10338
10339         items.each(function(f){
10340            f.clearInvalid();
10341         });
10342
10343         return this;
10344     },
10345
10346     /**
10347      * Resets this form.
10348      * @return {BasicForm} this
10349      */
10350     reset : function(){
10351         var items = this.getItems();
10352         items.each(function(f){
10353             f.reset();
10354         });
10355
10356         Roo.each(this.childForms || [], function (f) {
10357             f.reset();
10358         });
10359
10360
10361         return this;
10362     },
10363     
10364     getItems : function()
10365     {
10366         var r=new Roo.util.MixedCollection(false, function(o){
10367             return o.id || (o.id = Roo.id());
10368         });
10369         var iter = function(el) {
10370             if (el.inputEl) {
10371                 r.add(el);
10372             }
10373             if (!el.items) {
10374                 return;
10375             }
10376             Roo.each(el.items,function(e) {
10377                 iter(e);
10378             });
10379         };
10380
10381         iter(this);
10382         return r;
10383     },
10384     
10385     hideFields : function(items)
10386     {
10387         Roo.each(items, function(i){
10388             
10389             var f = this.findField(i);
10390             
10391             if(!f){
10392                 return;
10393             }
10394             
10395             f.hide();
10396             
10397         }, this);
10398     },
10399     
10400     showFields : function(items)
10401     {
10402         Roo.each(items, function(i){
10403             
10404             var f = this.findField(i);
10405             
10406             if(!f){
10407                 return;
10408             }
10409             
10410             f.show();
10411             
10412         }, this);
10413     }
10414
10415 });
10416
10417 Roo.apply(Roo.bootstrap.Form, {
10418     
10419     popover : {
10420         
10421         padding : 5,
10422         
10423         isApplied : false,
10424         
10425         isMasked : false,
10426         
10427         form : false,
10428         
10429         target : false,
10430         
10431         toolTip : false,
10432         
10433         intervalID : false,
10434         
10435         maskEl : false,
10436         
10437         apply : function()
10438         {
10439             if(this.isApplied){
10440                 return;
10441             }
10442             
10443             this.maskEl = {
10444                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10445                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10446                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10447                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10448             };
10449             
10450             this.maskEl.top.enableDisplayMode("block");
10451             this.maskEl.left.enableDisplayMode("block");
10452             this.maskEl.bottom.enableDisplayMode("block");
10453             this.maskEl.right.enableDisplayMode("block");
10454             
10455             this.toolTip = new Roo.bootstrap.Tooltip({
10456                 cls : 'roo-form-error-popover',
10457                 alignment : {
10458                     'left' : ['r-l', [-2,0], 'right'],
10459                     'right' : ['l-r', [2,0], 'left'],
10460                     'bottom' : ['tl-bl', [0,2], 'top'],
10461                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10462                 }
10463             });
10464             
10465             this.toolTip.render(Roo.get(document.body));
10466
10467             this.toolTip.el.enableDisplayMode("block");
10468             
10469             Roo.get(document.body).on('click', function(){
10470                 this.unmask();
10471             }, this);
10472             
10473             Roo.get(document.body).on('touchstart', function(){
10474                 this.unmask();
10475             }, this);
10476             
10477             this.isApplied = true
10478         },
10479         
10480         mask : function(form, target)
10481         {
10482             this.form = form;
10483             
10484             this.target = target;
10485             
10486             if(!this.form.errorMask || !target.el){
10487                 return;
10488             }
10489             
10490             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10491             
10492             Roo.log(scrollable);
10493             
10494             var ot = this.target.el.calcOffsetsTo(scrollable);
10495             
10496             var scrollTo = ot[1] - this.form.maskOffset;
10497             
10498             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10499             
10500             scrollable.scrollTo('top', scrollTo);
10501             
10502             var box = this.target.el.getBox();
10503             Roo.log(box);
10504             var zIndex = Roo.bootstrap.Modal.zIndex++;
10505
10506             
10507             this.maskEl.top.setStyle('position', 'absolute');
10508             this.maskEl.top.setStyle('z-index', zIndex);
10509             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10510             this.maskEl.top.setLeft(0);
10511             this.maskEl.top.setTop(0);
10512             this.maskEl.top.show();
10513             
10514             this.maskEl.left.setStyle('position', 'absolute');
10515             this.maskEl.left.setStyle('z-index', zIndex);
10516             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10517             this.maskEl.left.setLeft(0);
10518             this.maskEl.left.setTop(box.y - this.padding);
10519             this.maskEl.left.show();
10520
10521             this.maskEl.bottom.setStyle('position', 'absolute');
10522             this.maskEl.bottom.setStyle('z-index', zIndex);
10523             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10524             this.maskEl.bottom.setLeft(0);
10525             this.maskEl.bottom.setTop(box.bottom + this.padding);
10526             this.maskEl.bottom.show();
10527
10528             this.maskEl.right.setStyle('position', 'absolute');
10529             this.maskEl.right.setStyle('z-index', zIndex);
10530             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10531             this.maskEl.right.setLeft(box.right + this.padding);
10532             this.maskEl.right.setTop(box.y - this.padding);
10533             this.maskEl.right.show();
10534
10535             this.toolTip.bindEl = this.target.el;
10536
10537             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10538
10539             var tip = this.target.blankText;
10540
10541             if(this.target.getValue() !== '' ) {
10542                 
10543                 if (this.target.invalidText.length) {
10544                     tip = this.target.invalidText;
10545                 } else if (this.target.regexText.length){
10546                     tip = this.target.regexText;
10547                 }
10548             }
10549
10550             this.toolTip.show(tip);
10551
10552             this.intervalID = window.setInterval(function() {
10553                 Roo.bootstrap.Form.popover.unmask();
10554             }, 10000);
10555
10556             window.onwheel = function(){ return false;};
10557             
10558             (function(){ this.isMasked = true; }).defer(500, this);
10559             
10560         },
10561         
10562         unmask : function()
10563         {
10564             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10565                 return;
10566             }
10567             
10568             this.maskEl.top.setStyle('position', 'absolute');
10569             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10570             this.maskEl.top.hide();
10571
10572             this.maskEl.left.setStyle('position', 'absolute');
10573             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10574             this.maskEl.left.hide();
10575
10576             this.maskEl.bottom.setStyle('position', 'absolute');
10577             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10578             this.maskEl.bottom.hide();
10579
10580             this.maskEl.right.setStyle('position', 'absolute');
10581             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10582             this.maskEl.right.hide();
10583             
10584             this.toolTip.hide();
10585             
10586             this.toolTip.el.hide();
10587             
10588             window.onwheel = function(){ return true;};
10589             
10590             if(this.intervalID){
10591                 window.clearInterval(this.intervalID);
10592                 this.intervalID = false;
10593             }
10594             
10595             this.isMasked = false;
10596             
10597         }
10598         
10599     }
10600     
10601 });
10602
10603 /*
10604  * Based on:
10605  * Ext JS Library 1.1.1
10606  * Copyright(c) 2006-2007, Ext JS, LLC.
10607  *
10608  * Originally Released Under LGPL - original licence link has changed is not relivant.
10609  *
10610  * Fork - LGPL
10611  * <script type="text/javascript">
10612  */
10613 /**
10614  * @class Roo.form.VTypes
10615  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10616  * @singleton
10617  */
10618 Roo.form.VTypes = function(){
10619     // closure these in so they are only created once.
10620     var alpha = /^[a-zA-Z_]+$/;
10621     var alphanum = /^[a-zA-Z0-9_]+$/;
10622     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10623     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10624
10625     // All these messages and functions are configurable
10626     return {
10627         /**
10628          * The function used to validate email addresses
10629          * @param {String} value The email address
10630          */
10631         'email' : function(v){
10632             return email.test(v);
10633         },
10634         /**
10635          * The error text to display when the email validation function returns false
10636          * @type String
10637          */
10638         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10639         /**
10640          * The keystroke filter mask to be applied on email input
10641          * @type RegExp
10642          */
10643         'emailMask' : /[a-z0-9_\.\-@]/i,
10644
10645         /**
10646          * The function used to validate URLs
10647          * @param {String} value The URL
10648          */
10649         'url' : function(v){
10650             return url.test(v);
10651         },
10652         /**
10653          * The error text to display when the url validation function returns false
10654          * @type String
10655          */
10656         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10657         
10658         /**
10659          * The function used to validate alpha values
10660          * @param {String} value The value
10661          */
10662         'alpha' : function(v){
10663             return alpha.test(v);
10664         },
10665         /**
10666          * The error text to display when the alpha validation function returns false
10667          * @type String
10668          */
10669         'alphaText' : 'This field should only contain letters and _',
10670         /**
10671          * The keystroke filter mask to be applied on alpha input
10672          * @type RegExp
10673          */
10674         'alphaMask' : /[a-z_]/i,
10675
10676         /**
10677          * The function used to validate alphanumeric values
10678          * @param {String} value The value
10679          */
10680         'alphanum' : function(v){
10681             return alphanum.test(v);
10682         },
10683         /**
10684          * The error text to display when the alphanumeric validation function returns false
10685          * @type String
10686          */
10687         'alphanumText' : 'This field should only contain letters, numbers and _',
10688         /**
10689          * The keystroke filter mask to be applied on alphanumeric input
10690          * @type RegExp
10691          */
10692         'alphanumMask' : /[a-z0-9_]/i
10693     };
10694 }();/*
10695  * - LGPL
10696  *
10697  * Input
10698  * 
10699  */
10700
10701 /**
10702  * @class Roo.bootstrap.Input
10703  * @extends Roo.bootstrap.Component
10704  * Bootstrap Input class
10705  * @cfg {Boolean} disabled is it disabled
10706  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10707  * @cfg {String} name name of the input
10708  * @cfg {string} fieldLabel - the label associated
10709  * @cfg {string} placeholder - placeholder to put in text.
10710  * @cfg {string}  before - input group add on before
10711  * @cfg {string} after - input group add on after
10712  * @cfg {string} size - (lg|sm) or leave empty..
10713  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10714  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10715  * @cfg {Number} md colspan out of 12 for computer-sized screens
10716  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10717  * @cfg {string} value default value of the input
10718  * @cfg {Number} labelWidth set the width of label 
10719  * @cfg {Number} labellg set the width of label (1-12)
10720  * @cfg {Number} labelmd set the width of label (1-12)
10721  * @cfg {Number} labelsm set the width of label (1-12)
10722  * @cfg {Number} labelxs set the width of label (1-12)
10723  * @cfg {String} labelAlign (top|left)
10724  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10725  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10726  * @cfg {String} indicatorpos (left|right) default left
10727  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10728  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10729  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10730
10731  * @cfg {String} align (left|center|right) Default left
10732  * @cfg {Boolean} forceFeedback (true|false) Default false
10733  * 
10734  * @constructor
10735  * Create a new Input
10736  * @param {Object} config The config object
10737  */
10738
10739 Roo.bootstrap.Input = function(config){
10740     
10741     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10742     
10743     this.addEvents({
10744         /**
10745          * @event focus
10746          * Fires when this field receives input focus.
10747          * @param {Roo.form.Field} this
10748          */
10749         focus : true,
10750         /**
10751          * @event blur
10752          * Fires when this field loses input focus.
10753          * @param {Roo.form.Field} this
10754          */
10755         blur : true,
10756         /**
10757          * @event specialkey
10758          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10759          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10760          * @param {Roo.form.Field} this
10761          * @param {Roo.EventObject} e The event object
10762          */
10763         specialkey : true,
10764         /**
10765          * @event change
10766          * Fires just before the field blurs if the field value has changed.
10767          * @param {Roo.form.Field} this
10768          * @param {Mixed} newValue The new value
10769          * @param {Mixed} oldValue The original value
10770          */
10771         change : true,
10772         /**
10773          * @event invalid
10774          * Fires after the field has been marked as invalid.
10775          * @param {Roo.form.Field} this
10776          * @param {String} msg The validation message
10777          */
10778         invalid : true,
10779         /**
10780          * @event valid
10781          * Fires after the field has been validated with no errors.
10782          * @param {Roo.form.Field} this
10783          */
10784         valid : true,
10785          /**
10786          * @event keyup
10787          * Fires after the key up
10788          * @param {Roo.form.Field} this
10789          * @param {Roo.EventObject}  e The event Object
10790          */
10791         keyup : true,
10792         /**
10793          * @event paste
10794          * Fires after the user pastes into input
10795          * @param {Roo.form.Field} this
10796          * @param {Roo.EventObject}  e The event Object
10797          */
10798         paste : true
10799     });
10800 };
10801
10802 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10803      /**
10804      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10805       automatic validation (defaults to "keyup").
10806      */
10807     validationEvent : "keyup",
10808      /**
10809      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10810      */
10811     validateOnBlur : true,
10812     /**
10813      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10814      */
10815     validationDelay : 250,
10816      /**
10817      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10818      */
10819     focusClass : "x-form-focus",  // not needed???
10820     
10821        
10822     /**
10823      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10824      */
10825     invalidClass : "has-warning",
10826     
10827     /**
10828      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10829      */
10830     validClass : "has-success",
10831     
10832     /**
10833      * @cfg {Boolean} hasFeedback (true|false) default true
10834      */
10835     hasFeedback : true,
10836     
10837     /**
10838      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10839      */
10840     invalidFeedbackClass : "glyphicon-warning-sign",
10841     
10842     /**
10843      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10844      */
10845     validFeedbackClass : "glyphicon-ok",
10846     
10847     /**
10848      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10849      */
10850     selectOnFocus : false,
10851     
10852      /**
10853      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10854      */
10855     maskRe : null,
10856        /**
10857      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10858      */
10859     vtype : null,
10860     
10861       /**
10862      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10863      */
10864     disableKeyFilter : false,
10865     
10866        /**
10867      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10868      */
10869     disabled : false,
10870      /**
10871      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10872      */
10873     allowBlank : true,
10874     /**
10875      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10876      */
10877     blankText : "Please complete this mandatory field",
10878     
10879      /**
10880      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10881      */
10882     minLength : 0,
10883     /**
10884      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10885      */
10886     maxLength : Number.MAX_VALUE,
10887     /**
10888      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10889      */
10890     minLengthText : "The minimum length for this field is {0}",
10891     /**
10892      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10893      */
10894     maxLengthText : "The maximum length for this field is {0}",
10895   
10896     
10897     /**
10898      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10899      * If available, this function will be called only after the basic validators all return true, and will be passed the
10900      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10901      */
10902     validator : null,
10903     /**
10904      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10905      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10906      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10907      */
10908     regex : null,
10909     /**
10910      * @cfg {String} regexText -- Depricated - use Invalid Text
10911      */
10912     regexText : "",
10913     
10914     /**
10915      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10916      */
10917     invalidText : "",
10918     
10919     
10920     
10921     autocomplete: false,
10922     
10923     
10924     fieldLabel : '',
10925     inputType : 'text',
10926     
10927     name : false,
10928     placeholder: false,
10929     before : false,
10930     after : false,
10931     size : false,
10932     hasFocus : false,
10933     preventMark: false,
10934     isFormField : true,
10935     value : '',
10936     labelWidth : 2,
10937     labelAlign : false,
10938     readOnly : false,
10939     align : false,
10940     formatedValue : false,
10941     forceFeedback : false,
10942     
10943     indicatorpos : 'left',
10944     
10945     labellg : 0,
10946     labelmd : 0,
10947     labelsm : 0,
10948     labelxs : 0,
10949     
10950     capture : '',
10951     accept : '',
10952     
10953     parentLabelAlign : function()
10954     {
10955         var parent = this;
10956         while (parent.parent()) {
10957             parent = parent.parent();
10958             if (typeof(parent.labelAlign) !='undefined') {
10959                 return parent.labelAlign;
10960             }
10961         }
10962         return 'left';
10963         
10964     },
10965     
10966     getAutoCreate : function()
10967     {
10968         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10969         
10970         var id = Roo.id();
10971         
10972         var cfg = {};
10973         
10974         if(this.inputType != 'hidden'){
10975             cfg.cls = 'form-group' //input-group
10976         }
10977         
10978         var input =  {
10979             tag: 'input',
10980             id : id,
10981             type : this.inputType,
10982             value : this.value,
10983             cls : 'form-control',
10984             placeholder : this.placeholder || '',
10985             autocomplete : this.autocomplete || 'new-password'
10986         };
10987         if (this.inputType == 'file') {
10988             input.style = 'overflow:hidden'; // why not in CSS?
10989         }
10990         
10991         if(this.capture.length){
10992             input.capture = this.capture;
10993         }
10994         
10995         if(this.accept.length){
10996             input.accept = this.accept + "/*";
10997         }
10998         
10999         if(this.align){
11000             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11001         }
11002         
11003         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11004             input.maxLength = this.maxLength;
11005         }
11006         
11007         if (this.disabled) {
11008             input.disabled=true;
11009         }
11010         
11011         if (this.readOnly) {
11012             input.readonly=true;
11013         }
11014         
11015         if (this.name) {
11016             input.name = this.name;
11017         }
11018         
11019         if (this.size) {
11020             input.cls += ' input-' + this.size;
11021         }
11022         
11023         var settings=this;
11024         ['xs','sm','md','lg'].map(function(size){
11025             if (settings[size]) {
11026                 cfg.cls += ' col-' + size + '-' + settings[size];
11027             }
11028         });
11029         
11030         var inputblock = input;
11031         
11032         var feedback = {
11033             tag: 'span',
11034             cls: 'glyphicon form-control-feedback'
11035         };
11036             
11037         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11038             
11039             inputblock = {
11040                 cls : 'has-feedback',
11041                 cn :  [
11042                     input,
11043                     feedback
11044                 ] 
11045             };  
11046         }
11047         
11048         if (this.before || this.after) {
11049             
11050             inputblock = {
11051                 cls : 'input-group',
11052                 cn :  [] 
11053             };
11054             
11055             if (this.before && typeof(this.before) == 'string') {
11056                 
11057                 inputblock.cn.push({
11058                     tag :'span',
11059                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11060                     html : this.before
11061                 });
11062             }
11063             if (this.before && typeof(this.before) == 'object') {
11064                 this.before = Roo.factory(this.before);
11065                 
11066                 inputblock.cn.push({
11067                     tag :'span',
11068                     cls : 'roo-input-before input-group-prepend   input-group-' +
11069                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11070                 });
11071             }
11072             
11073             inputblock.cn.push(input);
11074             
11075             if (this.after && typeof(this.after) == 'string') {
11076                 inputblock.cn.push({
11077                     tag :'span',
11078                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11079                     html : this.after
11080                 });
11081             }
11082             if (this.after && typeof(this.after) == 'object') {
11083                 this.after = Roo.factory(this.after);
11084                 
11085                 inputblock.cn.push({
11086                     tag :'span',
11087                     cls : 'roo-input-after input-group-append  input-group-' +
11088                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11089                 });
11090             }
11091             
11092             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11093                 inputblock.cls += ' has-feedback';
11094                 inputblock.cn.push(feedback);
11095             }
11096         };
11097         var indicator = {
11098             tag : 'i',
11099             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11100             tooltip : 'This field is required'
11101         };
11102         if (this.allowBlank ) {
11103             indicator.style = this.allowBlank ? ' display:none' : '';
11104         }
11105         if (align ==='left' && this.fieldLabel.length) {
11106             
11107             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11108             
11109             cfg.cn = [
11110                 indicator,
11111                 {
11112                     tag: 'label',
11113                     'for' :  id,
11114                     cls : 'control-label col-form-label',
11115                     html : this.fieldLabel
11116
11117                 },
11118                 {
11119                     cls : "", 
11120                     cn: [
11121                         inputblock
11122                     ]
11123                 }
11124             ];
11125             
11126             var labelCfg = cfg.cn[1];
11127             var contentCfg = cfg.cn[2];
11128             
11129             if(this.indicatorpos == 'right'){
11130                 cfg.cn = [
11131                     {
11132                         tag: 'label',
11133                         'for' :  id,
11134                         cls : 'control-label col-form-label',
11135                         cn : [
11136                             {
11137                                 tag : 'span',
11138                                 html : this.fieldLabel
11139                             },
11140                             indicator
11141                         ]
11142                     },
11143                     {
11144                         cls : "",
11145                         cn: [
11146                             inputblock
11147                         ]
11148                     }
11149
11150                 ];
11151                 
11152                 labelCfg = cfg.cn[0];
11153                 contentCfg = cfg.cn[1];
11154             
11155             }
11156             
11157             if(this.labelWidth > 12){
11158                 labelCfg.style = "width: " + this.labelWidth + 'px';
11159             }
11160             
11161             if(this.labelWidth < 13 && this.labelmd == 0){
11162                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11163             }
11164             
11165             if(this.labellg > 0){
11166                 labelCfg.cls += ' col-lg-' + this.labellg;
11167                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11168             }
11169             
11170             if(this.labelmd > 0){
11171                 labelCfg.cls += ' col-md-' + this.labelmd;
11172                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11173             }
11174             
11175             if(this.labelsm > 0){
11176                 labelCfg.cls += ' col-sm-' + this.labelsm;
11177                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11178             }
11179             
11180             if(this.labelxs > 0){
11181                 labelCfg.cls += ' col-xs-' + this.labelxs;
11182                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11183             }
11184             
11185             
11186         } else if ( this.fieldLabel.length) {
11187                 
11188             
11189             
11190             cfg.cn = [
11191                 {
11192                     tag : 'i',
11193                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11194                     tooltip : 'This field is required',
11195                     style : this.allowBlank ? ' display:none' : '' 
11196                 },
11197                 {
11198                     tag: 'label',
11199                    //cls : 'input-group-addon',
11200                     html : this.fieldLabel
11201
11202                 },
11203
11204                inputblock
11205
11206            ];
11207            
11208            if(this.indicatorpos == 'right'){
11209        
11210                 cfg.cn = [
11211                     {
11212                         tag: 'label',
11213                        //cls : 'input-group-addon',
11214                         html : this.fieldLabel
11215
11216                     },
11217                     {
11218                         tag : 'i',
11219                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11220                         tooltip : 'This field is required',
11221                         style : this.allowBlank ? ' display:none' : '' 
11222                     },
11223
11224                    inputblock
11225
11226                ];
11227
11228             }
11229
11230         } else {
11231             
11232             cfg.cn = [
11233
11234                     inputblock
11235
11236             ];
11237                 
11238                 
11239         };
11240         
11241         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11242            cfg.cls += ' navbar-form';
11243         }
11244         
11245         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11246             // on BS4 we do this only if not form 
11247             cfg.cls += ' navbar-form';
11248             cfg.tag = 'li';
11249         }
11250         
11251         return cfg;
11252         
11253     },
11254     /**
11255      * return the real input element.
11256      */
11257     inputEl: function ()
11258     {
11259         return this.el.select('input.form-control',true).first();
11260     },
11261     
11262     tooltipEl : function()
11263     {
11264         return this.inputEl();
11265     },
11266     
11267     indicatorEl : function()
11268     {
11269         if (Roo.bootstrap.version == 4) {
11270             return false; // not enabled in v4 yet.
11271         }
11272         
11273         var indicator = this.el.select('i.roo-required-indicator',true).first();
11274         
11275         if(!indicator){
11276             return false;
11277         }
11278         
11279         return indicator;
11280         
11281     },
11282     
11283     setDisabled : function(v)
11284     {
11285         var i  = this.inputEl().dom;
11286         if (!v) {
11287             i.removeAttribute('disabled');
11288             return;
11289             
11290         }
11291         i.setAttribute('disabled','true');
11292     },
11293     initEvents : function()
11294     {
11295           
11296         this.inputEl().on("keydown" , this.fireKey,  this);
11297         this.inputEl().on("focus", this.onFocus,  this);
11298         this.inputEl().on("blur", this.onBlur,  this);
11299         
11300         this.inputEl().relayEvent('keyup', this);
11301         this.inputEl().relayEvent('paste', this);
11302         
11303         this.indicator = this.indicatorEl();
11304         
11305         if(this.indicator){
11306             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11307         }
11308  
11309         // reference to original value for reset
11310         this.originalValue = this.getValue();
11311         //Roo.form.TextField.superclass.initEvents.call(this);
11312         if(this.validationEvent == 'keyup'){
11313             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11314             this.inputEl().on('keyup', this.filterValidation, this);
11315         }
11316         else if(this.validationEvent !== false){
11317             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11318         }
11319         
11320         if(this.selectOnFocus){
11321             this.on("focus", this.preFocus, this);
11322             
11323         }
11324         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11325             this.inputEl().on("keypress", this.filterKeys, this);
11326         } else {
11327             this.inputEl().relayEvent('keypress', this);
11328         }
11329        /* if(this.grow){
11330             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11331             this.el.on("click", this.autoSize,  this);
11332         }
11333         */
11334         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11335             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11336         }
11337         
11338         if (typeof(this.before) == 'object') {
11339             this.before.render(this.el.select('.roo-input-before',true).first());
11340         }
11341         if (typeof(this.after) == 'object') {
11342             this.after.render(this.el.select('.roo-input-after',true).first());
11343         }
11344         
11345         this.inputEl().on('change', this.onChange, this);
11346         
11347     },
11348     filterValidation : function(e){
11349         if(!e.isNavKeyPress()){
11350             this.validationTask.delay(this.validationDelay);
11351         }
11352     },
11353      /**
11354      * Validates the field value
11355      * @return {Boolean} True if the value is valid, else false
11356      */
11357     validate : function(){
11358         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11359         if(this.disabled || this.validateValue(this.getRawValue())){
11360             this.markValid();
11361             return true;
11362         }
11363         
11364         this.markInvalid();
11365         return false;
11366     },
11367     
11368     
11369     /**
11370      * Validates a value according to the field's validation rules and marks the field as invalid
11371      * if the validation fails
11372      * @param {Mixed} value The value to validate
11373      * @return {Boolean} True if the value is valid, else false
11374      */
11375     validateValue : function(value)
11376     {
11377         if(this.getVisibilityEl().hasClass('hidden')){
11378             return true;
11379         }
11380         
11381         if(value.length < 1)  { // if it's blank
11382             if(this.allowBlank){
11383                 return true;
11384             }
11385             return false;
11386         }
11387         
11388         if(value.length < this.minLength){
11389             return false;
11390         }
11391         if(value.length > this.maxLength){
11392             return false;
11393         }
11394         if(this.vtype){
11395             var vt = Roo.form.VTypes;
11396             if(!vt[this.vtype](value, this)){
11397                 return false;
11398             }
11399         }
11400         if(typeof this.validator == "function"){
11401             var msg = this.validator(value);
11402             if(msg !== true){
11403                 return false;
11404             }
11405             if (typeof(msg) == 'string') {
11406                 this.invalidText = msg;
11407             }
11408         }
11409         
11410         if(this.regex && !this.regex.test(value)){
11411             return false;
11412         }
11413         
11414         return true;
11415     },
11416     
11417      // private
11418     fireKey : function(e){
11419         //Roo.log('field ' + e.getKey());
11420         if(e.isNavKeyPress()){
11421             this.fireEvent("specialkey", this, e);
11422         }
11423     },
11424     focus : function (selectText){
11425         if(this.rendered){
11426             this.inputEl().focus();
11427             if(selectText === true){
11428                 this.inputEl().dom.select();
11429             }
11430         }
11431         return this;
11432     } ,
11433     
11434     onFocus : function(){
11435         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11436            // this.el.addClass(this.focusClass);
11437         }
11438         if(!this.hasFocus){
11439             this.hasFocus = true;
11440             this.startValue = this.getValue();
11441             this.fireEvent("focus", this);
11442         }
11443     },
11444     
11445     beforeBlur : Roo.emptyFn,
11446
11447     
11448     // private
11449     onBlur : function(){
11450         this.beforeBlur();
11451         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11452             //this.el.removeClass(this.focusClass);
11453         }
11454         this.hasFocus = false;
11455         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11456             this.validate();
11457         }
11458         var v = this.getValue();
11459         if(String(v) !== String(this.startValue)){
11460             this.fireEvent('change', this, v, this.startValue);
11461         }
11462         this.fireEvent("blur", this);
11463     },
11464     
11465     onChange : function(e)
11466     {
11467         var v = this.getValue();
11468         if(String(v) !== String(this.startValue)){
11469             this.fireEvent('change', this, v, this.startValue);
11470         }
11471         
11472     },
11473     
11474     /**
11475      * Resets the current field value to the originally loaded value and clears any validation messages
11476      */
11477     reset : function(){
11478         this.setValue(this.originalValue);
11479         this.validate();
11480     },
11481      /**
11482      * Returns the name of the field
11483      * @return {Mixed} name The name field
11484      */
11485     getName: function(){
11486         return this.name;
11487     },
11488      /**
11489      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11490      * @return {Mixed} value The field value
11491      */
11492     getValue : function(){
11493         
11494         var v = this.inputEl().getValue();
11495         
11496         return v;
11497     },
11498     /**
11499      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11500      * @return {Mixed} value The field value
11501      */
11502     getRawValue : function(){
11503         var v = this.inputEl().getValue();
11504         
11505         return v;
11506     },
11507     
11508     /**
11509      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11510      * @param {Mixed} value The value to set
11511      */
11512     setRawValue : function(v){
11513         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11514     },
11515     
11516     selectText : function(start, end){
11517         var v = this.getRawValue();
11518         if(v.length > 0){
11519             start = start === undefined ? 0 : start;
11520             end = end === undefined ? v.length : end;
11521             var d = this.inputEl().dom;
11522             if(d.setSelectionRange){
11523                 d.setSelectionRange(start, end);
11524             }else if(d.createTextRange){
11525                 var range = d.createTextRange();
11526                 range.moveStart("character", start);
11527                 range.moveEnd("character", v.length-end);
11528                 range.select();
11529             }
11530         }
11531     },
11532     
11533     /**
11534      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11535      * @param {Mixed} value The value to set
11536      */
11537     setValue : function(v){
11538         this.value = v;
11539         if(this.rendered){
11540             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11541             this.validate();
11542         }
11543     },
11544     
11545     /*
11546     processValue : function(value){
11547         if(this.stripCharsRe){
11548             var newValue = value.replace(this.stripCharsRe, '');
11549             if(newValue !== value){
11550                 this.setRawValue(newValue);
11551                 return newValue;
11552             }
11553         }
11554         return value;
11555     },
11556   */
11557     preFocus : function(){
11558         
11559         if(this.selectOnFocus){
11560             this.inputEl().dom.select();
11561         }
11562     },
11563     filterKeys : function(e){
11564         var k = e.getKey();
11565         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11566             return;
11567         }
11568         var c = e.getCharCode(), cc = String.fromCharCode(c);
11569         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11570             return;
11571         }
11572         if(!this.maskRe.test(cc)){
11573             e.stopEvent();
11574         }
11575     },
11576      /**
11577      * Clear any invalid styles/messages for this field
11578      */
11579     clearInvalid : function(){
11580         
11581         if(!this.el || this.preventMark){ // not rendered
11582             return;
11583         }
11584         
11585         
11586         this.el.removeClass([this.invalidClass, 'is-invalid']);
11587         
11588         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11589             
11590             var feedback = this.el.select('.form-control-feedback', true).first();
11591             
11592             if(feedback){
11593                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11594             }
11595             
11596         }
11597         
11598         if(this.indicator){
11599             this.indicator.removeClass('visible');
11600             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11601         }
11602         
11603         this.fireEvent('valid', this);
11604     },
11605     
11606      /**
11607      * Mark this field as valid
11608      */
11609     markValid : function()
11610     {
11611         if(!this.el  || this.preventMark){ // not rendered...
11612             return;
11613         }
11614         
11615         this.el.removeClass([this.invalidClass, this.validClass]);
11616         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11617
11618         var feedback = this.el.select('.form-control-feedback', true).first();
11619             
11620         if(feedback){
11621             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11622         }
11623         
11624         if(this.indicator){
11625             this.indicator.removeClass('visible');
11626             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11627         }
11628         
11629         if(this.disabled){
11630             return;
11631         }
11632         
11633            
11634         if(this.allowBlank && !this.getRawValue().length){
11635             return;
11636         }
11637         if (Roo.bootstrap.version == 3) {
11638             this.el.addClass(this.validClass);
11639         } else {
11640             this.inputEl().addClass('is-valid');
11641         }
11642
11643         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11644             
11645             var feedback = this.el.select('.form-control-feedback', true).first();
11646             
11647             if(feedback){
11648                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11649                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11650             }
11651             
11652         }
11653         
11654         this.fireEvent('valid', this);
11655     },
11656     
11657      /**
11658      * Mark this field as invalid
11659      * @param {String} msg The validation message
11660      */
11661     markInvalid : function(msg)
11662     {
11663         if(!this.el  || this.preventMark){ // not rendered
11664             return;
11665         }
11666         
11667         this.el.removeClass([this.invalidClass, this.validClass]);
11668         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11669         
11670         var feedback = this.el.select('.form-control-feedback', true).first();
11671             
11672         if(feedback){
11673             this.el.select('.form-control-feedback', true).first().removeClass(
11674                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11675         }
11676
11677         if(this.disabled){
11678             return;
11679         }
11680         
11681         if(this.allowBlank && !this.getRawValue().length){
11682             return;
11683         }
11684         
11685         if(this.indicator){
11686             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11687             this.indicator.addClass('visible');
11688         }
11689         if (Roo.bootstrap.version == 3) {
11690             this.el.addClass(this.invalidClass);
11691         } else {
11692             this.inputEl().addClass('is-invalid');
11693         }
11694         
11695         
11696         
11697         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11698             
11699             var feedback = this.el.select('.form-control-feedback', true).first();
11700             
11701             if(feedback){
11702                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11703                 
11704                 if(this.getValue().length || this.forceFeedback){
11705                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11706                 }
11707                 
11708             }
11709             
11710         }
11711         
11712         this.fireEvent('invalid', this, msg);
11713     },
11714     // private
11715     SafariOnKeyDown : function(event)
11716     {
11717         // this is a workaround for a password hang bug on chrome/ webkit.
11718         if (this.inputEl().dom.type != 'password') {
11719             return;
11720         }
11721         
11722         var isSelectAll = false;
11723         
11724         if(this.inputEl().dom.selectionEnd > 0){
11725             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11726         }
11727         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11728             event.preventDefault();
11729             this.setValue('');
11730             return;
11731         }
11732         
11733         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11734             
11735             event.preventDefault();
11736             // this is very hacky as keydown always get's upper case.
11737             //
11738             var cc = String.fromCharCode(event.getCharCode());
11739             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11740             
11741         }
11742     },
11743     adjustWidth : function(tag, w){
11744         tag = tag.toLowerCase();
11745         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11746             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11747                 if(tag == 'input'){
11748                     return w + 2;
11749                 }
11750                 if(tag == 'textarea'){
11751                     return w-2;
11752                 }
11753             }else if(Roo.isOpera){
11754                 if(tag == 'input'){
11755                     return w + 2;
11756                 }
11757                 if(tag == 'textarea'){
11758                     return w-2;
11759                 }
11760             }
11761         }
11762         return w;
11763     },
11764     
11765     setFieldLabel : function(v)
11766     {
11767         if(!this.rendered){
11768             return;
11769         }
11770         
11771         if(this.indicatorEl()){
11772             var ar = this.el.select('label > span',true);
11773             
11774             if (ar.elements.length) {
11775                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11776                 this.fieldLabel = v;
11777                 return;
11778             }
11779             
11780             var br = this.el.select('label',true);
11781             
11782             if(br.elements.length) {
11783                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11784                 this.fieldLabel = v;
11785                 return;
11786             }
11787             
11788             Roo.log('Cannot Found any of label > span || label in input');
11789             return;
11790         }
11791         
11792         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11793         this.fieldLabel = v;
11794         
11795         
11796     }
11797 });
11798
11799  
11800 /*
11801  * - LGPL
11802  *
11803  * Input
11804  * 
11805  */
11806
11807 /**
11808  * @class Roo.bootstrap.TextArea
11809  * @extends Roo.bootstrap.Input
11810  * Bootstrap TextArea class
11811  * @cfg {Number} cols Specifies the visible width of a text area
11812  * @cfg {Number} rows Specifies the visible number of lines in a text area
11813  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11814  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11815  * @cfg {string} html text
11816  * 
11817  * @constructor
11818  * Create a new TextArea
11819  * @param {Object} config The config object
11820  */
11821
11822 Roo.bootstrap.TextArea = function(config){
11823     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11824    
11825 };
11826
11827 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11828      
11829     cols : false,
11830     rows : 5,
11831     readOnly : false,
11832     warp : 'soft',
11833     resize : false,
11834     value: false,
11835     html: false,
11836     
11837     getAutoCreate : function(){
11838         
11839         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11840         
11841         var id = Roo.id();
11842         
11843         var cfg = {};
11844         
11845         if(this.inputType != 'hidden'){
11846             cfg.cls = 'form-group' //input-group
11847         }
11848         
11849         var input =  {
11850             tag: 'textarea',
11851             id : id,
11852             warp : this.warp,
11853             rows : this.rows,
11854             value : this.value || '',
11855             html: this.html || '',
11856             cls : 'form-control',
11857             placeholder : this.placeholder || '' 
11858             
11859         };
11860         
11861         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11862             input.maxLength = this.maxLength;
11863         }
11864         
11865         if(this.resize){
11866             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11867         }
11868         
11869         if(this.cols){
11870             input.cols = this.cols;
11871         }
11872         
11873         if (this.readOnly) {
11874             input.readonly = true;
11875         }
11876         
11877         if (this.name) {
11878             input.name = this.name;
11879         }
11880         
11881         if (this.size) {
11882             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11883         }
11884         
11885         var settings=this;
11886         ['xs','sm','md','lg'].map(function(size){
11887             if (settings[size]) {
11888                 cfg.cls += ' col-' + size + '-' + settings[size];
11889             }
11890         });
11891         
11892         var inputblock = input;
11893         
11894         if(this.hasFeedback && !this.allowBlank){
11895             
11896             var feedback = {
11897                 tag: 'span',
11898                 cls: 'glyphicon form-control-feedback'
11899             };
11900
11901             inputblock = {
11902                 cls : 'has-feedback',
11903                 cn :  [
11904                     input,
11905                     feedback
11906                 ] 
11907             };  
11908         }
11909         
11910         
11911         if (this.before || this.after) {
11912             
11913             inputblock = {
11914                 cls : 'input-group',
11915                 cn :  [] 
11916             };
11917             if (this.before) {
11918                 inputblock.cn.push({
11919                     tag :'span',
11920                     cls : 'input-group-addon',
11921                     html : this.before
11922                 });
11923             }
11924             
11925             inputblock.cn.push(input);
11926             
11927             if(this.hasFeedback && !this.allowBlank){
11928                 inputblock.cls += ' has-feedback';
11929                 inputblock.cn.push(feedback);
11930             }
11931             
11932             if (this.after) {
11933                 inputblock.cn.push({
11934                     tag :'span',
11935                     cls : 'input-group-addon',
11936                     html : this.after
11937                 });
11938             }
11939             
11940         }
11941         
11942         if (align ==='left' && this.fieldLabel.length) {
11943             cfg.cn = [
11944                 {
11945                     tag: 'label',
11946                     'for' :  id,
11947                     cls : 'control-label',
11948                     html : this.fieldLabel
11949                 },
11950                 {
11951                     cls : "",
11952                     cn: [
11953                         inputblock
11954                     ]
11955                 }
11956
11957             ];
11958             
11959             if(this.labelWidth > 12){
11960                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11961             }
11962
11963             if(this.labelWidth < 13 && this.labelmd == 0){
11964                 this.labelmd = this.labelWidth;
11965             }
11966
11967             if(this.labellg > 0){
11968                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11969                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11970             }
11971
11972             if(this.labelmd > 0){
11973                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11974                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11975             }
11976
11977             if(this.labelsm > 0){
11978                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11979                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11980             }
11981
11982             if(this.labelxs > 0){
11983                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11984                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11985             }
11986             
11987         } else if ( this.fieldLabel.length) {
11988             cfg.cn = [
11989
11990                {
11991                    tag: 'label',
11992                    //cls : 'input-group-addon',
11993                    html : this.fieldLabel
11994
11995                },
11996
11997                inputblock
11998
11999            ];
12000
12001         } else {
12002
12003             cfg.cn = [
12004
12005                 inputblock
12006
12007             ];
12008                 
12009         }
12010         
12011         if (this.disabled) {
12012             input.disabled=true;
12013         }
12014         
12015         return cfg;
12016         
12017     },
12018     /**
12019      * return the real textarea element.
12020      */
12021     inputEl: function ()
12022     {
12023         return this.el.select('textarea.form-control',true).first();
12024     },
12025     
12026     /**
12027      * Clear any invalid styles/messages for this field
12028      */
12029     clearInvalid : function()
12030     {
12031         
12032         if(!this.el || this.preventMark){ // not rendered
12033             return;
12034         }
12035         
12036         var label = this.el.select('label', true).first();
12037         var icon = this.el.select('i.fa-star', true).first();
12038         
12039         if(label && icon){
12040             icon.remove();
12041         }
12042         this.el.removeClass( this.validClass);
12043         this.inputEl().removeClass('is-invalid');
12044          
12045         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12046             
12047             var feedback = this.el.select('.form-control-feedback', true).first();
12048             
12049             if(feedback){
12050                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12051             }
12052             
12053         }
12054         
12055         this.fireEvent('valid', this);
12056     },
12057     
12058      /**
12059      * Mark this field as valid
12060      */
12061     markValid : function()
12062     {
12063         if(!this.el  || this.preventMark){ // not rendered
12064             return;
12065         }
12066         
12067         this.el.removeClass([this.invalidClass, this.validClass]);
12068         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12069         
12070         var feedback = this.el.select('.form-control-feedback', true).first();
12071             
12072         if(feedback){
12073             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12074         }
12075
12076         if(this.disabled || this.allowBlank){
12077             return;
12078         }
12079         
12080         var label = this.el.select('label', true).first();
12081         var icon = this.el.select('i.fa-star', true).first();
12082         
12083         if(label && icon){
12084             icon.remove();
12085         }
12086         if (Roo.bootstrap.version == 3) {
12087             this.el.addClass(this.validClass);
12088         } else {
12089             this.inputEl().addClass('is-valid');
12090         }
12091         
12092         
12093         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12094             
12095             var feedback = this.el.select('.form-control-feedback', true).first();
12096             
12097             if(feedback){
12098                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12099                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12100             }
12101             
12102         }
12103         
12104         this.fireEvent('valid', this);
12105     },
12106     
12107      /**
12108      * Mark this field as invalid
12109      * @param {String} msg The validation message
12110      */
12111     markInvalid : function(msg)
12112     {
12113         if(!this.el  || this.preventMark){ // not rendered
12114             return;
12115         }
12116         
12117         this.el.removeClass([this.invalidClass, this.validClass]);
12118         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12119         
12120         var feedback = this.el.select('.form-control-feedback', true).first();
12121             
12122         if(feedback){
12123             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12124         }
12125
12126         if(this.disabled || this.allowBlank){
12127             return;
12128         }
12129         
12130         var label = this.el.select('label', true).first();
12131         var icon = this.el.select('i.fa-star', true).first();
12132         
12133         if(!this.getValue().length && label && !icon){
12134             this.el.createChild({
12135                 tag : 'i',
12136                 cls : 'text-danger fa fa-lg fa-star',
12137                 tooltip : 'This field is required',
12138                 style : 'margin-right:5px;'
12139             }, label, true);
12140         }
12141         
12142         if (Roo.bootstrap.version == 3) {
12143             this.el.addClass(this.invalidClass);
12144         } else {
12145             this.inputEl().addClass('is-invalid');
12146         }
12147         
12148         // fixme ... this may be depricated need to test..
12149         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12150             
12151             var feedback = this.el.select('.form-control-feedback', true).first();
12152             
12153             if(feedback){
12154                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12155                 
12156                 if(this.getValue().length || this.forceFeedback){
12157                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12158                 }
12159                 
12160             }
12161             
12162         }
12163         
12164         this.fireEvent('invalid', this, msg);
12165     }
12166 });
12167
12168  
12169 /*
12170  * - LGPL
12171  *
12172  * trigger field - base class for combo..
12173  * 
12174  */
12175  
12176 /**
12177  * @class Roo.bootstrap.TriggerField
12178  * @extends Roo.bootstrap.Input
12179  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12180  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12181  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12182  * for which you can provide a custom implementation.  For example:
12183  * <pre><code>
12184 var trigger = new Roo.bootstrap.TriggerField();
12185 trigger.onTriggerClick = myTriggerFn;
12186 trigger.applyTo('my-field');
12187 </code></pre>
12188  *
12189  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12190  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12191  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12192  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12193  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12194
12195  * @constructor
12196  * Create a new TriggerField.
12197  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12198  * to the base TextField)
12199  */
12200 Roo.bootstrap.TriggerField = function(config){
12201     this.mimicing = false;
12202     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12203 };
12204
12205 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12206     /**
12207      * @cfg {String} triggerClass A CSS class to apply to the trigger
12208      */
12209      /**
12210      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12211      */
12212     hideTrigger:false,
12213
12214     /**
12215      * @cfg {Boolean} removable (true|false) special filter default false
12216      */
12217     removable : false,
12218     
12219     /** @cfg {Boolean} grow @hide */
12220     /** @cfg {Number} growMin @hide */
12221     /** @cfg {Number} growMax @hide */
12222
12223     /**
12224      * @hide 
12225      * @method
12226      */
12227     autoSize: Roo.emptyFn,
12228     // private
12229     monitorTab : true,
12230     // private
12231     deferHeight : true,
12232
12233     
12234     actionMode : 'wrap',
12235     
12236     caret : false,
12237     
12238     
12239     getAutoCreate : function(){
12240        
12241         var align = this.labelAlign || this.parentLabelAlign();
12242         
12243         var id = Roo.id();
12244         
12245         var cfg = {
12246             cls: 'form-group' //input-group
12247         };
12248         
12249         
12250         var input =  {
12251             tag: 'input',
12252             id : id,
12253             type : this.inputType,
12254             cls : 'form-control',
12255             autocomplete: 'new-password',
12256             placeholder : this.placeholder || '' 
12257             
12258         };
12259         if (this.name) {
12260             input.name = this.name;
12261         }
12262         if (this.size) {
12263             input.cls += ' input-' + this.size;
12264         }
12265         
12266         if (this.disabled) {
12267             input.disabled=true;
12268         }
12269         
12270         var inputblock = input;
12271         
12272         if(this.hasFeedback && !this.allowBlank){
12273             
12274             var feedback = {
12275                 tag: 'span',
12276                 cls: 'glyphicon form-control-feedback'
12277             };
12278             
12279             if(this.removable && !this.editable  ){
12280                 inputblock = {
12281                     cls : 'has-feedback',
12282                     cn :  [
12283                         inputblock,
12284                         {
12285                             tag: 'button',
12286                             html : 'x',
12287                             cls : 'roo-combo-removable-btn close'
12288                         },
12289                         feedback
12290                     ] 
12291                 };
12292             } else {
12293                 inputblock = {
12294                     cls : 'has-feedback',
12295                     cn :  [
12296                         inputblock,
12297                         feedback
12298                     ] 
12299                 };
12300             }
12301
12302         } else {
12303             if(this.removable && !this.editable ){
12304                 inputblock = {
12305                     cls : 'roo-removable',
12306                     cn :  [
12307                         inputblock,
12308                         {
12309                             tag: 'button',
12310                             html : 'x',
12311                             cls : 'roo-combo-removable-btn close'
12312                         }
12313                     ] 
12314                 };
12315             }
12316         }
12317         
12318         if (this.before || this.after) {
12319             
12320             inputblock = {
12321                 cls : 'input-group',
12322                 cn :  [] 
12323             };
12324             if (this.before) {
12325                 inputblock.cn.push({
12326                     tag :'span',
12327                     cls : 'input-group-addon input-group-prepend input-group-text',
12328                     html : this.before
12329                 });
12330             }
12331             
12332             inputblock.cn.push(input);
12333             
12334             if(this.hasFeedback && !this.allowBlank){
12335                 inputblock.cls += ' has-feedback';
12336                 inputblock.cn.push(feedback);
12337             }
12338             
12339             if (this.after) {
12340                 inputblock.cn.push({
12341                     tag :'span',
12342                     cls : 'input-group-addon input-group-append input-group-text',
12343                     html : this.after
12344                 });
12345             }
12346             
12347         };
12348         
12349       
12350         
12351         var ibwrap = inputblock;
12352         
12353         if(this.multiple){
12354             ibwrap = {
12355                 tag: 'ul',
12356                 cls: 'roo-select2-choices',
12357                 cn:[
12358                     {
12359                         tag: 'li',
12360                         cls: 'roo-select2-search-field',
12361                         cn: [
12362
12363                             inputblock
12364                         ]
12365                     }
12366                 ]
12367             };
12368                 
12369         }
12370         
12371         var combobox = {
12372             cls: 'roo-select2-container input-group',
12373             cn: [
12374                  {
12375                     tag: 'input',
12376                     type : 'hidden',
12377                     cls: 'form-hidden-field'
12378                 },
12379                 ibwrap
12380             ]
12381         };
12382         
12383         if(!this.multiple && this.showToggleBtn){
12384             
12385             var caret = {
12386                         tag: 'span',
12387                         cls: 'caret'
12388              };
12389             if (this.caret != false) {
12390                 caret = {
12391                      tag: 'i',
12392                      cls: 'fa fa-' + this.caret
12393                 };
12394                 
12395             }
12396             
12397             combobox.cn.push({
12398                 tag :'span',
12399                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12400                 cn : [
12401                     Roo.bootstrap.version == 3 ? caret : '',
12402                     {
12403                         tag: 'span',
12404                         cls: 'combobox-clear',
12405                         cn  : [
12406                             {
12407                                 tag : 'i',
12408                                 cls: 'icon-remove'
12409                             }
12410                         ]
12411                     }
12412                 ]
12413
12414             })
12415         }
12416         
12417         if(this.multiple){
12418             combobox.cls += ' roo-select2-container-multi';
12419         }
12420          var indicator = {
12421             tag : 'i',
12422             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12423             tooltip : 'This field is required'
12424         };
12425         if (Roo.bootstrap.version == 4) {
12426             indicator = {
12427                 tag : 'i',
12428                 style : 'display:none'
12429             };
12430         }
12431         
12432         
12433         if (align ==='left' && this.fieldLabel.length) {
12434             
12435             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12436
12437             cfg.cn = [
12438                 indicator,
12439                 {
12440                     tag: 'label',
12441                     'for' :  id,
12442                     cls : 'control-label',
12443                     html : this.fieldLabel
12444
12445                 },
12446                 {
12447                     cls : "", 
12448                     cn: [
12449                         combobox
12450                     ]
12451                 }
12452
12453             ];
12454             
12455             var labelCfg = cfg.cn[1];
12456             var contentCfg = cfg.cn[2];
12457             
12458             if(this.indicatorpos == 'right'){
12459                 cfg.cn = [
12460                     {
12461                         tag: 'label',
12462                         'for' :  id,
12463                         cls : 'control-label',
12464                         cn : [
12465                             {
12466                                 tag : 'span',
12467                                 html : this.fieldLabel
12468                             },
12469                             indicator
12470                         ]
12471                     },
12472                     {
12473                         cls : "", 
12474                         cn: [
12475                             combobox
12476                         ]
12477                     }
12478
12479                 ];
12480                 
12481                 labelCfg = cfg.cn[0];
12482                 contentCfg = cfg.cn[1];
12483             }
12484             
12485             if(this.labelWidth > 12){
12486                 labelCfg.style = "width: " + this.labelWidth + 'px';
12487             }
12488             
12489             if(this.labelWidth < 13 && this.labelmd == 0){
12490                 this.labelmd = this.labelWidth;
12491             }
12492             
12493             if(this.labellg > 0){
12494                 labelCfg.cls += ' col-lg-' + this.labellg;
12495                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12496             }
12497             
12498             if(this.labelmd > 0){
12499                 labelCfg.cls += ' col-md-' + this.labelmd;
12500                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12501             }
12502             
12503             if(this.labelsm > 0){
12504                 labelCfg.cls += ' col-sm-' + this.labelsm;
12505                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12506             }
12507             
12508             if(this.labelxs > 0){
12509                 labelCfg.cls += ' col-xs-' + this.labelxs;
12510                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12511             }
12512             
12513         } else if ( this.fieldLabel.length) {
12514 //                Roo.log(" label");
12515             cfg.cn = [
12516                 indicator,
12517                {
12518                    tag: 'label',
12519                    //cls : 'input-group-addon',
12520                    html : this.fieldLabel
12521
12522                },
12523
12524                combobox
12525
12526             ];
12527             
12528             if(this.indicatorpos == 'right'){
12529                 
12530                 cfg.cn = [
12531                     {
12532                        tag: 'label',
12533                        cn : [
12534                            {
12535                                tag : 'span',
12536                                html : this.fieldLabel
12537                            },
12538                            indicator
12539                        ]
12540
12541                     },
12542                     combobox
12543
12544                 ];
12545
12546             }
12547
12548         } else {
12549             
12550 //                Roo.log(" no label && no align");
12551                 cfg = combobox
12552                      
12553                 
12554         }
12555         
12556         var settings=this;
12557         ['xs','sm','md','lg'].map(function(size){
12558             if (settings[size]) {
12559                 cfg.cls += ' col-' + size + '-' + settings[size];
12560             }
12561         });
12562         
12563         return cfg;
12564         
12565     },
12566     
12567     
12568     
12569     // private
12570     onResize : function(w, h){
12571 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12572 //        if(typeof w == 'number'){
12573 //            var x = w - this.trigger.getWidth();
12574 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12575 //            this.trigger.setStyle('left', x+'px');
12576 //        }
12577     },
12578
12579     // private
12580     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12581
12582     // private
12583     getResizeEl : function(){
12584         return this.inputEl();
12585     },
12586
12587     // private
12588     getPositionEl : function(){
12589         return this.inputEl();
12590     },
12591
12592     // private
12593     alignErrorIcon : function(){
12594         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12595     },
12596
12597     // private
12598     initEvents : function(){
12599         
12600         this.createList();
12601         
12602         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12603         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12604         if(!this.multiple && this.showToggleBtn){
12605             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12606             if(this.hideTrigger){
12607                 this.trigger.setDisplayed(false);
12608             }
12609             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12610         }
12611         
12612         if(this.multiple){
12613             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12614         }
12615         
12616         if(this.removable && !this.editable && !this.tickable){
12617             var close = this.closeTriggerEl();
12618             
12619             if(close){
12620                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12621                 close.on('click', this.removeBtnClick, this, close);
12622             }
12623         }
12624         
12625         //this.trigger.addClassOnOver('x-form-trigger-over');
12626         //this.trigger.addClassOnClick('x-form-trigger-click');
12627         
12628         //if(!this.width){
12629         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12630         //}
12631     },
12632     
12633     closeTriggerEl : function()
12634     {
12635         var close = this.el.select('.roo-combo-removable-btn', true).first();
12636         return close ? close : false;
12637     },
12638     
12639     removeBtnClick : function(e, h, el)
12640     {
12641         e.preventDefault();
12642         
12643         if(this.fireEvent("remove", this) !== false){
12644             this.reset();
12645             this.fireEvent("afterremove", this)
12646         }
12647     },
12648     
12649     createList : function()
12650     {
12651         this.list = Roo.get(document.body).createChild({
12652             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12653             cls: 'typeahead typeahead-long dropdown-menu shadow',
12654             style: 'display:none'
12655         });
12656         
12657         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12658         
12659     },
12660
12661     // private
12662     initTrigger : function(){
12663        
12664     },
12665
12666     // private
12667     onDestroy : function(){
12668         if(this.trigger){
12669             this.trigger.removeAllListeners();
12670           //  this.trigger.remove();
12671         }
12672         //if(this.wrap){
12673         //    this.wrap.remove();
12674         //}
12675         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12676     },
12677
12678     // private
12679     onFocus : function(){
12680         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12681         /*
12682         if(!this.mimicing){
12683             this.wrap.addClass('x-trigger-wrap-focus');
12684             this.mimicing = true;
12685             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12686             if(this.monitorTab){
12687                 this.el.on("keydown", this.checkTab, this);
12688             }
12689         }
12690         */
12691     },
12692
12693     // private
12694     checkTab : function(e){
12695         if(e.getKey() == e.TAB){
12696             this.triggerBlur();
12697         }
12698     },
12699
12700     // private
12701     onBlur : function(){
12702         // do nothing
12703     },
12704
12705     // private
12706     mimicBlur : function(e, t){
12707         /*
12708         if(!this.wrap.contains(t) && this.validateBlur()){
12709             this.triggerBlur();
12710         }
12711         */
12712     },
12713
12714     // private
12715     triggerBlur : function(){
12716         this.mimicing = false;
12717         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12718         if(this.monitorTab){
12719             this.el.un("keydown", this.checkTab, this);
12720         }
12721         //this.wrap.removeClass('x-trigger-wrap-focus');
12722         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12723     },
12724
12725     // private
12726     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12727     validateBlur : function(e, t){
12728         return true;
12729     },
12730
12731     // private
12732     onDisable : function(){
12733         this.inputEl().dom.disabled = true;
12734         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12735         //if(this.wrap){
12736         //    this.wrap.addClass('x-item-disabled');
12737         //}
12738     },
12739
12740     // private
12741     onEnable : function(){
12742         this.inputEl().dom.disabled = false;
12743         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12744         //if(this.wrap){
12745         //    this.el.removeClass('x-item-disabled');
12746         //}
12747     },
12748
12749     // private
12750     onShow : function(){
12751         var ae = this.getActionEl();
12752         
12753         if(ae){
12754             ae.dom.style.display = '';
12755             ae.dom.style.visibility = 'visible';
12756         }
12757     },
12758
12759     // private
12760     
12761     onHide : function(){
12762         var ae = this.getActionEl();
12763         ae.dom.style.display = 'none';
12764     },
12765
12766     /**
12767      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12768      * by an implementing function.
12769      * @method
12770      * @param {EventObject} e
12771      */
12772     onTriggerClick : Roo.emptyFn
12773 });
12774  
12775 /*
12776 * Licence: LGPL
12777 */
12778
12779 /**
12780  * @class Roo.bootstrap.CardUploader
12781  * @extends Roo.bootstrap.Button
12782  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12783  * @cfg {Number} errorTimeout default 3000
12784  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12785  * @cfg {Array}  html The button text.
12786
12787  *
12788  * @constructor
12789  * Create a new CardUploader
12790  * @param {Object} config The config object
12791  */
12792
12793 Roo.bootstrap.CardUploader = function(config){
12794     
12795  
12796     
12797     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12798     
12799     
12800     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12801         return r.data.id
12802      });
12803     
12804      this.addEvents({
12805          // raw events
12806         /**
12807          * @event preview
12808          * When a image is clicked on - and needs to display a slideshow or similar..
12809          * @param {Roo.bootstrap.Card} this
12810          * @param {Object} The image information data 
12811          *
12812          */
12813         'preview' : true,
12814          /**
12815          * @event download
12816          * When a the download link is clicked
12817          * @param {Roo.bootstrap.Card} this
12818          * @param {Object} The image information data  contains 
12819          */
12820         'download' : true
12821         
12822     });
12823 };
12824  
12825 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12826     
12827      
12828     errorTimeout : 3000,
12829      
12830     images : false,
12831    
12832     fileCollection : false,
12833     allowBlank : true,
12834     
12835     getAutoCreate : function()
12836     {
12837         
12838         var cfg =  {
12839             cls :'form-group' ,
12840             cn : [
12841                
12842                 {
12843                     tag: 'label',
12844                    //cls : 'input-group-addon',
12845                     html : this.fieldLabel
12846
12847                 },
12848
12849                 {
12850                     tag: 'input',
12851                     type : 'hidden',
12852                     name : this.name,
12853                     value : this.value,
12854                     cls : 'd-none  form-control'
12855                 },
12856                 
12857                 {
12858                     tag: 'input',
12859                     multiple : 'multiple',
12860                     type : 'file',
12861                     cls : 'd-none  roo-card-upload-selector'
12862                 },
12863                 
12864                 {
12865                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12866                 },
12867                 {
12868                     cls : 'card-columns roo-card-uploader-container'
12869                 }
12870
12871             ]
12872         };
12873            
12874          
12875         return cfg;
12876     },
12877     
12878     getChildContainer : function() /// what children are added to.
12879     {
12880         return this.containerEl;
12881     },
12882    
12883     getButtonContainer : function() /// what children are added to.
12884     {
12885         return this.el.select(".roo-card-uploader-button-container").first();
12886     },
12887    
12888     initEvents : function()
12889     {
12890         
12891         Roo.bootstrap.Input.prototype.initEvents.call(this);
12892         
12893         var t = this;
12894         this.addxtype({
12895             xns: Roo.bootstrap,
12896
12897             xtype : 'Button',
12898             container_method : 'getButtonContainer' ,            
12899             html :  this.html, // fix changable?
12900             cls : 'w-100 ',
12901             listeners : {
12902                 'click' : function(btn, e) {
12903                     t.onClick(e);
12904                 }
12905             }
12906         });
12907         
12908         
12909         
12910         
12911         this.urlAPI = (window.createObjectURL && window) || 
12912                                 (window.URL && URL.revokeObjectURL && URL) || 
12913                                 (window.webkitURL && webkitURL);
12914                         
12915          
12916          
12917          
12918         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12919         
12920         this.selectorEl.on('change', this.onFileSelected, this);
12921         if (this.images) {
12922             var t = this;
12923             this.images.forEach(function(img) {
12924                 t.addCard(img)
12925             });
12926             this.images = false;
12927         }
12928         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12929          
12930        
12931     },
12932     
12933    
12934     onClick : function(e)
12935     {
12936         e.preventDefault();
12937          
12938         this.selectorEl.dom.click();
12939          
12940     },
12941     
12942     onFileSelected : function(e)
12943     {
12944         e.preventDefault();
12945         
12946         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12947             return;
12948         }
12949         
12950         Roo.each(this.selectorEl.dom.files, function(file){    
12951             this.addFile(file);
12952         }, this);
12953          
12954     },
12955     
12956       
12957     
12958       
12959     
12960     addFile : function(file)
12961     {
12962            
12963         if(typeof(file) === 'string'){
12964             throw "Add file by name?"; // should not happen
12965             return;
12966         }
12967         
12968         if(!file || !this.urlAPI){
12969             return;
12970         }
12971         
12972         // file;
12973         // file.type;
12974         
12975         var _this = this;
12976         
12977         
12978         var url = _this.urlAPI.createObjectURL( file);
12979            
12980         this.addCard({
12981             id : Roo.bootstrap.CardUploader.ID--,
12982             is_uploaded : false,
12983             src : url,
12984             srcfile : file,
12985             title : file.name,
12986             mimetype : file.type,
12987             preview : false,
12988             is_deleted : 0
12989         });
12990         
12991     },
12992     
12993     /**
12994      * addCard - add an Attachment to the uploader
12995      * @param data - the data about the image to upload
12996      *
12997      * {
12998           id : 123
12999           title : "Title of file",
13000           is_uploaded : false,
13001           src : "http://.....",
13002           srcfile : { the File upload object },
13003           mimetype : file.type,
13004           preview : false,
13005           is_deleted : 0
13006           .. any other data...
13007         }
13008      *
13009      * 
13010     */
13011     
13012     addCard : function (data)
13013     {
13014         // hidden input element?
13015         // if the file is not an image...
13016         //then we need to use something other that and header_image
13017         var t = this;
13018         //   remove.....
13019         var footer = [
13020             {
13021                 xns : Roo.bootstrap,
13022                 xtype : 'CardFooter',
13023                  items: [
13024                     {
13025                         xns : Roo.bootstrap,
13026                         xtype : 'Element',
13027                         cls : 'd-flex',
13028                         items : [
13029                             
13030                             {
13031                                 xns : Roo.bootstrap,
13032                                 xtype : 'Button',
13033                                 html : String.format("<small>{0}</small>", data.title),
13034                                 cls : 'col-10 text-left',
13035                                 size: 'sm',
13036                                 weight: 'link',
13037                                 fa : 'download',
13038                                 listeners : {
13039                                     click : function() {
13040                                      
13041                                         t.fireEvent( "download", t, data );
13042                                     }
13043                                 }
13044                             },
13045                           
13046                             {
13047                                 xns : Roo.bootstrap,
13048                                 xtype : 'Button',
13049                                 style: 'max-height: 28px; ',
13050                                 size : 'sm',
13051                                 weight: 'danger',
13052                                 cls : 'col-2',
13053                                 fa : 'times',
13054                                 listeners : {
13055                                     click : function() {
13056                                         t.removeCard(data.id)
13057                                     }
13058                                 }
13059                             }
13060                         ]
13061                     }
13062                     
13063                 ] 
13064             }
13065             
13066         ];
13067         
13068         var cn = this.addxtype(
13069             {
13070                  
13071                 xns : Roo.bootstrap,
13072                 xtype : 'Card',
13073                 closeable : true,
13074                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13075                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13076                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13077                 data : data,
13078                 html : false,
13079                  
13080                 items : footer,
13081                 initEvents : function() {
13082                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13083                     var card = this;
13084                     this.imgEl = this.el.select('.card-img-top').first();
13085                     if (this.imgEl) {
13086                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13087                         this.imgEl.set({ 'pointer' : 'cursor' });
13088                                   
13089                     }
13090                     this.getCardFooter().addClass('p-1');
13091                     
13092                   
13093                 }
13094                 
13095             }
13096         );
13097         // dont' really need ot update items.
13098         // this.items.push(cn);
13099         this.fileCollection.add(cn);
13100         
13101         if (!data.srcfile) {
13102             this.updateInput();
13103             return;
13104         }
13105             
13106         var _t = this;
13107         var reader = new FileReader();
13108         reader.addEventListener("load", function() {  
13109             data.srcdata =  reader.result;
13110             _t.updateInput();
13111         });
13112         reader.readAsDataURL(data.srcfile);
13113         
13114         
13115         
13116     },
13117     removeCard : function(id)
13118     {
13119         
13120         var card  = this.fileCollection.get(id);
13121         card.data.is_deleted = 1;
13122         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13123         //this.fileCollection.remove(card);
13124         //this.items = this.items.filter(function(e) { return e != card });
13125         // dont' really need ot update items.
13126         card.el.dom.parentNode.removeChild(card.el.dom);
13127         this.updateInput();
13128
13129         
13130     },
13131     reset: function()
13132     {
13133         this.fileCollection.each(function(card) {
13134             if (card.el.dom && card.el.dom.parentNode) {
13135                 card.el.dom.parentNode.removeChild(card.el.dom);
13136             }
13137         });
13138         this.fileCollection.clear();
13139         this.updateInput();
13140     },
13141     
13142     updateInput : function()
13143     {
13144          var data = [];
13145         this.fileCollection.each(function(e) {
13146             data.push(e.data);
13147             
13148         });
13149         this.inputEl().dom.value = JSON.stringify(data);
13150         
13151         
13152         
13153     }
13154     
13155     
13156 });
13157
13158
13159 Roo.bootstrap.CardUploader.ID = -1;/*
13160  * Based on:
13161  * Ext JS Library 1.1.1
13162  * Copyright(c) 2006-2007, Ext JS, LLC.
13163  *
13164  * Originally Released Under LGPL - original licence link has changed is not relivant.
13165  *
13166  * Fork - LGPL
13167  * <script type="text/javascript">
13168  */
13169
13170
13171 /**
13172  * @class Roo.data.SortTypes
13173  * @singleton
13174  * Defines the default sorting (casting?) comparison functions used when sorting data.
13175  */
13176 Roo.data.SortTypes = {
13177     /**
13178      * Default sort that does nothing
13179      * @param {Mixed} s The value being converted
13180      * @return {Mixed} The comparison value
13181      */
13182     none : function(s){
13183         return s;
13184     },
13185     
13186     /**
13187      * The regular expression used to strip tags
13188      * @type {RegExp}
13189      * @property
13190      */
13191     stripTagsRE : /<\/?[^>]+>/gi,
13192     
13193     /**
13194      * Strips all HTML tags to sort on text only
13195      * @param {Mixed} s The value being converted
13196      * @return {String} The comparison value
13197      */
13198     asText : function(s){
13199         return String(s).replace(this.stripTagsRE, "");
13200     },
13201     
13202     /**
13203      * Strips all HTML tags to sort on text only - Case insensitive
13204      * @param {Mixed} s The value being converted
13205      * @return {String} The comparison value
13206      */
13207     asUCText : function(s){
13208         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13209     },
13210     
13211     /**
13212      * Case insensitive string
13213      * @param {Mixed} s The value being converted
13214      * @return {String} The comparison value
13215      */
13216     asUCString : function(s) {
13217         return String(s).toUpperCase();
13218     },
13219     
13220     /**
13221      * Date sorting
13222      * @param {Mixed} s The value being converted
13223      * @return {Number} The comparison value
13224      */
13225     asDate : function(s) {
13226         if(!s){
13227             return 0;
13228         }
13229         if(s instanceof Date){
13230             return s.getTime();
13231         }
13232         return Date.parse(String(s));
13233     },
13234     
13235     /**
13236      * Float sorting
13237      * @param {Mixed} s The value being converted
13238      * @return {Float} The comparison value
13239      */
13240     asFloat : function(s) {
13241         var val = parseFloat(String(s).replace(/,/g, ""));
13242         if(isNaN(val)) {
13243             val = 0;
13244         }
13245         return val;
13246     },
13247     
13248     /**
13249      * Integer sorting
13250      * @param {Mixed} s The value being converted
13251      * @return {Number} The comparison value
13252      */
13253     asInt : function(s) {
13254         var val = parseInt(String(s).replace(/,/g, ""));
13255         if(isNaN(val)) {
13256             val = 0;
13257         }
13258         return val;
13259     }
13260 };/*
13261  * Based on:
13262  * Ext JS Library 1.1.1
13263  * Copyright(c) 2006-2007, Ext JS, LLC.
13264  *
13265  * Originally Released Under LGPL - original licence link has changed is not relivant.
13266  *
13267  * Fork - LGPL
13268  * <script type="text/javascript">
13269  */
13270
13271 /**
13272 * @class Roo.data.Record
13273  * Instances of this class encapsulate both record <em>definition</em> information, and record
13274  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13275  * to access Records cached in an {@link Roo.data.Store} object.<br>
13276  * <p>
13277  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13278  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13279  * objects.<br>
13280  * <p>
13281  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13282  * @constructor
13283  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13284  * {@link #create}. The parameters are the same.
13285  * @param {Array} data An associative Array of data values keyed by the field name.
13286  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13287  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13288  * not specified an integer id is generated.
13289  */
13290 Roo.data.Record = function(data, id){
13291     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13292     this.data = data;
13293 };
13294
13295 /**
13296  * Generate a constructor for a specific record layout.
13297  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13298  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13299  * Each field definition object may contain the following properties: <ul>
13300  * <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,
13301  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13302  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13303  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13304  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13305  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13306  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13307  * this may be omitted.</p></li>
13308  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13309  * <ul><li>auto (Default, implies no conversion)</li>
13310  * <li>string</li>
13311  * <li>int</li>
13312  * <li>float</li>
13313  * <li>boolean</li>
13314  * <li>date</li></ul></p></li>
13315  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13316  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13317  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13318  * by the Reader into an object that will be stored in the Record. It is passed the
13319  * following parameters:<ul>
13320  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13321  * </ul></p></li>
13322  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13323  * </ul>
13324  * <br>usage:<br><pre><code>
13325 var TopicRecord = Roo.data.Record.create(
13326     {name: 'title', mapping: 'topic_title'},
13327     {name: 'author', mapping: 'username'},
13328     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13329     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13330     {name: 'lastPoster', mapping: 'user2'},
13331     {name: 'excerpt', mapping: 'post_text'}
13332 );
13333
13334 var myNewRecord = new TopicRecord({
13335     title: 'Do my job please',
13336     author: 'noobie',
13337     totalPosts: 1,
13338     lastPost: new Date(),
13339     lastPoster: 'Animal',
13340     excerpt: 'No way dude!'
13341 });
13342 myStore.add(myNewRecord);
13343 </code></pre>
13344  * @method create
13345  * @static
13346  */
13347 Roo.data.Record.create = function(o){
13348     var f = function(){
13349         f.superclass.constructor.apply(this, arguments);
13350     };
13351     Roo.extend(f, Roo.data.Record);
13352     var p = f.prototype;
13353     p.fields = new Roo.util.MixedCollection(false, function(field){
13354         return field.name;
13355     });
13356     for(var i = 0, len = o.length; i < len; i++){
13357         p.fields.add(new Roo.data.Field(o[i]));
13358     }
13359     f.getField = function(name){
13360         return p.fields.get(name);  
13361     };
13362     return f;
13363 };
13364
13365 Roo.data.Record.AUTO_ID = 1000;
13366 Roo.data.Record.EDIT = 'edit';
13367 Roo.data.Record.REJECT = 'reject';
13368 Roo.data.Record.COMMIT = 'commit';
13369
13370 Roo.data.Record.prototype = {
13371     /**
13372      * Readonly flag - true if this record has been modified.
13373      * @type Boolean
13374      */
13375     dirty : false,
13376     editing : false,
13377     error: null,
13378     modified: null,
13379
13380     // private
13381     join : function(store){
13382         this.store = store;
13383     },
13384
13385     /**
13386      * Set the named field to the specified value.
13387      * @param {String} name The name of the field to set.
13388      * @param {Object} value The value to set the field to.
13389      */
13390     set : function(name, value){
13391         if(this.data[name] == value){
13392             return;
13393         }
13394         this.dirty = true;
13395         if(!this.modified){
13396             this.modified = {};
13397         }
13398         if(typeof this.modified[name] == 'undefined'){
13399             this.modified[name] = this.data[name];
13400         }
13401         this.data[name] = value;
13402         if(!this.editing && this.store){
13403             this.store.afterEdit(this);
13404         }       
13405     },
13406
13407     /**
13408      * Get the value of the named field.
13409      * @param {String} name The name of the field to get the value of.
13410      * @return {Object} The value of the field.
13411      */
13412     get : function(name){
13413         return this.data[name]; 
13414     },
13415
13416     // private
13417     beginEdit : function(){
13418         this.editing = true;
13419         this.modified = {}; 
13420     },
13421
13422     // private
13423     cancelEdit : function(){
13424         this.editing = false;
13425         delete this.modified;
13426     },
13427
13428     // private
13429     endEdit : function(){
13430         this.editing = false;
13431         if(this.dirty && this.store){
13432             this.store.afterEdit(this);
13433         }
13434     },
13435
13436     /**
13437      * Usually called by the {@link Roo.data.Store} which owns the Record.
13438      * Rejects all changes made to the Record since either creation, or the last commit operation.
13439      * Modified fields are reverted to their original values.
13440      * <p>
13441      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13442      * of reject operations.
13443      */
13444     reject : function(){
13445         var m = this.modified;
13446         for(var n in m){
13447             if(typeof m[n] != "function"){
13448                 this.data[n] = m[n];
13449             }
13450         }
13451         this.dirty = false;
13452         delete this.modified;
13453         this.editing = false;
13454         if(this.store){
13455             this.store.afterReject(this);
13456         }
13457     },
13458
13459     /**
13460      * Usually called by the {@link Roo.data.Store} which owns the Record.
13461      * Commits all changes made to the Record since either creation, or the last commit operation.
13462      * <p>
13463      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13464      * of commit operations.
13465      */
13466     commit : function(){
13467         this.dirty = false;
13468         delete this.modified;
13469         this.editing = false;
13470         if(this.store){
13471             this.store.afterCommit(this);
13472         }
13473     },
13474
13475     // private
13476     hasError : function(){
13477         return this.error != null;
13478     },
13479
13480     // private
13481     clearError : function(){
13482         this.error = null;
13483     },
13484
13485     /**
13486      * Creates a copy of this record.
13487      * @param {String} id (optional) A new record id if you don't want to use this record's id
13488      * @return {Record}
13489      */
13490     copy : function(newId) {
13491         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13492     }
13493 };/*
13494  * Based on:
13495  * Ext JS Library 1.1.1
13496  * Copyright(c) 2006-2007, Ext JS, LLC.
13497  *
13498  * Originally Released Under LGPL - original licence link has changed is not relivant.
13499  *
13500  * Fork - LGPL
13501  * <script type="text/javascript">
13502  */
13503
13504
13505
13506 /**
13507  * @class Roo.data.Store
13508  * @extends Roo.util.Observable
13509  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13510  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13511  * <p>
13512  * 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
13513  * has no knowledge of the format of the data returned by the Proxy.<br>
13514  * <p>
13515  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13516  * instances from the data object. These records are cached and made available through accessor functions.
13517  * @constructor
13518  * Creates a new Store.
13519  * @param {Object} config A config object containing the objects needed for the Store to access data,
13520  * and read the data into Records.
13521  */
13522 Roo.data.Store = function(config){
13523     this.data = new Roo.util.MixedCollection(false);
13524     this.data.getKey = function(o){
13525         return o.id;
13526     };
13527     this.baseParams = {};
13528     // private
13529     this.paramNames = {
13530         "start" : "start",
13531         "limit" : "limit",
13532         "sort" : "sort",
13533         "dir" : "dir",
13534         "multisort" : "_multisort"
13535     };
13536
13537     if(config && config.data){
13538         this.inlineData = config.data;
13539         delete config.data;
13540     }
13541
13542     Roo.apply(this, config);
13543     
13544     if(this.reader){ // reader passed
13545         this.reader = Roo.factory(this.reader, Roo.data);
13546         this.reader.xmodule = this.xmodule || false;
13547         if(!this.recordType){
13548             this.recordType = this.reader.recordType;
13549         }
13550         if(this.reader.onMetaChange){
13551             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13552         }
13553     }
13554
13555     if(this.recordType){
13556         this.fields = this.recordType.prototype.fields;
13557     }
13558     this.modified = [];
13559
13560     this.addEvents({
13561         /**
13562          * @event datachanged
13563          * Fires when the data cache has changed, and a widget which is using this Store
13564          * as a Record cache should refresh its view.
13565          * @param {Store} this
13566          */
13567         datachanged : true,
13568         /**
13569          * @event metachange
13570          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13571          * @param {Store} this
13572          * @param {Object} meta The JSON metadata
13573          */
13574         metachange : true,
13575         /**
13576          * @event add
13577          * Fires when Records have been added to the Store
13578          * @param {Store} this
13579          * @param {Roo.data.Record[]} records The array of Records added
13580          * @param {Number} index The index at which the record(s) were added
13581          */
13582         add : true,
13583         /**
13584          * @event remove
13585          * Fires when a Record has been removed from the Store
13586          * @param {Store} this
13587          * @param {Roo.data.Record} record The Record that was removed
13588          * @param {Number} index The index at which the record was removed
13589          */
13590         remove : true,
13591         /**
13592          * @event update
13593          * Fires when a Record has been updated
13594          * @param {Store} this
13595          * @param {Roo.data.Record} record The Record that was updated
13596          * @param {String} operation The update operation being performed.  Value may be one of:
13597          * <pre><code>
13598  Roo.data.Record.EDIT
13599  Roo.data.Record.REJECT
13600  Roo.data.Record.COMMIT
13601          * </code></pre>
13602          */
13603         update : true,
13604         /**
13605          * @event clear
13606          * Fires when the data cache has been cleared.
13607          * @param {Store} this
13608          */
13609         clear : true,
13610         /**
13611          * @event beforeload
13612          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13613          * the load action will be canceled.
13614          * @param {Store} this
13615          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13616          */
13617         beforeload : true,
13618         /**
13619          * @event beforeloadadd
13620          * Fires after a new set of Records has been loaded.
13621          * @param {Store} this
13622          * @param {Roo.data.Record[]} records The Records that were loaded
13623          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13624          */
13625         beforeloadadd : true,
13626         /**
13627          * @event load
13628          * Fires after a new set of Records has been loaded, before they are added to the store.
13629          * @param {Store} this
13630          * @param {Roo.data.Record[]} records The Records that were loaded
13631          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13632          * @params {Object} return from reader
13633          */
13634         load : true,
13635         /**
13636          * @event loadexception
13637          * Fires if an exception occurs in the Proxy during loading.
13638          * Called with the signature of the Proxy's "loadexception" event.
13639          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13640          * 
13641          * @param {Proxy} 
13642          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13643          * @param {Object} load options 
13644          * @param {Object} jsonData from your request (normally this contains the Exception)
13645          */
13646         loadexception : true
13647     });
13648     
13649     if(this.proxy){
13650         this.proxy = Roo.factory(this.proxy, Roo.data);
13651         this.proxy.xmodule = this.xmodule || false;
13652         this.relayEvents(this.proxy,  ["loadexception"]);
13653     }
13654     this.sortToggle = {};
13655     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13656
13657     Roo.data.Store.superclass.constructor.call(this);
13658
13659     if(this.inlineData){
13660         this.loadData(this.inlineData);
13661         delete this.inlineData;
13662     }
13663 };
13664
13665 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13666      /**
13667     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13668     * without a remote query - used by combo/forms at present.
13669     */
13670     
13671     /**
13672     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13673     */
13674     /**
13675     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13676     */
13677     /**
13678     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13679     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13680     */
13681     /**
13682     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13683     * on any HTTP request
13684     */
13685     /**
13686     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13687     */
13688     /**
13689     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13690     */
13691     multiSort: false,
13692     /**
13693     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13694     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13695     */
13696     remoteSort : false,
13697
13698     /**
13699     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13700      * loaded or when a record is removed. (defaults to false).
13701     */
13702     pruneModifiedRecords : false,
13703
13704     // private
13705     lastOptions : null,
13706
13707     /**
13708      * Add Records to the Store and fires the add event.
13709      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13710      */
13711     add : function(records){
13712         records = [].concat(records);
13713         for(var i = 0, len = records.length; i < len; i++){
13714             records[i].join(this);
13715         }
13716         var index = this.data.length;
13717         this.data.addAll(records);
13718         this.fireEvent("add", this, records, index);
13719     },
13720
13721     /**
13722      * Remove a Record from the Store and fires the remove event.
13723      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13724      */
13725     remove : function(record){
13726         var index = this.data.indexOf(record);
13727         this.data.removeAt(index);
13728  
13729         if(this.pruneModifiedRecords){
13730             this.modified.remove(record);
13731         }
13732         this.fireEvent("remove", this, record, index);
13733     },
13734
13735     /**
13736      * Remove all Records from the Store and fires the clear event.
13737      */
13738     removeAll : function(){
13739         this.data.clear();
13740         if(this.pruneModifiedRecords){
13741             this.modified = [];
13742         }
13743         this.fireEvent("clear", this);
13744     },
13745
13746     /**
13747      * Inserts Records to the Store at the given index and fires the add event.
13748      * @param {Number} index The start index at which to insert the passed Records.
13749      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13750      */
13751     insert : function(index, records){
13752         records = [].concat(records);
13753         for(var i = 0, len = records.length; i < len; i++){
13754             this.data.insert(index, records[i]);
13755             records[i].join(this);
13756         }
13757         this.fireEvent("add", this, records, index);
13758     },
13759
13760     /**
13761      * Get the index within the cache of the passed Record.
13762      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13763      * @return {Number} The index of the passed Record. Returns -1 if not found.
13764      */
13765     indexOf : function(record){
13766         return this.data.indexOf(record);
13767     },
13768
13769     /**
13770      * Get the index within the cache of the Record with the passed id.
13771      * @param {String} id The id of the Record to find.
13772      * @return {Number} The index of the Record. Returns -1 if not found.
13773      */
13774     indexOfId : function(id){
13775         return this.data.indexOfKey(id);
13776     },
13777
13778     /**
13779      * Get the Record with the specified id.
13780      * @param {String} id The id of the Record to find.
13781      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13782      */
13783     getById : function(id){
13784         return this.data.key(id);
13785     },
13786
13787     /**
13788      * Get the Record at the specified index.
13789      * @param {Number} index The index of the Record to find.
13790      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13791      */
13792     getAt : function(index){
13793         return this.data.itemAt(index);
13794     },
13795
13796     /**
13797      * Returns a range of Records between specified indices.
13798      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13799      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13800      * @return {Roo.data.Record[]} An array of Records
13801      */
13802     getRange : function(start, end){
13803         return this.data.getRange(start, end);
13804     },
13805
13806     // private
13807     storeOptions : function(o){
13808         o = Roo.apply({}, o);
13809         delete o.callback;
13810         delete o.scope;
13811         this.lastOptions = o;
13812     },
13813
13814     /**
13815      * Loads the Record cache from the configured Proxy using the configured Reader.
13816      * <p>
13817      * If using remote paging, then the first load call must specify the <em>start</em>
13818      * and <em>limit</em> properties in the options.params property to establish the initial
13819      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13820      * <p>
13821      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13822      * and this call will return before the new data has been loaded. Perform any post-processing
13823      * in a callback function, or in a "load" event handler.</strong>
13824      * <p>
13825      * @param {Object} options An object containing properties which control loading options:<ul>
13826      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13827      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13828      * passed the following arguments:<ul>
13829      * <li>r : Roo.data.Record[]</li>
13830      * <li>options: Options object from the load call</li>
13831      * <li>success: Boolean success indicator</li></ul></li>
13832      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13833      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13834      * </ul>
13835      */
13836     load : function(options){
13837         options = options || {};
13838         if(this.fireEvent("beforeload", this, options) !== false){
13839             this.storeOptions(options);
13840             var p = Roo.apply(options.params || {}, this.baseParams);
13841             // if meta was not loaded from remote source.. try requesting it.
13842             if (!this.reader.metaFromRemote) {
13843                 p._requestMeta = 1;
13844             }
13845             if(this.sortInfo && this.remoteSort){
13846                 var pn = this.paramNames;
13847                 p[pn["sort"]] = this.sortInfo.field;
13848                 p[pn["dir"]] = this.sortInfo.direction;
13849             }
13850             if (this.multiSort) {
13851                 var pn = this.paramNames;
13852                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13853             }
13854             
13855             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13856         }
13857     },
13858
13859     /**
13860      * Reloads the Record cache from the configured Proxy using the configured Reader and
13861      * the options from the last load operation performed.
13862      * @param {Object} options (optional) An object containing properties which may override the options
13863      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13864      * the most recently used options are reused).
13865      */
13866     reload : function(options){
13867         this.load(Roo.applyIf(options||{}, this.lastOptions));
13868     },
13869
13870     // private
13871     // Called as a callback by the Reader during a load operation.
13872     loadRecords : function(o, options, success){
13873         if(!o || success === false){
13874             if(success !== false){
13875                 this.fireEvent("load", this, [], options, o);
13876             }
13877             if(options.callback){
13878                 options.callback.call(options.scope || this, [], options, false);
13879             }
13880             return;
13881         }
13882         // if data returned failure - throw an exception.
13883         if (o.success === false) {
13884             // show a message if no listener is registered.
13885             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13886                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13887             }
13888             // loadmask wil be hooked into this..
13889             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13890             return;
13891         }
13892         var r = o.records, t = o.totalRecords || r.length;
13893         
13894         this.fireEvent("beforeloadadd", this, r, options, o);
13895         
13896         if(!options || options.add !== true){
13897             if(this.pruneModifiedRecords){
13898                 this.modified = [];
13899             }
13900             for(var i = 0, len = r.length; i < len; i++){
13901                 r[i].join(this);
13902             }
13903             if(this.snapshot){
13904                 this.data = this.snapshot;
13905                 delete this.snapshot;
13906             }
13907             this.data.clear();
13908             this.data.addAll(r);
13909             this.totalLength = t;
13910             this.applySort();
13911             this.fireEvent("datachanged", this);
13912         }else{
13913             this.totalLength = Math.max(t, this.data.length+r.length);
13914             this.add(r);
13915         }
13916         
13917         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13918                 
13919             var e = new Roo.data.Record({});
13920
13921             e.set(this.parent.displayField, this.parent.emptyTitle);
13922             e.set(this.parent.valueField, '');
13923
13924             this.insert(0, e);
13925         }
13926             
13927         this.fireEvent("load", this, r, options, o);
13928         if(options.callback){
13929             options.callback.call(options.scope || this, r, options, true);
13930         }
13931     },
13932
13933
13934     /**
13935      * Loads data from a passed data block. A Reader which understands the format of the data
13936      * must have been configured in the constructor.
13937      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13938      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13939      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13940      */
13941     loadData : function(o, append){
13942         var r = this.reader.readRecords(o);
13943         this.loadRecords(r, {add: append}, true);
13944     },
13945     
13946      /**
13947      * using 'cn' the nested child reader read the child array into it's child stores.
13948      * @param {Object} rec The record with a 'children array
13949      */
13950     loadDataFromChildren : function(rec)
13951     {
13952         this.loadData(this.reader.toLoadData(rec));
13953     },
13954     
13955
13956     /**
13957      * Gets the number of cached records.
13958      * <p>
13959      * <em>If using paging, this may not be the total size of the dataset. If the data object
13960      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13961      * the data set size</em>
13962      */
13963     getCount : function(){
13964         return this.data.length || 0;
13965     },
13966
13967     /**
13968      * Gets the total number of records in the dataset as returned by the server.
13969      * <p>
13970      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13971      * the dataset size</em>
13972      */
13973     getTotalCount : function(){
13974         return this.totalLength || 0;
13975     },
13976
13977     /**
13978      * Returns the sort state of the Store as an object with two properties:
13979      * <pre><code>
13980  field {String} The name of the field by which the Records are sorted
13981  direction {String} The sort order, "ASC" or "DESC"
13982      * </code></pre>
13983      */
13984     getSortState : function(){
13985         return this.sortInfo;
13986     },
13987
13988     // private
13989     applySort : function(){
13990         if(this.sortInfo && !this.remoteSort){
13991             var s = this.sortInfo, f = s.field;
13992             var st = this.fields.get(f).sortType;
13993             var fn = function(r1, r2){
13994                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13995                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13996             };
13997             this.data.sort(s.direction, fn);
13998             if(this.snapshot && this.snapshot != this.data){
13999                 this.snapshot.sort(s.direction, fn);
14000             }
14001         }
14002     },
14003
14004     /**
14005      * Sets the default sort column and order to be used by the next load operation.
14006      * @param {String} fieldName The name of the field to sort by.
14007      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14008      */
14009     setDefaultSort : function(field, dir){
14010         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14011     },
14012
14013     /**
14014      * Sort the Records.
14015      * If remote sorting is used, the sort is performed on the server, and the cache is
14016      * reloaded. If local sorting is used, the cache is sorted internally.
14017      * @param {String} fieldName The name of the field to sort by.
14018      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14019      */
14020     sort : function(fieldName, dir){
14021         var f = this.fields.get(fieldName);
14022         if(!dir){
14023             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14024             
14025             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14026                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14027             }else{
14028                 dir = f.sortDir;
14029             }
14030         }
14031         this.sortToggle[f.name] = dir;
14032         this.sortInfo = {field: f.name, direction: dir};
14033         if(!this.remoteSort){
14034             this.applySort();
14035             this.fireEvent("datachanged", this);
14036         }else{
14037             this.load(this.lastOptions);
14038         }
14039     },
14040
14041     /**
14042      * Calls the specified function for each of the Records in the cache.
14043      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14044      * Returning <em>false</em> aborts and exits the iteration.
14045      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14046      */
14047     each : function(fn, scope){
14048         this.data.each(fn, scope);
14049     },
14050
14051     /**
14052      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14053      * (e.g., during paging).
14054      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14055      */
14056     getModifiedRecords : function(){
14057         return this.modified;
14058     },
14059
14060     // private
14061     createFilterFn : function(property, value, anyMatch){
14062         if(!value.exec){ // not a regex
14063             value = String(value);
14064             if(value.length == 0){
14065                 return false;
14066             }
14067             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14068         }
14069         return function(r){
14070             return value.test(r.data[property]);
14071         };
14072     },
14073
14074     /**
14075      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14076      * @param {String} property A field on your records
14077      * @param {Number} start The record index to start at (defaults to 0)
14078      * @param {Number} end The last record index to include (defaults to length - 1)
14079      * @return {Number} The sum
14080      */
14081     sum : function(property, start, end){
14082         var rs = this.data.items, v = 0;
14083         start = start || 0;
14084         end = (end || end === 0) ? end : rs.length-1;
14085
14086         for(var i = start; i <= end; i++){
14087             v += (rs[i].data[property] || 0);
14088         }
14089         return v;
14090     },
14091
14092     /**
14093      * Filter the records by a specified property.
14094      * @param {String} field A field on your records
14095      * @param {String/RegExp} value Either a string that the field
14096      * should start with or a RegExp to test against the field
14097      * @param {Boolean} anyMatch True to match any part not just the beginning
14098      */
14099     filter : function(property, value, anyMatch){
14100         var fn = this.createFilterFn(property, value, anyMatch);
14101         return fn ? this.filterBy(fn) : this.clearFilter();
14102     },
14103
14104     /**
14105      * Filter by a function. The specified function will be called with each
14106      * record in this data source. If the function returns true the record is included,
14107      * otherwise it is filtered.
14108      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14109      * @param {Object} scope (optional) The scope of the function (defaults to this)
14110      */
14111     filterBy : function(fn, scope){
14112         this.snapshot = this.snapshot || this.data;
14113         this.data = this.queryBy(fn, scope||this);
14114         this.fireEvent("datachanged", this);
14115     },
14116
14117     /**
14118      * Query the records by a specified property.
14119      * @param {String} field A field on your records
14120      * @param {String/RegExp} value Either a string that the field
14121      * should start with or a RegExp to test against the field
14122      * @param {Boolean} anyMatch True to match any part not just the beginning
14123      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14124      */
14125     query : function(property, value, anyMatch){
14126         var fn = this.createFilterFn(property, value, anyMatch);
14127         return fn ? this.queryBy(fn) : this.data.clone();
14128     },
14129
14130     /**
14131      * Query by a function. The specified function will be called with each
14132      * record in this data source. If the function returns true the record is included
14133      * in the results.
14134      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14135      * @param {Object} scope (optional) The scope of the function (defaults to this)
14136       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14137      **/
14138     queryBy : function(fn, scope){
14139         var data = this.snapshot || this.data;
14140         return data.filterBy(fn, scope||this);
14141     },
14142
14143     /**
14144      * Collects unique values for a particular dataIndex from this store.
14145      * @param {String} dataIndex The property to collect
14146      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14147      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14148      * @return {Array} An array of the unique values
14149      **/
14150     collect : function(dataIndex, allowNull, bypassFilter){
14151         var d = (bypassFilter === true && this.snapshot) ?
14152                 this.snapshot.items : this.data.items;
14153         var v, sv, r = [], l = {};
14154         for(var i = 0, len = d.length; i < len; i++){
14155             v = d[i].data[dataIndex];
14156             sv = String(v);
14157             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14158                 l[sv] = true;
14159                 r[r.length] = v;
14160             }
14161         }
14162         return r;
14163     },
14164
14165     /**
14166      * Revert to a view of the Record cache with no filtering applied.
14167      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14168      */
14169     clearFilter : function(suppressEvent){
14170         if(this.snapshot && this.snapshot != this.data){
14171             this.data = this.snapshot;
14172             delete this.snapshot;
14173             if(suppressEvent !== true){
14174                 this.fireEvent("datachanged", this);
14175             }
14176         }
14177     },
14178
14179     // private
14180     afterEdit : function(record){
14181         if(this.modified.indexOf(record) == -1){
14182             this.modified.push(record);
14183         }
14184         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14185     },
14186     
14187     // private
14188     afterReject : function(record){
14189         this.modified.remove(record);
14190         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14191     },
14192
14193     // private
14194     afterCommit : function(record){
14195         this.modified.remove(record);
14196         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14197     },
14198
14199     /**
14200      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14201      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14202      */
14203     commitChanges : function(){
14204         var m = this.modified.slice(0);
14205         this.modified = [];
14206         for(var i = 0, len = m.length; i < len; i++){
14207             m[i].commit();
14208         }
14209     },
14210
14211     /**
14212      * Cancel outstanding changes on all changed records.
14213      */
14214     rejectChanges : function(){
14215         var m = this.modified.slice(0);
14216         this.modified = [];
14217         for(var i = 0, len = m.length; i < len; i++){
14218             m[i].reject();
14219         }
14220     },
14221
14222     onMetaChange : function(meta, rtype, o){
14223         this.recordType = rtype;
14224         this.fields = rtype.prototype.fields;
14225         delete this.snapshot;
14226         this.sortInfo = meta.sortInfo || this.sortInfo;
14227         this.modified = [];
14228         this.fireEvent('metachange', this, this.reader.meta);
14229     },
14230     
14231     moveIndex : function(data, type)
14232     {
14233         var index = this.indexOf(data);
14234         
14235         var newIndex = index + type;
14236         
14237         this.remove(data);
14238         
14239         this.insert(newIndex, data);
14240         
14241     }
14242 });/*
14243  * Based on:
14244  * Ext JS Library 1.1.1
14245  * Copyright(c) 2006-2007, Ext JS, LLC.
14246  *
14247  * Originally Released Under LGPL - original licence link has changed is not relivant.
14248  *
14249  * Fork - LGPL
14250  * <script type="text/javascript">
14251  */
14252
14253 /**
14254  * @class Roo.data.SimpleStore
14255  * @extends Roo.data.Store
14256  * Small helper class to make creating Stores from Array data easier.
14257  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14258  * @cfg {Array} fields An array of field definition objects, or field name strings.
14259  * @cfg {Object} an existing reader (eg. copied from another store)
14260  * @cfg {Array} data The multi-dimensional array of data
14261  * @constructor
14262  * @param {Object} config
14263  */
14264 Roo.data.SimpleStore = function(config)
14265 {
14266     Roo.data.SimpleStore.superclass.constructor.call(this, {
14267         isLocal : true,
14268         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14269                 id: config.id
14270             },
14271             Roo.data.Record.create(config.fields)
14272         ),
14273         proxy : new Roo.data.MemoryProxy(config.data)
14274     });
14275     this.load();
14276 };
14277 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14278  * Based on:
14279  * Ext JS Library 1.1.1
14280  * Copyright(c) 2006-2007, Ext JS, LLC.
14281  *
14282  * Originally Released Under LGPL - original licence link has changed is not relivant.
14283  *
14284  * Fork - LGPL
14285  * <script type="text/javascript">
14286  */
14287
14288 /**
14289 /**
14290  * @extends Roo.data.Store
14291  * @class Roo.data.JsonStore
14292  * Small helper class to make creating Stores for JSON data easier. <br/>
14293 <pre><code>
14294 var store = new Roo.data.JsonStore({
14295     url: 'get-images.php',
14296     root: 'images',
14297     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14298 });
14299 </code></pre>
14300  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14301  * JsonReader and HttpProxy (unless inline data is provided).</b>
14302  * @cfg {Array} fields An array of field definition objects, or field name strings.
14303  * @constructor
14304  * @param {Object} config
14305  */
14306 Roo.data.JsonStore = function(c){
14307     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14308         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14309         reader: new Roo.data.JsonReader(c, c.fields)
14310     }));
14311 };
14312 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14313  * Based on:
14314  * Ext JS Library 1.1.1
14315  * Copyright(c) 2006-2007, Ext JS, LLC.
14316  *
14317  * Originally Released Under LGPL - original licence link has changed is not relivant.
14318  *
14319  * Fork - LGPL
14320  * <script type="text/javascript">
14321  */
14322
14323  
14324 Roo.data.Field = function(config){
14325     if(typeof config == "string"){
14326         config = {name: config};
14327     }
14328     Roo.apply(this, config);
14329     
14330     if(!this.type){
14331         this.type = "auto";
14332     }
14333     
14334     var st = Roo.data.SortTypes;
14335     // named sortTypes are supported, here we look them up
14336     if(typeof this.sortType == "string"){
14337         this.sortType = st[this.sortType];
14338     }
14339     
14340     // set default sortType for strings and dates
14341     if(!this.sortType){
14342         switch(this.type){
14343             case "string":
14344                 this.sortType = st.asUCString;
14345                 break;
14346             case "date":
14347                 this.sortType = st.asDate;
14348                 break;
14349             default:
14350                 this.sortType = st.none;
14351         }
14352     }
14353
14354     // define once
14355     var stripRe = /[\$,%]/g;
14356
14357     // prebuilt conversion function for this field, instead of
14358     // switching every time we're reading a value
14359     if(!this.convert){
14360         var cv, dateFormat = this.dateFormat;
14361         switch(this.type){
14362             case "":
14363             case "auto":
14364             case undefined:
14365                 cv = function(v){ return v; };
14366                 break;
14367             case "string":
14368                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14369                 break;
14370             case "int":
14371                 cv = function(v){
14372                     return v !== undefined && v !== null && v !== '' ?
14373                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14374                     };
14375                 break;
14376             case "float":
14377                 cv = function(v){
14378                     return v !== undefined && v !== null && v !== '' ?
14379                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14380                     };
14381                 break;
14382             case "bool":
14383             case "boolean":
14384                 cv = function(v){ return v === true || v === "true" || v == 1; };
14385                 break;
14386             case "date":
14387                 cv = function(v){
14388                     if(!v){
14389                         return '';
14390                     }
14391                     if(v instanceof Date){
14392                         return v;
14393                     }
14394                     if(dateFormat){
14395                         if(dateFormat == "timestamp"){
14396                             return new Date(v*1000);
14397                         }
14398                         return Date.parseDate(v, dateFormat);
14399                     }
14400                     var parsed = Date.parse(v);
14401                     return parsed ? new Date(parsed) : null;
14402                 };
14403              break;
14404             
14405         }
14406         this.convert = cv;
14407     }
14408 };
14409
14410 Roo.data.Field.prototype = {
14411     dateFormat: null,
14412     defaultValue: "",
14413     mapping: null,
14414     sortType : null,
14415     sortDir : "ASC"
14416 };/*
14417  * Based on:
14418  * Ext JS Library 1.1.1
14419  * Copyright(c) 2006-2007, Ext JS, LLC.
14420  *
14421  * Originally Released Under LGPL - original licence link has changed is not relivant.
14422  *
14423  * Fork - LGPL
14424  * <script type="text/javascript">
14425  */
14426  
14427 // Base class for reading structured data from a data source.  This class is intended to be
14428 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14429
14430 /**
14431  * @class Roo.data.DataReader
14432  * Base class for reading structured data from a data source.  This class is intended to be
14433  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14434  */
14435
14436 Roo.data.DataReader = function(meta, recordType){
14437     
14438     this.meta = meta;
14439     
14440     this.recordType = recordType instanceof Array ? 
14441         Roo.data.Record.create(recordType) : recordType;
14442 };
14443
14444 Roo.data.DataReader.prototype = {
14445     
14446     
14447     readerType : 'Data',
14448      /**
14449      * Create an empty record
14450      * @param {Object} data (optional) - overlay some values
14451      * @return {Roo.data.Record} record created.
14452      */
14453     newRow :  function(d) {
14454         var da =  {};
14455         this.recordType.prototype.fields.each(function(c) {
14456             switch( c.type) {
14457                 case 'int' : da[c.name] = 0; break;
14458                 case 'date' : da[c.name] = new Date(); break;
14459                 case 'float' : da[c.name] = 0.0; break;
14460                 case 'boolean' : da[c.name] = false; break;
14461                 default : da[c.name] = ""; break;
14462             }
14463             
14464         });
14465         return new this.recordType(Roo.apply(da, d));
14466     }
14467     
14468     
14469 };/*
14470  * Based on:
14471  * Ext JS Library 1.1.1
14472  * Copyright(c) 2006-2007, Ext JS, LLC.
14473  *
14474  * Originally Released Under LGPL - original licence link has changed is not relivant.
14475  *
14476  * Fork - LGPL
14477  * <script type="text/javascript">
14478  */
14479
14480 /**
14481  * @class Roo.data.DataProxy
14482  * @extends Roo.data.Observable
14483  * This class is an abstract base class for implementations which provide retrieval of
14484  * unformatted data objects.<br>
14485  * <p>
14486  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14487  * (of the appropriate type which knows how to parse the data object) to provide a block of
14488  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14489  * <p>
14490  * Custom implementations must implement the load method as described in
14491  * {@link Roo.data.HttpProxy#load}.
14492  */
14493 Roo.data.DataProxy = function(){
14494     this.addEvents({
14495         /**
14496          * @event beforeload
14497          * Fires before a network request is made to retrieve a data object.
14498          * @param {Object} This DataProxy object.
14499          * @param {Object} params The params parameter to the load function.
14500          */
14501         beforeload : true,
14502         /**
14503          * @event load
14504          * Fires before the load method's callback is called.
14505          * @param {Object} This DataProxy object.
14506          * @param {Object} o The data object.
14507          * @param {Object} arg The callback argument object passed to the load function.
14508          */
14509         load : true,
14510         /**
14511          * @event loadexception
14512          * Fires if an Exception occurs during data retrieval.
14513          * @param {Object} This DataProxy object.
14514          * @param {Object} o The data object.
14515          * @param {Object} arg The callback argument object passed to the load function.
14516          * @param {Object} e The Exception.
14517          */
14518         loadexception : true
14519     });
14520     Roo.data.DataProxy.superclass.constructor.call(this);
14521 };
14522
14523 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14524
14525     /**
14526      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14527      */
14528 /*
14529  * Based on:
14530  * Ext JS Library 1.1.1
14531  * Copyright(c) 2006-2007, Ext JS, LLC.
14532  *
14533  * Originally Released Under LGPL - original licence link has changed is not relivant.
14534  *
14535  * Fork - LGPL
14536  * <script type="text/javascript">
14537  */
14538 /**
14539  * @class Roo.data.MemoryProxy
14540  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14541  * to the Reader when its load method is called.
14542  * @constructor
14543  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14544  */
14545 Roo.data.MemoryProxy = function(data){
14546     if (data.data) {
14547         data = data.data;
14548     }
14549     Roo.data.MemoryProxy.superclass.constructor.call(this);
14550     this.data = data;
14551 };
14552
14553 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14554     
14555     /**
14556      * Load data from the requested source (in this case an in-memory
14557      * data object passed to the constructor), read the data object into
14558      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14559      * process that block using the passed callback.
14560      * @param {Object} params This parameter is not used by the MemoryProxy class.
14561      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14562      * object into a block of Roo.data.Records.
14563      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14564      * The function must be passed <ul>
14565      * <li>The Record block object</li>
14566      * <li>The "arg" argument from the load function</li>
14567      * <li>A boolean success indicator</li>
14568      * </ul>
14569      * @param {Object} scope The scope in which to call the callback
14570      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14571      */
14572     load : function(params, reader, callback, scope, arg){
14573         params = params || {};
14574         var result;
14575         try {
14576             result = reader.readRecords(params.data ? params.data :this.data);
14577         }catch(e){
14578             this.fireEvent("loadexception", this, arg, null, e);
14579             callback.call(scope, null, arg, false);
14580             return;
14581         }
14582         callback.call(scope, result, arg, true);
14583     },
14584     
14585     // private
14586     update : function(params, records){
14587         
14588     }
14589 });/*
14590  * Based on:
14591  * Ext JS Library 1.1.1
14592  * Copyright(c) 2006-2007, Ext JS, LLC.
14593  *
14594  * Originally Released Under LGPL - original licence link has changed is not relivant.
14595  *
14596  * Fork - LGPL
14597  * <script type="text/javascript">
14598  */
14599 /**
14600  * @class Roo.data.HttpProxy
14601  * @extends Roo.data.DataProxy
14602  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14603  * configured to reference a certain URL.<br><br>
14604  * <p>
14605  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14606  * from which the running page was served.<br><br>
14607  * <p>
14608  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14609  * <p>
14610  * Be aware that to enable the browser to parse an XML document, the server must set
14611  * the Content-Type header in the HTTP response to "text/xml".
14612  * @constructor
14613  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14614  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14615  * will be used to make the request.
14616  */
14617 Roo.data.HttpProxy = function(conn){
14618     Roo.data.HttpProxy.superclass.constructor.call(this);
14619     // is conn a conn config or a real conn?
14620     this.conn = conn;
14621     this.useAjax = !conn || !conn.events;
14622   
14623 };
14624
14625 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14626     // thse are take from connection...
14627     
14628     /**
14629      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14630      */
14631     /**
14632      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14633      * extra parameters to each request made by this object. (defaults to undefined)
14634      */
14635     /**
14636      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14637      *  to each request made by this object. (defaults to undefined)
14638      */
14639     /**
14640      * @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)
14641      */
14642     /**
14643      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14644      */
14645      /**
14646      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14647      * @type Boolean
14648      */
14649   
14650
14651     /**
14652      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14653      * @type Boolean
14654      */
14655     /**
14656      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14657      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14658      * a finer-grained basis than the DataProxy events.
14659      */
14660     getConnection : function(){
14661         return this.useAjax ? Roo.Ajax : this.conn;
14662     },
14663
14664     /**
14665      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14666      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14667      * process that block using the passed callback.
14668      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14669      * for the request to the remote server.
14670      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14671      * object into a block of Roo.data.Records.
14672      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14673      * The function must be passed <ul>
14674      * <li>The Record block object</li>
14675      * <li>The "arg" argument from the load function</li>
14676      * <li>A boolean success indicator</li>
14677      * </ul>
14678      * @param {Object} scope The scope in which to call the callback
14679      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14680      */
14681     load : function(params, reader, callback, scope, arg){
14682         if(this.fireEvent("beforeload", this, params) !== false){
14683             var  o = {
14684                 params : params || {},
14685                 request: {
14686                     callback : callback,
14687                     scope : scope,
14688                     arg : arg
14689                 },
14690                 reader: reader,
14691                 callback : this.loadResponse,
14692                 scope: this
14693             };
14694             if(this.useAjax){
14695                 Roo.applyIf(o, this.conn);
14696                 if(this.activeRequest){
14697                     Roo.Ajax.abort(this.activeRequest);
14698                 }
14699                 this.activeRequest = Roo.Ajax.request(o);
14700             }else{
14701                 this.conn.request(o);
14702             }
14703         }else{
14704             callback.call(scope||this, null, arg, false);
14705         }
14706     },
14707
14708     // private
14709     loadResponse : function(o, success, response){
14710         delete this.activeRequest;
14711         if(!success){
14712             this.fireEvent("loadexception", this, o, response);
14713             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14714             return;
14715         }
14716         var result;
14717         try {
14718             result = o.reader.read(response);
14719         }catch(e){
14720             this.fireEvent("loadexception", this, o, response, e);
14721             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14722             return;
14723         }
14724         
14725         this.fireEvent("load", this, o, o.request.arg);
14726         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14727     },
14728
14729     // private
14730     update : function(dataSet){
14731
14732     },
14733
14734     // private
14735     updateResponse : function(dataSet){
14736
14737     }
14738 });/*
14739  * Based on:
14740  * Ext JS Library 1.1.1
14741  * Copyright(c) 2006-2007, Ext JS, LLC.
14742  *
14743  * Originally Released Under LGPL - original licence link has changed is not relivant.
14744  *
14745  * Fork - LGPL
14746  * <script type="text/javascript">
14747  */
14748
14749 /**
14750  * @class Roo.data.ScriptTagProxy
14751  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14752  * other than the originating domain of the running page.<br><br>
14753  * <p>
14754  * <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
14755  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14756  * <p>
14757  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14758  * source code that is used as the source inside a &lt;script> tag.<br><br>
14759  * <p>
14760  * In order for the browser to process the returned data, the server must wrap the data object
14761  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14762  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14763  * depending on whether the callback name was passed:
14764  * <p>
14765  * <pre><code>
14766 boolean scriptTag = false;
14767 String cb = request.getParameter("callback");
14768 if (cb != null) {
14769     scriptTag = true;
14770     response.setContentType("text/javascript");
14771 } else {
14772     response.setContentType("application/x-json");
14773 }
14774 Writer out = response.getWriter();
14775 if (scriptTag) {
14776     out.write(cb + "(");
14777 }
14778 out.print(dataBlock.toJsonString());
14779 if (scriptTag) {
14780     out.write(");");
14781 }
14782 </pre></code>
14783  *
14784  * @constructor
14785  * @param {Object} config A configuration object.
14786  */
14787 Roo.data.ScriptTagProxy = function(config){
14788     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14789     Roo.apply(this, config);
14790     this.head = document.getElementsByTagName("head")[0];
14791 };
14792
14793 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14794
14795 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14796     /**
14797      * @cfg {String} url The URL from which to request the data object.
14798      */
14799     /**
14800      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14801      */
14802     timeout : 30000,
14803     /**
14804      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14805      * the server the name of the callback function set up by the load call to process the returned data object.
14806      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14807      * javascript output which calls this named function passing the data object as its only parameter.
14808      */
14809     callbackParam : "callback",
14810     /**
14811      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14812      * name to the request.
14813      */
14814     nocache : true,
14815
14816     /**
14817      * Load data from the configured URL, read the data object into
14818      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14819      * process that block using the passed callback.
14820      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14821      * for the request to the remote server.
14822      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14823      * object into a block of Roo.data.Records.
14824      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14825      * The function must be passed <ul>
14826      * <li>The Record block object</li>
14827      * <li>The "arg" argument from the load function</li>
14828      * <li>A boolean success indicator</li>
14829      * </ul>
14830      * @param {Object} scope The scope in which to call the callback
14831      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14832      */
14833     load : function(params, reader, callback, scope, arg){
14834         if(this.fireEvent("beforeload", this, params) !== false){
14835
14836             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14837
14838             var url = this.url;
14839             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14840             if(this.nocache){
14841                 url += "&_dc=" + (new Date().getTime());
14842             }
14843             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14844             var trans = {
14845                 id : transId,
14846                 cb : "stcCallback"+transId,
14847                 scriptId : "stcScript"+transId,
14848                 params : params,
14849                 arg : arg,
14850                 url : url,
14851                 callback : callback,
14852                 scope : scope,
14853                 reader : reader
14854             };
14855             var conn = this;
14856
14857             window[trans.cb] = function(o){
14858                 conn.handleResponse(o, trans);
14859             };
14860
14861             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14862
14863             if(this.autoAbort !== false){
14864                 this.abort();
14865             }
14866
14867             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14868
14869             var script = document.createElement("script");
14870             script.setAttribute("src", url);
14871             script.setAttribute("type", "text/javascript");
14872             script.setAttribute("id", trans.scriptId);
14873             this.head.appendChild(script);
14874
14875             this.trans = trans;
14876         }else{
14877             callback.call(scope||this, null, arg, false);
14878         }
14879     },
14880
14881     // private
14882     isLoading : function(){
14883         return this.trans ? true : false;
14884     },
14885
14886     /**
14887      * Abort the current server request.
14888      */
14889     abort : function(){
14890         if(this.isLoading()){
14891             this.destroyTrans(this.trans);
14892         }
14893     },
14894
14895     // private
14896     destroyTrans : function(trans, isLoaded){
14897         this.head.removeChild(document.getElementById(trans.scriptId));
14898         clearTimeout(trans.timeoutId);
14899         if(isLoaded){
14900             window[trans.cb] = undefined;
14901             try{
14902                 delete window[trans.cb];
14903             }catch(e){}
14904         }else{
14905             // if hasn't been loaded, wait for load to remove it to prevent script error
14906             window[trans.cb] = function(){
14907                 window[trans.cb] = undefined;
14908                 try{
14909                     delete window[trans.cb];
14910                 }catch(e){}
14911             };
14912         }
14913     },
14914
14915     // private
14916     handleResponse : function(o, trans){
14917         this.trans = false;
14918         this.destroyTrans(trans, true);
14919         var result;
14920         try {
14921             result = trans.reader.readRecords(o);
14922         }catch(e){
14923             this.fireEvent("loadexception", this, o, trans.arg, e);
14924             trans.callback.call(trans.scope||window, null, trans.arg, false);
14925             return;
14926         }
14927         this.fireEvent("load", this, o, trans.arg);
14928         trans.callback.call(trans.scope||window, result, trans.arg, true);
14929     },
14930
14931     // private
14932     handleFailure : function(trans){
14933         this.trans = false;
14934         this.destroyTrans(trans, false);
14935         this.fireEvent("loadexception", this, null, trans.arg);
14936         trans.callback.call(trans.scope||window, null, trans.arg, false);
14937     }
14938 });/*
14939  * Based on:
14940  * Ext JS Library 1.1.1
14941  * Copyright(c) 2006-2007, Ext JS, LLC.
14942  *
14943  * Originally Released Under LGPL - original licence link has changed is not relivant.
14944  *
14945  * Fork - LGPL
14946  * <script type="text/javascript">
14947  */
14948
14949 /**
14950  * @class Roo.data.JsonReader
14951  * @extends Roo.data.DataReader
14952  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14953  * based on mappings in a provided Roo.data.Record constructor.
14954  * 
14955  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14956  * in the reply previously. 
14957  * 
14958  * <p>
14959  * Example code:
14960  * <pre><code>
14961 var RecordDef = Roo.data.Record.create([
14962     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14963     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14964 ]);
14965 var myReader = new Roo.data.JsonReader({
14966     totalProperty: "results",    // The property which contains the total dataset size (optional)
14967     root: "rows",                // The property which contains an Array of row objects
14968     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14969 }, RecordDef);
14970 </code></pre>
14971  * <p>
14972  * This would consume a JSON file like this:
14973  * <pre><code>
14974 { 'results': 2, 'rows': [
14975     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14976     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14977 }
14978 </code></pre>
14979  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14980  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14981  * paged from the remote server.
14982  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14983  * @cfg {String} root name of the property which contains the Array of row objects.
14984  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14985  * @cfg {Array} fields Array of field definition objects
14986  * @constructor
14987  * Create a new JsonReader
14988  * @param {Object} meta Metadata configuration options
14989  * @param {Object} recordType Either an Array of field definition objects,
14990  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14991  */
14992 Roo.data.JsonReader = function(meta, recordType){
14993     
14994     meta = meta || {};
14995     // set some defaults:
14996     Roo.applyIf(meta, {
14997         totalProperty: 'total',
14998         successProperty : 'success',
14999         root : 'data',
15000         id : 'id'
15001     });
15002     
15003     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15004 };
15005 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15006     
15007     readerType : 'Json',
15008     
15009     /**
15010      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15011      * Used by Store query builder to append _requestMeta to params.
15012      * 
15013      */
15014     metaFromRemote : false,
15015     /**
15016      * This method is only used by a DataProxy which has retrieved data from a remote server.
15017      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15018      * @return {Object} data A data block which is used by an Roo.data.Store object as
15019      * a cache of Roo.data.Records.
15020      */
15021     read : function(response){
15022         var json = response.responseText;
15023        
15024         var o = /* eval:var:o */ eval("("+json+")");
15025         if(!o) {
15026             throw {message: "JsonReader.read: Json object not found"};
15027         }
15028         
15029         if(o.metaData){
15030             
15031             delete this.ef;
15032             this.metaFromRemote = true;
15033             this.meta = o.metaData;
15034             this.recordType = Roo.data.Record.create(o.metaData.fields);
15035             this.onMetaChange(this.meta, this.recordType, o);
15036         }
15037         return this.readRecords(o);
15038     },
15039
15040     // private function a store will implement
15041     onMetaChange : function(meta, recordType, o){
15042
15043     },
15044
15045     /**
15046          * @ignore
15047          */
15048     simpleAccess: function(obj, subsc) {
15049         return obj[subsc];
15050     },
15051
15052         /**
15053          * @ignore
15054          */
15055     getJsonAccessor: function(){
15056         var re = /[\[\.]/;
15057         return function(expr) {
15058             try {
15059                 return(re.test(expr))
15060                     ? new Function("obj", "return obj." + expr)
15061                     : function(obj){
15062                         return obj[expr];
15063                     };
15064             } catch(e){}
15065             return Roo.emptyFn;
15066         };
15067     }(),
15068
15069     /**
15070      * Create a data block containing Roo.data.Records from an XML document.
15071      * @param {Object} o An object which contains an Array of row objects in the property specified
15072      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15073      * which contains the total size of the dataset.
15074      * @return {Object} data A data block which is used by an Roo.data.Store object as
15075      * a cache of Roo.data.Records.
15076      */
15077     readRecords : function(o){
15078         /**
15079          * After any data loads, the raw JSON data is available for further custom processing.
15080          * @type Object
15081          */
15082         this.o = o;
15083         var s = this.meta, Record = this.recordType,
15084             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15085
15086 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15087         if (!this.ef) {
15088             if(s.totalProperty) {
15089                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15090                 }
15091                 if(s.successProperty) {
15092                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15093                 }
15094                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15095                 if (s.id) {
15096                         var g = this.getJsonAccessor(s.id);
15097                         this.getId = function(rec) {
15098                                 var r = g(rec);  
15099                                 return (r === undefined || r === "") ? null : r;
15100                         };
15101                 } else {
15102                         this.getId = function(){return null;};
15103                 }
15104             this.ef = [];
15105             for(var jj = 0; jj < fl; jj++){
15106                 f = fi[jj];
15107                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15108                 this.ef[jj] = this.getJsonAccessor(map);
15109             }
15110         }
15111
15112         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15113         if(s.totalProperty){
15114             var vt = parseInt(this.getTotal(o), 10);
15115             if(!isNaN(vt)){
15116                 totalRecords = vt;
15117             }
15118         }
15119         if(s.successProperty){
15120             var vs = this.getSuccess(o);
15121             if(vs === false || vs === 'false'){
15122                 success = false;
15123             }
15124         }
15125         var records = [];
15126         for(var i = 0; i < c; i++){
15127                 var n = root[i];
15128             var values = {};
15129             var id = this.getId(n);
15130             for(var j = 0; j < fl; j++){
15131                 f = fi[j];
15132             var v = this.ef[j](n);
15133             if (!f.convert) {
15134                 Roo.log('missing convert for ' + f.name);
15135                 Roo.log(f);
15136                 continue;
15137             }
15138             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15139             }
15140             var record = new Record(values, id);
15141             record.json = n;
15142             records[i] = record;
15143         }
15144         return {
15145             raw : o,
15146             success : success,
15147             records : records,
15148             totalRecords : totalRecords
15149         };
15150     },
15151     // used when loading children.. @see loadDataFromChildren
15152     toLoadData: function(rec)
15153     {
15154         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15155         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15156         return { data : data, total : data.length };
15157         
15158     }
15159 });/*
15160  * Based on:
15161  * Ext JS Library 1.1.1
15162  * Copyright(c) 2006-2007, Ext JS, LLC.
15163  *
15164  * Originally Released Under LGPL - original licence link has changed is not relivant.
15165  *
15166  * Fork - LGPL
15167  * <script type="text/javascript">
15168  */
15169
15170 /**
15171  * @class Roo.data.ArrayReader
15172  * @extends Roo.data.DataReader
15173  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15174  * Each element of that Array represents a row of data fields. The
15175  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15176  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15177  * <p>
15178  * Example code:.
15179  * <pre><code>
15180 var RecordDef = Roo.data.Record.create([
15181     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15182     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15183 ]);
15184 var myReader = new Roo.data.ArrayReader({
15185     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15186 }, RecordDef);
15187 </code></pre>
15188  * <p>
15189  * This would consume an Array like this:
15190  * <pre><code>
15191 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15192   </code></pre>
15193  
15194  * @constructor
15195  * Create a new JsonReader
15196  * @param {Object} meta Metadata configuration options.
15197  * @param {Object|Array} recordType Either an Array of field definition objects
15198  * 
15199  * @cfg {Array} fields Array of field definition objects
15200  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15201  * as specified to {@link Roo.data.Record#create},
15202  * or an {@link Roo.data.Record} object
15203  *
15204  * 
15205  * created using {@link Roo.data.Record#create}.
15206  */
15207 Roo.data.ArrayReader = function(meta, recordType)
15208 {    
15209     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15210 };
15211
15212 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15213     
15214       /**
15215      * Create a data block containing Roo.data.Records from an XML document.
15216      * @param {Object} o An Array of row objects which represents the dataset.
15217      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15218      * a cache of Roo.data.Records.
15219      */
15220     readRecords : function(o)
15221     {
15222         var sid = this.meta ? this.meta.id : null;
15223         var recordType = this.recordType, fields = recordType.prototype.fields;
15224         var records = [];
15225         var root = o;
15226         for(var i = 0; i < root.length; i++){
15227             var n = root[i];
15228             var values = {};
15229             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15230             for(var j = 0, jlen = fields.length; j < jlen; j++){
15231                 var f = fields.items[j];
15232                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15233                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15234                 v = f.convert(v);
15235                 values[f.name] = v;
15236             }
15237             var record = new recordType(values, id);
15238             record.json = n;
15239             records[records.length] = record;
15240         }
15241         return {
15242             records : records,
15243             totalRecords : records.length
15244         };
15245     },
15246     // used when loading children.. @see loadDataFromChildren
15247     toLoadData: function(rec)
15248     {
15249         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15250         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15251         
15252     }
15253     
15254     
15255 });/*
15256  * - LGPL
15257  * * 
15258  */
15259
15260 /**
15261  * @class Roo.bootstrap.ComboBox
15262  * @extends Roo.bootstrap.TriggerField
15263  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15264  * @cfg {Boolean} append (true|false) default false
15265  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15266  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15267  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15268  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15269  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15270  * @cfg {Boolean} animate default true
15271  * @cfg {Boolean} emptyResultText only for touch device
15272  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15273  * @cfg {String} emptyTitle default ''
15274  * @cfg {Number} width fixed with? experimental
15275  * @constructor
15276  * Create a new ComboBox.
15277  * @param {Object} config Configuration options
15278  */
15279 Roo.bootstrap.ComboBox = function(config){
15280     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15281     this.addEvents({
15282         /**
15283          * @event expand
15284          * Fires when the dropdown list is expanded
15285         * @param {Roo.bootstrap.ComboBox} combo This combo box
15286         */
15287         'expand' : true,
15288         /**
15289          * @event collapse
15290          * Fires when the dropdown list is collapsed
15291         * @param {Roo.bootstrap.ComboBox} combo This combo box
15292         */
15293         'collapse' : true,
15294         /**
15295          * @event beforeselect
15296          * Fires before a list item is selected. Return false to cancel the selection.
15297         * @param {Roo.bootstrap.ComboBox} combo This combo box
15298         * @param {Roo.data.Record} record The data record returned from the underlying store
15299         * @param {Number} index The index of the selected item in the dropdown list
15300         */
15301         'beforeselect' : true,
15302         /**
15303          * @event select
15304          * Fires when a list item is selected
15305         * @param {Roo.bootstrap.ComboBox} combo This combo box
15306         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15307         * @param {Number} index The index of the selected item in the dropdown list
15308         */
15309         'select' : true,
15310         /**
15311          * @event beforequery
15312          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15313          * The event object passed has these properties:
15314         * @param {Roo.bootstrap.ComboBox} combo This combo box
15315         * @param {String} query The query
15316         * @param {Boolean} forceAll true to force "all" query
15317         * @param {Boolean} cancel true to cancel the query
15318         * @param {Object} e The query event object
15319         */
15320         'beforequery': true,
15321          /**
15322          * @event add
15323          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15324         * @param {Roo.bootstrap.ComboBox} combo This combo box
15325         */
15326         'add' : true,
15327         /**
15328          * @event edit
15329          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15330         * @param {Roo.bootstrap.ComboBox} combo This combo box
15331         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15332         */
15333         'edit' : true,
15334         /**
15335          * @event remove
15336          * Fires when the remove value from the combobox array
15337         * @param {Roo.bootstrap.ComboBox} combo This combo box
15338         */
15339         'remove' : true,
15340         /**
15341          * @event afterremove
15342          * Fires when the remove value from the combobox array
15343         * @param {Roo.bootstrap.ComboBox} combo This combo box
15344         */
15345         'afterremove' : true,
15346         /**
15347          * @event specialfilter
15348          * Fires when specialfilter
15349             * @param {Roo.bootstrap.ComboBox} combo This combo box
15350             */
15351         'specialfilter' : true,
15352         /**
15353          * @event tick
15354          * Fires when tick the element
15355             * @param {Roo.bootstrap.ComboBox} combo This combo box
15356             */
15357         'tick' : true,
15358         /**
15359          * @event touchviewdisplay
15360          * Fires when touch view require special display (default is using displayField)
15361             * @param {Roo.bootstrap.ComboBox} combo This combo box
15362             * @param {Object} cfg set html .
15363             */
15364         'touchviewdisplay' : true
15365         
15366     });
15367     
15368     this.item = [];
15369     this.tickItems = [];
15370     
15371     this.selectedIndex = -1;
15372     if(this.mode == 'local'){
15373         if(config.queryDelay === undefined){
15374             this.queryDelay = 10;
15375         }
15376         if(config.minChars === undefined){
15377             this.minChars = 0;
15378         }
15379     }
15380 };
15381
15382 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15383      
15384     /**
15385      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15386      * rendering into an Roo.Editor, defaults to false)
15387      */
15388     /**
15389      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15390      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15391      */
15392     /**
15393      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15394      */
15395     /**
15396      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15397      * the dropdown list (defaults to undefined, with no header element)
15398      */
15399
15400      /**
15401      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15402      */
15403      
15404      /**
15405      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15406      */
15407     listWidth: undefined,
15408     /**
15409      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15410      * mode = 'remote' or 'text' if mode = 'local')
15411      */
15412     displayField: undefined,
15413     
15414     /**
15415      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15416      * mode = 'remote' or 'value' if mode = 'local'). 
15417      * Note: use of a valueField requires the user make a selection
15418      * in order for a value to be mapped.
15419      */
15420     valueField: undefined,
15421     /**
15422      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15423      */
15424     modalTitle : '',
15425     
15426     /**
15427      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15428      * field's data value (defaults to the underlying DOM element's name)
15429      */
15430     hiddenName: undefined,
15431     /**
15432      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15433      */
15434     listClass: '',
15435     /**
15436      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15437      */
15438     selectedClass: 'active',
15439     
15440     /**
15441      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15442      */
15443     shadow:'sides',
15444     /**
15445      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15446      * anchor positions (defaults to 'tl-bl')
15447      */
15448     listAlign: 'tl-bl?',
15449     /**
15450      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15451      */
15452     maxHeight: 300,
15453     /**
15454      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15455      * query specified by the allQuery config option (defaults to 'query')
15456      */
15457     triggerAction: 'query',
15458     /**
15459      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15460      * (defaults to 4, does not apply if editable = false)
15461      */
15462     minChars : 4,
15463     /**
15464      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15465      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15466      */
15467     typeAhead: false,
15468     /**
15469      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15470      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15471      */
15472     queryDelay: 500,
15473     /**
15474      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15475      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15476      */
15477     pageSize: 0,
15478     /**
15479      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15480      * when editable = true (defaults to false)
15481      */
15482     selectOnFocus:false,
15483     /**
15484      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15485      */
15486     queryParam: 'query',
15487     /**
15488      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15489      * when mode = 'remote' (defaults to 'Loading...')
15490      */
15491     loadingText: 'Loading...',
15492     /**
15493      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15494      */
15495     resizable: false,
15496     /**
15497      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15498      */
15499     handleHeight : 8,
15500     /**
15501      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15502      * traditional select (defaults to true)
15503      */
15504     editable: true,
15505     /**
15506      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15507      */
15508     allQuery: '',
15509     /**
15510      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15511      */
15512     mode: 'remote',
15513     /**
15514      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15515      * listWidth has a higher value)
15516      */
15517     minListWidth : 70,
15518     /**
15519      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15520      * allow the user to set arbitrary text into the field (defaults to false)
15521      */
15522     forceSelection:false,
15523     /**
15524      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15525      * if typeAhead = true (defaults to 250)
15526      */
15527     typeAheadDelay : 250,
15528     /**
15529      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15530      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15531      */
15532     valueNotFoundText : undefined,
15533     /**
15534      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15535      */
15536     blockFocus : false,
15537     
15538     /**
15539      * @cfg {Boolean} disableClear Disable showing of clear button.
15540      */
15541     disableClear : false,
15542     /**
15543      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15544      */
15545     alwaysQuery : false,
15546     
15547     /**
15548      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15549      */
15550     multiple : false,
15551     
15552     /**
15553      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15554      */
15555     invalidClass : "has-warning",
15556     
15557     /**
15558      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15559      */
15560     validClass : "has-success",
15561     
15562     /**
15563      * @cfg {Boolean} specialFilter (true|false) special filter default false
15564      */
15565     specialFilter : false,
15566     
15567     /**
15568      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15569      */
15570     mobileTouchView : true,
15571     
15572     /**
15573      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15574      */
15575     useNativeIOS : false,
15576     
15577     /**
15578      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15579      */
15580     mobile_restrict_height : false,
15581     
15582     ios_options : false,
15583     
15584     //private
15585     addicon : false,
15586     editicon: false,
15587     
15588     page: 0,
15589     hasQuery: false,
15590     append: false,
15591     loadNext: false,
15592     autoFocus : true,
15593     tickable : false,
15594     btnPosition : 'right',
15595     triggerList : true,
15596     showToggleBtn : true,
15597     animate : true,
15598     emptyResultText: 'Empty',
15599     triggerText : 'Select',
15600     emptyTitle : '',
15601     width : false,
15602     
15603     // element that contains real text value.. (when hidden is used..)
15604     
15605     getAutoCreate : function()
15606     {   
15607         var cfg = false;
15608         //render
15609         /*
15610          * Render classic select for iso
15611          */
15612         
15613         if(Roo.isIOS && this.useNativeIOS){
15614             cfg = this.getAutoCreateNativeIOS();
15615             return cfg;
15616         }
15617         
15618         /*
15619          * Touch Devices
15620          */
15621         
15622         if(Roo.isTouch && this.mobileTouchView){
15623             cfg = this.getAutoCreateTouchView();
15624             return cfg;;
15625         }
15626         
15627         /*
15628          *  Normal ComboBox
15629          */
15630         if(!this.tickable){
15631             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15632             return cfg;
15633         }
15634         
15635         /*
15636          *  ComboBox with tickable selections
15637          */
15638              
15639         var align = this.labelAlign || this.parentLabelAlign();
15640         
15641         cfg = {
15642             cls : 'form-group roo-combobox-tickable' //input-group
15643         };
15644         
15645         var btn_text_select = '';
15646         var btn_text_done = '';
15647         var btn_text_cancel = '';
15648         
15649         if (this.btn_text_show) {
15650             btn_text_select = 'Select';
15651             btn_text_done = 'Done';
15652             btn_text_cancel = 'Cancel'; 
15653         }
15654         
15655         var buttons = {
15656             tag : 'div',
15657             cls : 'tickable-buttons',
15658             cn : [
15659                 {
15660                     tag : 'button',
15661                     type : 'button',
15662                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15663                     //html : this.triggerText
15664                     html: btn_text_select
15665                 },
15666                 {
15667                     tag : 'button',
15668                     type : 'button',
15669                     name : 'ok',
15670                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15671                     //html : 'Done'
15672                     html: btn_text_done
15673                 },
15674                 {
15675                     tag : 'button',
15676                     type : 'button',
15677                     name : 'cancel',
15678                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15679                     //html : 'Cancel'
15680                     html: btn_text_cancel
15681                 }
15682             ]
15683         };
15684         
15685         if(this.editable){
15686             buttons.cn.unshift({
15687                 tag: 'input',
15688                 cls: 'roo-select2-search-field-input'
15689             });
15690         }
15691         
15692         var _this = this;
15693         
15694         Roo.each(buttons.cn, function(c){
15695             if (_this.size) {
15696                 c.cls += ' btn-' + _this.size;
15697             }
15698
15699             if (_this.disabled) {
15700                 c.disabled = true;
15701             }
15702         });
15703         
15704         var box = {
15705             tag: 'div',
15706             style : 'display: contents',
15707             cn: [
15708                 {
15709                     tag: 'input',
15710                     type : 'hidden',
15711                     cls: 'form-hidden-field'
15712                 },
15713                 {
15714                     tag: 'ul',
15715                     cls: 'roo-select2-choices',
15716                     cn:[
15717                         {
15718                             tag: 'li',
15719                             cls: 'roo-select2-search-field',
15720                             cn: [
15721                                 buttons
15722                             ]
15723                         }
15724                     ]
15725                 }
15726             ]
15727         };
15728         
15729         var combobox = {
15730             cls: 'roo-select2-container input-group roo-select2-container-multi',
15731             cn: [
15732                 
15733                 box
15734 //                {
15735 //                    tag: 'ul',
15736 //                    cls: 'typeahead typeahead-long dropdown-menu',
15737 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15738 //                }
15739             ]
15740         };
15741         
15742         if(this.hasFeedback && !this.allowBlank){
15743             
15744             var feedback = {
15745                 tag: 'span',
15746                 cls: 'glyphicon form-control-feedback'
15747             };
15748
15749             combobox.cn.push(feedback);
15750         }
15751         
15752         
15753         
15754         var indicator = {
15755             tag : 'i',
15756             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15757             tooltip : 'This field is required'
15758         };
15759         if (Roo.bootstrap.version == 4) {
15760             indicator = {
15761                 tag : 'i',
15762                 style : 'display:none'
15763             };
15764         }
15765         if (align ==='left' && this.fieldLabel.length) {
15766             
15767             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15768             
15769             cfg.cn = [
15770                 indicator,
15771                 {
15772                     tag: 'label',
15773                     'for' :  id,
15774                     cls : 'control-label col-form-label',
15775                     html : this.fieldLabel
15776
15777                 },
15778                 {
15779                     cls : "", 
15780                     cn: [
15781                         combobox
15782                     ]
15783                 }
15784
15785             ];
15786             
15787             var labelCfg = cfg.cn[1];
15788             var contentCfg = cfg.cn[2];
15789             
15790
15791             if(this.indicatorpos == 'right'){
15792                 
15793                 cfg.cn = [
15794                     {
15795                         tag: 'label',
15796                         'for' :  id,
15797                         cls : 'control-label col-form-label',
15798                         cn : [
15799                             {
15800                                 tag : 'span',
15801                                 html : this.fieldLabel
15802                             },
15803                             indicator
15804                         ]
15805                     },
15806                     {
15807                         cls : "",
15808                         cn: [
15809                             combobox
15810                         ]
15811                     }
15812
15813                 ];
15814                 
15815                 
15816                 
15817                 labelCfg = cfg.cn[0];
15818                 contentCfg = cfg.cn[1];
15819             
15820             }
15821             
15822             if(this.labelWidth > 12){
15823                 labelCfg.style = "width: " + this.labelWidth + 'px';
15824             }
15825             if(this.width * 1 > 0){
15826                 contentCfg.style = "width: " + this.width + 'px';
15827             }
15828             if(this.labelWidth < 13 && this.labelmd == 0){
15829                 this.labelmd = this.labelWidth;
15830             }
15831             
15832             if(this.labellg > 0){
15833                 labelCfg.cls += ' col-lg-' + this.labellg;
15834                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15835             }
15836             
15837             if(this.labelmd > 0){
15838                 labelCfg.cls += ' col-md-' + this.labelmd;
15839                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15840             }
15841             
15842             if(this.labelsm > 0){
15843                 labelCfg.cls += ' col-sm-' + this.labelsm;
15844                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15845             }
15846             
15847             if(this.labelxs > 0){
15848                 labelCfg.cls += ' col-xs-' + this.labelxs;
15849                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15850             }
15851                 
15852                 
15853         } else if ( this.fieldLabel.length) {
15854 //                Roo.log(" label");
15855                  cfg.cn = [
15856                    indicator,
15857                     {
15858                         tag: 'label',
15859                         //cls : 'input-group-addon',
15860                         html : this.fieldLabel
15861                     },
15862                     combobox
15863                 ];
15864                 
15865                 if(this.indicatorpos == 'right'){
15866                     cfg.cn = [
15867                         {
15868                             tag: 'label',
15869                             //cls : 'input-group-addon',
15870                             html : this.fieldLabel
15871                         },
15872                         indicator,
15873                         combobox
15874                     ];
15875                     
15876                 }
15877
15878         } else {
15879             
15880 //                Roo.log(" no label && no align");
15881                 cfg = combobox
15882                      
15883                 
15884         }
15885          
15886         var settings=this;
15887         ['xs','sm','md','lg'].map(function(size){
15888             if (settings[size]) {
15889                 cfg.cls += ' col-' + size + '-' + settings[size];
15890             }
15891         });
15892         
15893         return cfg;
15894         
15895     },
15896     
15897     _initEventsCalled : false,
15898     
15899     // private
15900     initEvents: function()
15901     {   
15902         if (this._initEventsCalled) { // as we call render... prevent looping...
15903             return;
15904         }
15905         this._initEventsCalled = true;
15906         
15907         if (!this.store) {
15908             throw "can not find store for combo";
15909         }
15910         
15911         this.indicator = this.indicatorEl();
15912         
15913         this.store = Roo.factory(this.store, Roo.data);
15914         this.store.parent = this;
15915         
15916         // if we are building from html. then this element is so complex, that we can not really
15917         // use the rendered HTML.
15918         // so we have to trash and replace the previous code.
15919         if (Roo.XComponent.build_from_html) {
15920             // remove this element....
15921             var e = this.el.dom, k=0;
15922             while (e ) { e = e.previousSibling;  ++k;}
15923
15924             this.el.remove();
15925             
15926             this.el=false;
15927             this.rendered = false;
15928             
15929             this.render(this.parent().getChildContainer(true), k);
15930         }
15931         
15932         if(Roo.isIOS && this.useNativeIOS){
15933             this.initIOSView();
15934             return;
15935         }
15936         
15937         /*
15938          * Touch Devices
15939          */
15940         
15941         if(Roo.isTouch && this.mobileTouchView){
15942             this.initTouchView();
15943             return;
15944         }
15945         
15946         if(this.tickable){
15947             this.initTickableEvents();
15948             return;
15949         }
15950         
15951         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15952         
15953         if(this.hiddenName){
15954             
15955             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15956             
15957             this.hiddenField.dom.value =
15958                 this.hiddenValue !== undefined ? this.hiddenValue :
15959                 this.value !== undefined ? this.value : '';
15960
15961             // prevent input submission
15962             this.el.dom.removeAttribute('name');
15963             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15964              
15965              
15966         }
15967         //if(Roo.isGecko){
15968         //    this.el.dom.setAttribute('autocomplete', 'off');
15969         //}
15970         
15971         var cls = 'x-combo-list';
15972         
15973         //this.list = new Roo.Layer({
15974         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15975         //});
15976         
15977         var _this = this;
15978         
15979         (function(){
15980             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15981             _this.list.setWidth(lw);
15982         }).defer(100);
15983         
15984         this.list.on('mouseover', this.onViewOver, this);
15985         this.list.on('mousemove', this.onViewMove, this);
15986         this.list.on('scroll', this.onViewScroll, this);
15987         
15988         /*
15989         this.list.swallowEvent('mousewheel');
15990         this.assetHeight = 0;
15991
15992         if(this.title){
15993             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15994             this.assetHeight += this.header.getHeight();
15995         }
15996
15997         this.innerList = this.list.createChild({cls:cls+'-inner'});
15998         this.innerList.on('mouseover', this.onViewOver, this);
15999         this.innerList.on('mousemove', this.onViewMove, this);
16000         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16001         
16002         if(this.allowBlank && !this.pageSize && !this.disableClear){
16003             this.footer = this.list.createChild({cls:cls+'-ft'});
16004             this.pageTb = new Roo.Toolbar(this.footer);
16005            
16006         }
16007         if(this.pageSize){
16008             this.footer = this.list.createChild({cls:cls+'-ft'});
16009             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16010                     {pageSize: this.pageSize});
16011             
16012         }
16013         
16014         if (this.pageTb && this.allowBlank && !this.disableClear) {
16015             var _this = this;
16016             this.pageTb.add(new Roo.Toolbar.Fill(), {
16017                 cls: 'x-btn-icon x-btn-clear',
16018                 text: '&#160;',
16019                 handler: function()
16020                 {
16021                     _this.collapse();
16022                     _this.clearValue();
16023                     _this.onSelect(false, -1);
16024                 }
16025             });
16026         }
16027         if (this.footer) {
16028             this.assetHeight += this.footer.getHeight();
16029         }
16030         */
16031             
16032         if(!this.tpl){
16033             this.tpl = Roo.bootstrap.version == 4 ?
16034                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16035                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16036         }
16037
16038         this.view = new Roo.View(this.list, this.tpl, {
16039             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16040         });
16041         //this.view.wrapEl.setDisplayed(false);
16042         this.view.on('click', this.onViewClick, this);
16043         
16044         
16045         this.store.on('beforeload', this.onBeforeLoad, this);
16046         this.store.on('load', this.onLoad, this);
16047         this.store.on('loadexception', this.onLoadException, this);
16048         /*
16049         if(this.resizable){
16050             this.resizer = new Roo.Resizable(this.list,  {
16051                pinned:true, handles:'se'
16052             });
16053             this.resizer.on('resize', function(r, w, h){
16054                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16055                 this.listWidth = w;
16056                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16057                 this.restrictHeight();
16058             }, this);
16059             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16060         }
16061         */
16062         if(!this.editable){
16063             this.editable = true;
16064             this.setEditable(false);
16065         }
16066         
16067         /*
16068         
16069         if (typeof(this.events.add.listeners) != 'undefined') {
16070             
16071             this.addicon = this.wrap.createChild(
16072                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16073        
16074             this.addicon.on('click', function(e) {
16075                 this.fireEvent('add', this);
16076             }, this);
16077         }
16078         if (typeof(this.events.edit.listeners) != 'undefined') {
16079             
16080             this.editicon = this.wrap.createChild(
16081                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16082             if (this.addicon) {
16083                 this.editicon.setStyle('margin-left', '40px');
16084             }
16085             this.editicon.on('click', function(e) {
16086                 
16087                 // we fire even  if inothing is selected..
16088                 this.fireEvent('edit', this, this.lastData );
16089                 
16090             }, this);
16091         }
16092         */
16093         
16094         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16095             "up" : function(e){
16096                 this.inKeyMode = true;
16097                 this.selectPrev();
16098             },
16099
16100             "down" : function(e){
16101                 if(!this.isExpanded()){
16102                     this.onTriggerClick();
16103                 }else{
16104                     this.inKeyMode = true;
16105                     this.selectNext();
16106                 }
16107             },
16108
16109             "enter" : function(e){
16110 //                this.onViewClick();
16111                 //return true;
16112                 this.collapse();
16113                 
16114                 if(this.fireEvent("specialkey", this, e)){
16115                     this.onViewClick(false);
16116                 }
16117                 
16118                 return true;
16119             },
16120
16121             "esc" : function(e){
16122                 this.collapse();
16123             },
16124
16125             "tab" : function(e){
16126                 this.collapse();
16127                 
16128                 if(this.fireEvent("specialkey", this, e)){
16129                     this.onViewClick(false);
16130                 }
16131                 
16132                 return true;
16133             },
16134
16135             scope : this,
16136
16137             doRelay : function(foo, bar, hname){
16138                 if(hname == 'down' || this.scope.isExpanded()){
16139                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16140                 }
16141                 return true;
16142             },
16143
16144             forceKeyDown: true
16145         });
16146         
16147         
16148         this.queryDelay = Math.max(this.queryDelay || 10,
16149                 this.mode == 'local' ? 10 : 250);
16150         
16151         
16152         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16153         
16154         if(this.typeAhead){
16155             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16156         }
16157         if(this.editable !== false){
16158             this.inputEl().on("keyup", this.onKeyUp, this);
16159         }
16160         if(this.forceSelection){
16161             this.inputEl().on('blur', this.doForce, this);
16162         }
16163         
16164         if(this.multiple){
16165             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16166             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16167         }
16168     },
16169     
16170     initTickableEvents: function()
16171     {   
16172         this.createList();
16173         
16174         if(this.hiddenName){
16175             
16176             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16177             
16178             this.hiddenField.dom.value =
16179                 this.hiddenValue !== undefined ? this.hiddenValue :
16180                 this.value !== undefined ? this.value : '';
16181
16182             // prevent input submission
16183             this.el.dom.removeAttribute('name');
16184             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16185              
16186              
16187         }
16188         
16189 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16190         
16191         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16192         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16193         if(this.triggerList){
16194             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16195         }
16196          
16197         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16198         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16199         
16200         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16201         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16202         
16203         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16204         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16205         
16206         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16207         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16208         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16209         
16210         this.okBtn.hide();
16211         this.cancelBtn.hide();
16212         
16213         var _this = this;
16214         
16215         (function(){
16216             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16217             _this.list.setWidth(lw);
16218         }).defer(100);
16219         
16220         this.list.on('mouseover', this.onViewOver, this);
16221         this.list.on('mousemove', this.onViewMove, this);
16222         
16223         this.list.on('scroll', this.onViewScroll, this);
16224         
16225         if(!this.tpl){
16226             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16227                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16228         }
16229
16230         this.view = new Roo.View(this.list, this.tpl, {
16231             singleSelect:true,
16232             tickable:true,
16233             parent:this,
16234             store: this.store,
16235             selectedClass: this.selectedClass
16236         });
16237         
16238         //this.view.wrapEl.setDisplayed(false);
16239         this.view.on('click', this.onViewClick, this);
16240         
16241         
16242         
16243         this.store.on('beforeload', this.onBeforeLoad, this);
16244         this.store.on('load', this.onLoad, this);
16245         this.store.on('loadexception', this.onLoadException, this);
16246         
16247         if(this.editable){
16248             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16249                 "up" : function(e){
16250                     this.inKeyMode = true;
16251                     this.selectPrev();
16252                 },
16253
16254                 "down" : function(e){
16255                     this.inKeyMode = true;
16256                     this.selectNext();
16257                 },
16258
16259                 "enter" : function(e){
16260                     if(this.fireEvent("specialkey", this, e)){
16261                         this.onViewClick(false);
16262                     }
16263                     
16264                     return true;
16265                 },
16266
16267                 "esc" : function(e){
16268                     this.onTickableFooterButtonClick(e, false, false);
16269                 },
16270
16271                 "tab" : function(e){
16272                     this.fireEvent("specialkey", this, e);
16273                     
16274                     this.onTickableFooterButtonClick(e, false, false);
16275                     
16276                     return true;
16277                 },
16278
16279                 scope : this,
16280
16281                 doRelay : function(e, fn, key){
16282                     if(this.scope.isExpanded()){
16283                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16284                     }
16285                     return true;
16286                 },
16287
16288                 forceKeyDown: true
16289             });
16290         }
16291         
16292         this.queryDelay = Math.max(this.queryDelay || 10,
16293                 this.mode == 'local' ? 10 : 250);
16294         
16295         
16296         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16297         
16298         if(this.typeAhead){
16299             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16300         }
16301         
16302         if(this.editable !== false){
16303             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16304         }
16305         
16306         this.indicator = this.indicatorEl();
16307         
16308         if(this.indicator){
16309             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16310             this.indicator.hide();
16311         }
16312         
16313     },
16314
16315     onDestroy : function(){
16316         if(this.view){
16317             this.view.setStore(null);
16318             this.view.el.removeAllListeners();
16319             this.view.el.remove();
16320             this.view.purgeListeners();
16321         }
16322         if(this.list){
16323             this.list.dom.innerHTML  = '';
16324         }
16325         
16326         if(this.store){
16327             this.store.un('beforeload', this.onBeforeLoad, this);
16328             this.store.un('load', this.onLoad, this);
16329             this.store.un('loadexception', this.onLoadException, this);
16330         }
16331         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16332     },
16333
16334     // private
16335     fireKey : function(e){
16336         if(e.isNavKeyPress() && !this.list.isVisible()){
16337             this.fireEvent("specialkey", this, e);
16338         }
16339     },
16340
16341     // private
16342     onResize: function(w, h)
16343     {
16344         
16345         
16346 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16347 //        
16348 //        if(typeof w != 'number'){
16349 //            // we do not handle it!?!?
16350 //            return;
16351 //        }
16352 //        var tw = this.trigger.getWidth();
16353 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16354 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16355 //        var x = w - tw;
16356 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16357 //            
16358 //        //this.trigger.setStyle('left', x+'px');
16359 //        
16360 //        if(this.list && this.listWidth === undefined){
16361 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16362 //            this.list.setWidth(lw);
16363 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16364 //        }
16365         
16366     
16367         
16368     },
16369
16370     /**
16371      * Allow or prevent the user from directly editing the field text.  If false is passed,
16372      * the user will only be able to select from the items defined in the dropdown list.  This method
16373      * is the runtime equivalent of setting the 'editable' config option at config time.
16374      * @param {Boolean} value True to allow the user to directly edit the field text
16375      */
16376     setEditable : function(value){
16377         if(value == this.editable){
16378             return;
16379         }
16380         this.editable = value;
16381         if(!value){
16382             this.inputEl().dom.setAttribute('readOnly', true);
16383             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16384             this.inputEl().addClass('x-combo-noedit');
16385         }else{
16386             this.inputEl().dom.removeAttribute('readOnly');
16387             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16388             this.inputEl().removeClass('x-combo-noedit');
16389         }
16390     },
16391
16392     // private
16393     
16394     onBeforeLoad : function(combo,opts){
16395         if(!this.hasFocus){
16396             return;
16397         }
16398          if (!opts.add) {
16399             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16400          }
16401         this.restrictHeight();
16402         this.selectedIndex = -1;
16403     },
16404
16405     // private
16406     onLoad : function(){
16407         
16408         this.hasQuery = false;
16409         
16410         if(!this.hasFocus){
16411             return;
16412         }
16413         
16414         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16415             this.loading.hide();
16416         }
16417         
16418         if(this.store.getCount() > 0){
16419             
16420             this.expand();
16421             this.restrictHeight();
16422             if(this.lastQuery == this.allQuery){
16423                 if(this.editable && !this.tickable){
16424                     this.inputEl().dom.select();
16425                 }
16426                 
16427                 if(
16428                     !this.selectByValue(this.value, true) &&
16429                     this.autoFocus && 
16430                     (
16431                         !this.store.lastOptions ||
16432                         typeof(this.store.lastOptions.add) == 'undefined' || 
16433                         this.store.lastOptions.add != true
16434                     )
16435                 ){
16436                     this.select(0, true);
16437                 }
16438             }else{
16439                 if(this.autoFocus){
16440                     this.selectNext();
16441                 }
16442                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16443                     this.taTask.delay(this.typeAheadDelay);
16444                 }
16445             }
16446         }else{
16447             this.onEmptyResults();
16448         }
16449         
16450         //this.el.focus();
16451     },
16452     // private
16453     onLoadException : function()
16454     {
16455         this.hasQuery = false;
16456         
16457         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16458             this.loading.hide();
16459         }
16460         
16461         if(this.tickable && this.editable){
16462             return;
16463         }
16464         
16465         this.collapse();
16466         // only causes errors at present
16467         //Roo.log(this.store.reader.jsonData);
16468         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16469             // fixme
16470             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16471         //}
16472         
16473         
16474     },
16475     // private
16476     onTypeAhead : function(){
16477         if(this.store.getCount() > 0){
16478             var r = this.store.getAt(0);
16479             var newValue = r.data[this.displayField];
16480             var len = newValue.length;
16481             var selStart = this.getRawValue().length;
16482             
16483             if(selStart != len){
16484                 this.setRawValue(newValue);
16485                 this.selectText(selStart, newValue.length);
16486             }
16487         }
16488     },
16489
16490     // private
16491     onSelect : function(record, index){
16492         
16493         if(this.fireEvent('beforeselect', this, record, index) !== false){
16494         
16495             this.setFromData(index > -1 ? record.data : false);
16496             
16497             this.collapse();
16498             this.fireEvent('select', this, record, index);
16499         }
16500     },
16501
16502     /**
16503      * Returns the currently selected field value or empty string if no value is set.
16504      * @return {String} value The selected value
16505      */
16506     getValue : function()
16507     {
16508         if(Roo.isIOS && this.useNativeIOS){
16509             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16510         }
16511         
16512         if(this.multiple){
16513             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16514         }
16515         
16516         if(this.valueField){
16517             return typeof this.value != 'undefined' ? this.value : '';
16518         }else{
16519             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16520         }
16521     },
16522     
16523     getRawValue : function()
16524     {
16525         if(Roo.isIOS && this.useNativeIOS){
16526             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16527         }
16528         
16529         var v = this.inputEl().getValue();
16530         
16531         return v;
16532     },
16533
16534     /**
16535      * Clears any text/value currently set in the field
16536      */
16537     clearValue : function(){
16538         
16539         if(this.hiddenField){
16540             this.hiddenField.dom.value = '';
16541         }
16542         this.value = '';
16543         this.setRawValue('');
16544         this.lastSelectionText = '';
16545         this.lastData = false;
16546         
16547         var close = this.closeTriggerEl();
16548         
16549         if(close){
16550             close.hide();
16551         }
16552         
16553         this.validate();
16554         
16555     },
16556
16557     /**
16558      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16559      * will be displayed in the field.  If the value does not match the data value of an existing item,
16560      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16561      * Otherwise the field will be blank (although the value will still be set).
16562      * @param {String} value The value to match
16563      */
16564     setValue : function(v)
16565     {
16566         if(Roo.isIOS && this.useNativeIOS){
16567             this.setIOSValue(v);
16568             return;
16569         }
16570         
16571         if(this.multiple){
16572             this.syncValue();
16573             return;
16574         }
16575         
16576         var text = v;
16577         if(this.valueField){
16578             var r = this.findRecord(this.valueField, v);
16579             if(r){
16580                 text = r.data[this.displayField];
16581             }else if(this.valueNotFoundText !== undefined){
16582                 text = this.valueNotFoundText;
16583             }
16584         }
16585         this.lastSelectionText = text;
16586         if(this.hiddenField){
16587             this.hiddenField.dom.value = v;
16588         }
16589         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16590         this.value = v;
16591         
16592         var close = this.closeTriggerEl();
16593         
16594         if(close){
16595             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16596         }
16597         
16598         this.validate();
16599     },
16600     /**
16601      * @property {Object} the last set data for the element
16602      */
16603     
16604     lastData : false,
16605     /**
16606      * Sets the value of the field based on a object which is related to the record format for the store.
16607      * @param {Object} value the value to set as. or false on reset?
16608      */
16609     setFromData : function(o){
16610         
16611         if(this.multiple){
16612             this.addItem(o);
16613             return;
16614         }
16615             
16616         var dv = ''; // display value
16617         var vv = ''; // value value..
16618         this.lastData = o;
16619         if (this.displayField) {
16620             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16621         } else {
16622             // this is an error condition!!!
16623             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16624         }
16625         
16626         if(this.valueField){
16627             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16628         }
16629         
16630         var close = this.closeTriggerEl();
16631         
16632         if(close){
16633             if(dv.length || vv * 1 > 0){
16634                 close.show() ;
16635                 this.blockFocus=true;
16636             } else {
16637                 close.hide();
16638             }             
16639         }
16640         
16641         if(this.hiddenField){
16642             this.hiddenField.dom.value = vv;
16643             
16644             this.lastSelectionText = dv;
16645             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16646             this.value = vv;
16647             return;
16648         }
16649         // no hidden field.. - we store the value in 'value', but still display
16650         // display field!!!!
16651         this.lastSelectionText = dv;
16652         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16653         this.value = vv;
16654         
16655         
16656         
16657     },
16658     // private
16659     reset : function(){
16660         // overridden so that last data is reset..
16661         
16662         if(this.multiple){
16663             this.clearItem();
16664             return;
16665         }
16666         
16667         this.setValue(this.originalValue);
16668         //this.clearInvalid();
16669         this.lastData = false;
16670         if (this.view) {
16671             this.view.clearSelections();
16672         }
16673         
16674         this.validate();
16675     },
16676     // private
16677     findRecord : function(prop, value){
16678         var record;
16679         if(this.store.getCount() > 0){
16680             this.store.each(function(r){
16681                 if(r.data[prop] == value){
16682                     record = r;
16683                     return false;
16684                 }
16685                 return true;
16686             });
16687         }
16688         return record;
16689     },
16690     
16691     getName: function()
16692     {
16693         // returns hidden if it's set..
16694         if (!this.rendered) {return ''};
16695         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16696         
16697     },
16698     // private
16699     onViewMove : function(e, t){
16700         this.inKeyMode = false;
16701     },
16702
16703     // private
16704     onViewOver : function(e, t){
16705         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16706             return;
16707         }
16708         var item = this.view.findItemFromChild(t);
16709         
16710         if(item){
16711             var index = this.view.indexOf(item);
16712             this.select(index, false);
16713         }
16714     },
16715
16716     // private
16717     onViewClick : function(view, doFocus, el, e)
16718     {
16719         var index = this.view.getSelectedIndexes()[0];
16720         
16721         var r = this.store.getAt(index);
16722         
16723         if(this.tickable){
16724             
16725             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16726                 return;
16727             }
16728             
16729             var rm = false;
16730             var _this = this;
16731             
16732             Roo.each(this.tickItems, function(v,k){
16733                 
16734                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16735                     Roo.log(v);
16736                     _this.tickItems.splice(k, 1);
16737                     
16738                     if(typeof(e) == 'undefined' && view == false){
16739                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16740                     }
16741                     
16742                     rm = true;
16743                     return;
16744                 }
16745             });
16746             
16747             if(rm){
16748                 return;
16749             }
16750             
16751             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16752                 this.tickItems.push(r.data);
16753             }
16754             
16755             if(typeof(e) == 'undefined' && view == false){
16756                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16757             }
16758                     
16759             return;
16760         }
16761         
16762         if(r){
16763             this.onSelect(r, index);
16764         }
16765         if(doFocus !== false && !this.blockFocus){
16766             this.inputEl().focus();
16767         }
16768     },
16769
16770     // private
16771     restrictHeight : function(){
16772         //this.innerList.dom.style.height = '';
16773         //var inner = this.innerList.dom;
16774         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16775         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16776         //this.list.beginUpdate();
16777         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16778         this.list.alignTo(this.inputEl(), this.listAlign);
16779         this.list.alignTo(this.inputEl(), this.listAlign);
16780         //this.list.endUpdate();
16781     },
16782
16783     // private
16784     onEmptyResults : function(){
16785         
16786         if(this.tickable && this.editable){
16787             this.hasFocus = false;
16788             this.restrictHeight();
16789             return;
16790         }
16791         
16792         this.collapse();
16793     },
16794
16795     /**
16796      * Returns true if the dropdown list is expanded, else false.
16797      */
16798     isExpanded : function(){
16799         return this.list.isVisible();
16800     },
16801
16802     /**
16803      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16804      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16805      * @param {String} value The data value of the item to select
16806      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16807      * selected item if it is not currently in view (defaults to true)
16808      * @return {Boolean} True if the value matched an item in the list, else false
16809      */
16810     selectByValue : function(v, scrollIntoView){
16811         if(v !== undefined && v !== null){
16812             var r = this.findRecord(this.valueField || this.displayField, v);
16813             if(r){
16814                 this.select(this.store.indexOf(r), scrollIntoView);
16815                 return true;
16816             }
16817         }
16818         return false;
16819     },
16820
16821     /**
16822      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16823      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16824      * @param {Number} index The zero-based index of the list item to select
16825      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16826      * selected item if it is not currently in view (defaults to true)
16827      */
16828     select : function(index, scrollIntoView){
16829         this.selectedIndex = index;
16830         this.view.select(index);
16831         if(scrollIntoView !== false){
16832             var el = this.view.getNode(index);
16833             /*
16834              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16835              */
16836             if(el){
16837                 this.list.scrollChildIntoView(el, false);
16838             }
16839         }
16840     },
16841
16842     // private
16843     selectNext : function(){
16844         var ct = this.store.getCount();
16845         if(ct > 0){
16846             if(this.selectedIndex == -1){
16847                 this.select(0);
16848             }else if(this.selectedIndex < ct-1){
16849                 this.select(this.selectedIndex+1);
16850             }
16851         }
16852     },
16853
16854     // private
16855     selectPrev : function(){
16856         var ct = this.store.getCount();
16857         if(ct > 0){
16858             if(this.selectedIndex == -1){
16859                 this.select(0);
16860             }else if(this.selectedIndex != 0){
16861                 this.select(this.selectedIndex-1);
16862             }
16863         }
16864     },
16865
16866     // private
16867     onKeyUp : function(e){
16868         if(this.editable !== false && !e.isSpecialKey()){
16869             this.lastKey = e.getKey();
16870             this.dqTask.delay(this.queryDelay);
16871         }
16872     },
16873
16874     // private
16875     validateBlur : function(){
16876         return !this.list || !this.list.isVisible();   
16877     },
16878
16879     // private
16880     initQuery : function(){
16881         
16882         var v = this.getRawValue();
16883         
16884         if(this.tickable && this.editable){
16885             v = this.tickableInputEl().getValue();
16886         }
16887         
16888         this.doQuery(v);
16889     },
16890
16891     // private
16892     doForce : function(){
16893         if(this.inputEl().dom.value.length > 0){
16894             this.inputEl().dom.value =
16895                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16896              
16897         }
16898     },
16899
16900     /**
16901      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16902      * query allowing the query action to be canceled if needed.
16903      * @param {String} query The SQL query to execute
16904      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16905      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16906      * saved in the current store (defaults to false)
16907      */
16908     doQuery : function(q, forceAll){
16909         
16910         if(q === undefined || q === null){
16911             q = '';
16912         }
16913         var qe = {
16914             query: q,
16915             forceAll: forceAll,
16916             combo: this,
16917             cancel:false
16918         };
16919         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16920             return false;
16921         }
16922         q = qe.query;
16923         
16924         forceAll = qe.forceAll;
16925         if(forceAll === true || (q.length >= this.minChars)){
16926             
16927             this.hasQuery = true;
16928             
16929             if(this.lastQuery != q || this.alwaysQuery){
16930                 this.lastQuery = q;
16931                 if(this.mode == 'local'){
16932                     this.selectedIndex = -1;
16933                     if(forceAll){
16934                         this.store.clearFilter();
16935                     }else{
16936                         
16937                         if(this.specialFilter){
16938                             this.fireEvent('specialfilter', this);
16939                             this.onLoad();
16940                             return;
16941                         }
16942                         
16943                         this.store.filter(this.displayField, q);
16944                     }
16945                     
16946                     this.store.fireEvent("datachanged", this.store);
16947                     
16948                     this.onLoad();
16949                     
16950                     
16951                 }else{
16952                     
16953                     this.store.baseParams[this.queryParam] = q;
16954                     
16955                     var options = {params : this.getParams(q)};
16956                     
16957                     if(this.loadNext){
16958                         options.add = true;
16959                         options.params.start = this.page * this.pageSize;
16960                     }
16961                     
16962                     this.store.load(options);
16963                     
16964                     /*
16965                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16966                      *  we should expand the list on onLoad
16967                      *  so command out it
16968                      */
16969 //                    this.expand();
16970                 }
16971             }else{
16972                 this.selectedIndex = -1;
16973                 this.onLoad();   
16974             }
16975         }
16976         
16977         this.loadNext = false;
16978     },
16979     
16980     // private
16981     getParams : function(q){
16982         var p = {};
16983         //p[this.queryParam] = q;
16984         
16985         if(this.pageSize){
16986             p.start = 0;
16987             p.limit = this.pageSize;
16988         }
16989         return p;
16990     },
16991
16992     /**
16993      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16994      */
16995     collapse : function(){
16996         if(!this.isExpanded()){
16997             return;
16998         }
16999         
17000         this.list.hide();
17001         
17002         this.hasFocus = false;
17003         
17004         if(this.tickable){
17005             this.okBtn.hide();
17006             this.cancelBtn.hide();
17007             this.trigger.show();
17008             
17009             if(this.editable){
17010                 this.tickableInputEl().dom.value = '';
17011                 this.tickableInputEl().blur();
17012             }
17013             
17014         }
17015         
17016         Roo.get(document).un('mousedown', this.collapseIf, this);
17017         Roo.get(document).un('mousewheel', this.collapseIf, this);
17018         if (!this.editable) {
17019             Roo.get(document).un('keydown', this.listKeyPress, this);
17020         }
17021         this.fireEvent('collapse', this);
17022         
17023         this.validate();
17024     },
17025
17026     // private
17027     collapseIf : function(e){
17028         var in_combo  = e.within(this.el);
17029         var in_list =  e.within(this.list);
17030         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17031         
17032         if (in_combo || in_list || is_list) {
17033             //e.stopPropagation();
17034             return;
17035         }
17036         
17037         if(this.tickable){
17038             this.onTickableFooterButtonClick(e, false, false);
17039         }
17040
17041         this.collapse();
17042         
17043     },
17044
17045     /**
17046      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17047      */
17048     expand : function(){
17049        
17050         if(this.isExpanded() || !this.hasFocus){
17051             return;
17052         }
17053         
17054         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17055         this.list.setWidth(lw);
17056         
17057         Roo.log('expand');
17058         
17059         this.list.show();
17060         
17061         this.restrictHeight();
17062         
17063         if(this.tickable){
17064             
17065             this.tickItems = Roo.apply([], this.item);
17066             
17067             this.okBtn.show();
17068             this.cancelBtn.show();
17069             this.trigger.hide();
17070             
17071             if(this.editable){
17072                 this.tickableInputEl().focus();
17073             }
17074             
17075         }
17076         
17077         Roo.get(document).on('mousedown', this.collapseIf, this);
17078         Roo.get(document).on('mousewheel', this.collapseIf, this);
17079         if (!this.editable) {
17080             Roo.get(document).on('keydown', this.listKeyPress, this);
17081         }
17082         
17083         this.fireEvent('expand', this);
17084     },
17085
17086     // private
17087     // Implements the default empty TriggerField.onTriggerClick function
17088     onTriggerClick : function(e)
17089     {
17090         Roo.log('trigger click');
17091         
17092         if(this.disabled || !this.triggerList){
17093             return;
17094         }
17095         
17096         this.page = 0;
17097         this.loadNext = false;
17098         
17099         if(this.isExpanded()){
17100             this.collapse();
17101             if (!this.blockFocus) {
17102                 this.inputEl().focus();
17103             }
17104             
17105         }else {
17106             this.hasFocus = true;
17107             if(this.triggerAction == 'all') {
17108                 this.doQuery(this.allQuery, true);
17109             } else {
17110                 this.doQuery(this.getRawValue());
17111             }
17112             if (!this.blockFocus) {
17113                 this.inputEl().focus();
17114             }
17115         }
17116     },
17117     
17118     onTickableTriggerClick : function(e)
17119     {
17120         if(this.disabled){
17121             return;
17122         }
17123         
17124         this.page = 0;
17125         this.loadNext = false;
17126         this.hasFocus = true;
17127         
17128         if(this.triggerAction == 'all') {
17129             this.doQuery(this.allQuery, true);
17130         } else {
17131             this.doQuery(this.getRawValue());
17132         }
17133     },
17134     
17135     onSearchFieldClick : function(e)
17136     {
17137         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17138             this.onTickableFooterButtonClick(e, false, false);
17139             return;
17140         }
17141         
17142         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17143             return;
17144         }
17145         
17146         this.page = 0;
17147         this.loadNext = false;
17148         this.hasFocus = true;
17149         
17150         if(this.triggerAction == 'all') {
17151             this.doQuery(this.allQuery, true);
17152         } else {
17153             this.doQuery(this.getRawValue());
17154         }
17155     },
17156     
17157     listKeyPress : function(e)
17158     {
17159         //Roo.log('listkeypress');
17160         // scroll to first matching element based on key pres..
17161         if (e.isSpecialKey()) {
17162             return false;
17163         }
17164         var k = String.fromCharCode(e.getKey()).toUpperCase();
17165         //Roo.log(k);
17166         var match  = false;
17167         var csel = this.view.getSelectedNodes();
17168         var cselitem = false;
17169         if (csel.length) {
17170             var ix = this.view.indexOf(csel[0]);
17171             cselitem  = this.store.getAt(ix);
17172             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17173                 cselitem = false;
17174             }
17175             
17176         }
17177         
17178         this.store.each(function(v) { 
17179             if (cselitem) {
17180                 // start at existing selection.
17181                 if (cselitem.id == v.id) {
17182                     cselitem = false;
17183                 }
17184                 return true;
17185             }
17186                 
17187             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17188                 match = this.store.indexOf(v);
17189                 return false;
17190             }
17191             return true;
17192         }, this);
17193         
17194         if (match === false) {
17195             return true; // no more action?
17196         }
17197         // scroll to?
17198         this.view.select(match);
17199         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17200         sn.scrollIntoView(sn.dom.parentNode, false);
17201     },
17202     
17203     onViewScroll : function(e, t){
17204         
17205         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){
17206             return;
17207         }
17208         
17209         this.hasQuery = true;
17210         
17211         this.loading = this.list.select('.loading', true).first();
17212         
17213         if(this.loading === null){
17214             this.list.createChild({
17215                 tag: 'div',
17216                 cls: 'loading roo-select2-more-results roo-select2-active',
17217                 html: 'Loading more results...'
17218             });
17219             
17220             this.loading = this.list.select('.loading', true).first();
17221             
17222             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17223             
17224             this.loading.hide();
17225         }
17226         
17227         this.loading.show();
17228         
17229         var _combo = this;
17230         
17231         this.page++;
17232         this.loadNext = true;
17233         
17234         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17235         
17236         return;
17237     },
17238     
17239     addItem : function(o)
17240     {   
17241         var dv = ''; // display value
17242         
17243         if (this.displayField) {
17244             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17245         } else {
17246             // this is an error condition!!!
17247             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17248         }
17249         
17250         if(!dv.length){
17251             return;
17252         }
17253         
17254         var choice = this.choices.createChild({
17255             tag: 'li',
17256             cls: 'roo-select2-search-choice',
17257             cn: [
17258                 {
17259                     tag: 'div',
17260                     html: dv
17261                 },
17262                 {
17263                     tag: 'a',
17264                     href: '#',
17265                     cls: 'roo-select2-search-choice-close fa fa-times',
17266                     tabindex: '-1'
17267                 }
17268             ]
17269             
17270         }, this.searchField);
17271         
17272         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17273         
17274         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17275         
17276         this.item.push(o);
17277         
17278         this.lastData = o;
17279         
17280         this.syncValue();
17281         
17282         this.inputEl().dom.value = '';
17283         
17284         this.validate();
17285     },
17286     
17287     onRemoveItem : function(e, _self, o)
17288     {
17289         e.preventDefault();
17290         
17291         this.lastItem = Roo.apply([], this.item);
17292         
17293         var index = this.item.indexOf(o.data) * 1;
17294         
17295         if( index < 0){
17296             Roo.log('not this item?!');
17297             return;
17298         }
17299         
17300         this.item.splice(index, 1);
17301         o.item.remove();
17302         
17303         this.syncValue();
17304         
17305         this.fireEvent('remove', this, e);
17306         
17307         this.validate();
17308         
17309     },
17310     
17311     syncValue : function()
17312     {
17313         if(!this.item.length){
17314             this.clearValue();
17315             return;
17316         }
17317             
17318         var value = [];
17319         var _this = this;
17320         Roo.each(this.item, function(i){
17321             if(_this.valueField){
17322                 value.push(i[_this.valueField]);
17323                 return;
17324             }
17325
17326             value.push(i);
17327         });
17328
17329         this.value = value.join(',');
17330
17331         if(this.hiddenField){
17332             this.hiddenField.dom.value = this.value;
17333         }
17334         
17335         this.store.fireEvent("datachanged", this.store);
17336         
17337         this.validate();
17338     },
17339     
17340     clearItem : function()
17341     {
17342         if(!this.multiple){
17343             return;
17344         }
17345         
17346         this.item = [];
17347         
17348         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17349            c.remove();
17350         });
17351         
17352         this.syncValue();
17353         
17354         this.validate();
17355         
17356         if(this.tickable && !Roo.isTouch){
17357             this.view.refresh();
17358         }
17359     },
17360     
17361     inputEl: function ()
17362     {
17363         if(Roo.isIOS && this.useNativeIOS){
17364             return this.el.select('select.roo-ios-select', true).first();
17365         }
17366         
17367         if(Roo.isTouch && this.mobileTouchView){
17368             return this.el.select('input.form-control',true).first();
17369         }
17370         
17371         if(this.tickable){
17372             return this.searchField;
17373         }
17374         
17375         return this.el.select('input.form-control',true).first();
17376     },
17377     
17378     onTickableFooterButtonClick : function(e, btn, el)
17379     {
17380         e.preventDefault();
17381         
17382         this.lastItem = Roo.apply([], this.item);
17383         
17384         if(btn && btn.name == 'cancel'){
17385             this.tickItems = Roo.apply([], this.item);
17386             this.collapse();
17387             return;
17388         }
17389         
17390         this.clearItem();
17391         
17392         var _this = this;
17393         
17394         Roo.each(this.tickItems, function(o){
17395             _this.addItem(o);
17396         });
17397         
17398         this.collapse();
17399         
17400     },
17401     
17402     validate : function()
17403     {
17404         if(this.getVisibilityEl().hasClass('hidden')){
17405             return true;
17406         }
17407         
17408         var v = this.getRawValue();
17409         
17410         if(this.multiple){
17411             v = this.getValue();
17412         }
17413         
17414         if(this.disabled || this.allowBlank || v.length){
17415             this.markValid();
17416             return true;
17417         }
17418         
17419         this.markInvalid();
17420         return false;
17421     },
17422     
17423     tickableInputEl : function()
17424     {
17425         if(!this.tickable || !this.editable){
17426             return this.inputEl();
17427         }
17428         
17429         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17430     },
17431     
17432     
17433     getAutoCreateTouchView : function()
17434     {
17435         var id = Roo.id();
17436         
17437         var cfg = {
17438             cls: 'form-group' //input-group
17439         };
17440         
17441         var input =  {
17442             tag: 'input',
17443             id : id,
17444             type : this.inputType,
17445             cls : 'form-control x-combo-noedit',
17446             autocomplete: 'new-password',
17447             placeholder : this.placeholder || '',
17448             readonly : true
17449         };
17450         
17451         if (this.name) {
17452             input.name = this.name;
17453         }
17454         
17455         if (this.size) {
17456             input.cls += ' input-' + this.size;
17457         }
17458         
17459         if (this.disabled) {
17460             input.disabled = true;
17461         }
17462         
17463         var inputblock = {
17464             cls : 'roo-combobox-wrap',
17465             cn : [
17466                 input
17467             ]
17468         };
17469         
17470         if(this.before){
17471             inputblock.cls += ' input-group';
17472             
17473             inputblock.cn.unshift({
17474                 tag :'span',
17475                 cls : 'input-group-addon input-group-prepend input-group-text',
17476                 html : this.before
17477             });
17478         }
17479         
17480         if(this.removable && !this.multiple){
17481             inputblock.cls += ' roo-removable';
17482             
17483             inputblock.cn.push({
17484                 tag: 'button',
17485                 html : 'x',
17486                 cls : 'roo-combo-removable-btn close'
17487             });
17488         }
17489
17490         if(this.hasFeedback && !this.allowBlank){
17491             
17492             inputblock.cls += ' has-feedback';
17493             
17494             inputblock.cn.push({
17495                 tag: 'span',
17496                 cls: 'glyphicon form-control-feedback'
17497             });
17498             
17499         }
17500         
17501         if (this.after) {
17502             
17503             inputblock.cls += (this.before) ? '' : ' input-group';
17504             
17505             inputblock.cn.push({
17506                 tag :'span',
17507                 cls : 'input-group-addon input-group-append input-group-text',
17508                 html : this.after
17509             });
17510         }
17511
17512         
17513         var ibwrap = inputblock;
17514         
17515         if(this.multiple){
17516             ibwrap = {
17517                 tag: 'ul',
17518                 cls: 'roo-select2-choices',
17519                 cn:[
17520                     {
17521                         tag: 'li',
17522                         cls: 'roo-select2-search-field',
17523                         cn: [
17524
17525                             inputblock
17526                         ]
17527                     }
17528                 ]
17529             };
17530         
17531             
17532         }
17533         
17534         var combobox = {
17535             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17536             cn: [
17537                 {
17538                     tag: 'input',
17539                     type : 'hidden',
17540                     cls: 'form-hidden-field'
17541                 },
17542                 ibwrap
17543             ]
17544         };
17545         
17546         if(!this.multiple && this.showToggleBtn){
17547             
17548             var caret = {
17549                 cls: 'caret'
17550             };
17551             
17552             if (this.caret != false) {
17553                 caret = {
17554                      tag: 'i',
17555                      cls: 'fa fa-' + this.caret
17556                 };
17557                 
17558             }
17559             
17560             combobox.cn.push({
17561                 tag :'span',
17562                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17563                 cn : [
17564                     Roo.bootstrap.version == 3 ? caret : '',
17565                     {
17566                         tag: 'span',
17567                         cls: 'combobox-clear',
17568                         cn  : [
17569                             {
17570                                 tag : 'i',
17571                                 cls: 'icon-remove'
17572                             }
17573                         ]
17574                     }
17575                 ]
17576
17577             })
17578         }
17579         
17580         if(this.multiple){
17581             combobox.cls += ' roo-select2-container-multi';
17582         }
17583         
17584         var align = this.labelAlign || this.parentLabelAlign();
17585         
17586         if (align ==='left' && this.fieldLabel.length) {
17587
17588             cfg.cn = [
17589                 {
17590                    tag : 'i',
17591                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17592                    tooltip : 'This field is required'
17593                 },
17594                 {
17595                     tag: 'label',
17596                     cls : 'control-label col-form-label',
17597                     html : this.fieldLabel
17598
17599                 },
17600                 {
17601                     cls : 'roo-combobox-wrap ', 
17602                     cn: [
17603                         combobox
17604                     ]
17605                 }
17606             ];
17607             
17608             var labelCfg = cfg.cn[1];
17609             var contentCfg = cfg.cn[2];
17610             
17611
17612             if(this.indicatorpos == 'right'){
17613                 cfg.cn = [
17614                     {
17615                         tag: 'label',
17616                         'for' :  id,
17617                         cls : 'control-label col-form-label',
17618                         cn : [
17619                             {
17620                                 tag : 'span',
17621                                 html : this.fieldLabel
17622                             },
17623                             {
17624                                 tag : 'i',
17625                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17626                                 tooltip : 'This field is required'
17627                             }
17628                         ]
17629                     },
17630                     {
17631                         cls : "roo-combobox-wrap ",
17632                         cn: [
17633                             combobox
17634                         ]
17635                     }
17636
17637                 ];
17638                 
17639                 labelCfg = cfg.cn[0];
17640                 contentCfg = cfg.cn[1];
17641             }
17642             
17643            
17644             
17645             if(this.labelWidth > 12){
17646                 labelCfg.style = "width: " + this.labelWidth + 'px';
17647             }
17648            
17649             if(this.labelWidth < 13 && this.labelmd == 0){
17650                 this.labelmd = this.labelWidth;
17651             }
17652             
17653             if(this.labellg > 0){
17654                 labelCfg.cls += ' col-lg-' + this.labellg;
17655                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17656             }
17657             
17658             if(this.labelmd > 0){
17659                 labelCfg.cls += ' col-md-' + this.labelmd;
17660                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17661             }
17662             
17663             if(this.labelsm > 0){
17664                 labelCfg.cls += ' col-sm-' + this.labelsm;
17665                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17666             }
17667             
17668             if(this.labelxs > 0){
17669                 labelCfg.cls += ' col-xs-' + this.labelxs;
17670                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17671             }
17672                 
17673                 
17674         } else if ( this.fieldLabel.length) {
17675             cfg.cn = [
17676                 {
17677                    tag : 'i',
17678                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17679                    tooltip : 'This field is required'
17680                 },
17681                 {
17682                     tag: 'label',
17683                     cls : 'control-label',
17684                     html : this.fieldLabel
17685
17686                 },
17687                 {
17688                     cls : '', 
17689                     cn: [
17690                         combobox
17691                     ]
17692                 }
17693             ];
17694             
17695             if(this.indicatorpos == 'right'){
17696                 cfg.cn = [
17697                     {
17698                         tag: 'label',
17699                         cls : 'control-label',
17700                         html : this.fieldLabel,
17701                         cn : [
17702                             {
17703                                tag : 'i',
17704                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17705                                tooltip : 'This field is required'
17706                             }
17707                         ]
17708                     },
17709                     {
17710                         cls : '', 
17711                         cn: [
17712                             combobox
17713                         ]
17714                     }
17715                 ];
17716             }
17717         } else {
17718             cfg.cn = combobox;    
17719         }
17720         
17721         
17722         var settings = this;
17723         
17724         ['xs','sm','md','lg'].map(function(size){
17725             if (settings[size]) {
17726                 cfg.cls += ' col-' + size + '-' + settings[size];
17727             }
17728         });
17729         
17730         return cfg;
17731     },
17732     
17733     initTouchView : function()
17734     {
17735         this.renderTouchView();
17736         
17737         this.touchViewEl.on('scroll', function(){
17738             this.el.dom.scrollTop = 0;
17739         }, this);
17740         
17741         this.originalValue = this.getValue();
17742         
17743         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17744         
17745         this.inputEl().on("click", this.showTouchView, this);
17746         if (this.triggerEl) {
17747             this.triggerEl.on("click", this.showTouchView, this);
17748         }
17749         
17750         
17751         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17752         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17753         
17754         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17755         
17756         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17757         this.store.on('load', this.onTouchViewLoad, this);
17758         this.store.on('loadexception', this.onTouchViewLoadException, this);
17759         
17760         if(this.hiddenName){
17761             
17762             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17763             
17764             this.hiddenField.dom.value =
17765                 this.hiddenValue !== undefined ? this.hiddenValue :
17766                 this.value !== undefined ? this.value : '';
17767         
17768             this.el.dom.removeAttribute('name');
17769             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17770         }
17771         
17772         if(this.multiple){
17773             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17774             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17775         }
17776         
17777         if(this.removable && !this.multiple){
17778             var close = this.closeTriggerEl();
17779             if(close){
17780                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17781                 close.on('click', this.removeBtnClick, this, close);
17782             }
17783         }
17784         /*
17785          * fix the bug in Safari iOS8
17786          */
17787         this.inputEl().on("focus", function(e){
17788             document.activeElement.blur();
17789         }, this);
17790         
17791         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17792         
17793         return;
17794         
17795         
17796     },
17797     
17798     renderTouchView : function()
17799     {
17800         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17801         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17802         
17803         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17804         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17805         
17806         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17807         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17808         this.touchViewBodyEl.setStyle('overflow', 'auto');
17809         
17810         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17811         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17812         
17813         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17814         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17815         
17816     },
17817     
17818     showTouchView : function()
17819     {
17820         if(this.disabled){
17821             return;
17822         }
17823         
17824         this.touchViewHeaderEl.hide();
17825
17826         if(this.modalTitle.length){
17827             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17828             this.touchViewHeaderEl.show();
17829         }
17830
17831         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17832         this.touchViewEl.show();
17833
17834         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17835         
17836         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17837         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17838
17839         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17840
17841         if(this.modalTitle.length){
17842             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17843         }
17844         
17845         this.touchViewBodyEl.setHeight(bodyHeight);
17846
17847         if(this.animate){
17848             var _this = this;
17849             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17850         }else{
17851             this.touchViewEl.addClass(['in','show']);
17852         }
17853         
17854         if(this._touchViewMask){
17855             Roo.get(document.body).addClass("x-body-masked");
17856             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17857             this._touchViewMask.setStyle('z-index', 10000);
17858             this._touchViewMask.addClass('show');
17859         }
17860         
17861         this.doTouchViewQuery();
17862         
17863     },
17864     
17865     hideTouchView : function()
17866     {
17867         this.touchViewEl.removeClass(['in','show']);
17868
17869         if(this.animate){
17870             var _this = this;
17871             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17872         }else{
17873             this.touchViewEl.setStyle('display', 'none');
17874         }
17875         
17876         if(this._touchViewMask){
17877             this._touchViewMask.removeClass('show');
17878             Roo.get(document.body).removeClass("x-body-masked");
17879         }
17880     },
17881     
17882     setTouchViewValue : function()
17883     {
17884         if(this.multiple){
17885             this.clearItem();
17886         
17887             var _this = this;
17888
17889             Roo.each(this.tickItems, function(o){
17890                 this.addItem(o);
17891             }, this);
17892         }
17893         
17894         this.hideTouchView();
17895     },
17896     
17897     doTouchViewQuery : function()
17898     {
17899         var qe = {
17900             query: '',
17901             forceAll: true,
17902             combo: this,
17903             cancel:false
17904         };
17905         
17906         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17907             return false;
17908         }
17909         
17910         if(!this.alwaysQuery || this.mode == 'local'){
17911             this.onTouchViewLoad();
17912             return;
17913         }
17914         
17915         this.store.load();
17916     },
17917     
17918     onTouchViewBeforeLoad : function(combo,opts)
17919     {
17920         return;
17921     },
17922
17923     // private
17924     onTouchViewLoad : function()
17925     {
17926         if(this.store.getCount() < 1){
17927             this.onTouchViewEmptyResults();
17928             return;
17929         }
17930         
17931         this.clearTouchView();
17932         
17933         var rawValue = this.getRawValue();
17934         
17935         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17936         
17937         this.tickItems = [];
17938         
17939         this.store.data.each(function(d, rowIndex){
17940             var row = this.touchViewListGroup.createChild(template);
17941             
17942             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17943                 row.addClass(d.data.cls);
17944             }
17945             
17946             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17947                 var cfg = {
17948                     data : d.data,
17949                     html : d.data[this.displayField]
17950                 };
17951                 
17952                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17953                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17954                 }
17955             }
17956             row.removeClass('selected');
17957             if(!this.multiple && this.valueField &&
17958                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17959             {
17960                 // radio buttons..
17961                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17962                 row.addClass('selected');
17963             }
17964             
17965             if(this.multiple && this.valueField &&
17966                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17967             {
17968                 
17969                 // checkboxes...
17970                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17971                 this.tickItems.push(d.data);
17972             }
17973             
17974             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17975             
17976         }, this);
17977         
17978         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17979         
17980         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17981
17982         if(this.modalTitle.length){
17983             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17984         }
17985
17986         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17987         
17988         if(this.mobile_restrict_height && listHeight < bodyHeight){
17989             this.touchViewBodyEl.setHeight(listHeight);
17990         }
17991         
17992         var _this = this;
17993         
17994         if(firstChecked && listHeight > bodyHeight){
17995             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17996         }
17997         
17998     },
17999     
18000     onTouchViewLoadException : function()
18001     {
18002         this.hideTouchView();
18003     },
18004     
18005     onTouchViewEmptyResults : function()
18006     {
18007         this.clearTouchView();
18008         
18009         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18010         
18011         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18012         
18013     },
18014     
18015     clearTouchView : function()
18016     {
18017         this.touchViewListGroup.dom.innerHTML = '';
18018     },
18019     
18020     onTouchViewClick : function(e, el, o)
18021     {
18022         e.preventDefault();
18023         
18024         var row = o.row;
18025         var rowIndex = o.rowIndex;
18026         
18027         var r = this.store.getAt(rowIndex);
18028         
18029         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18030             
18031             if(!this.multiple){
18032                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18033                     c.dom.removeAttribute('checked');
18034                 }, this);
18035
18036                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18037
18038                 this.setFromData(r.data);
18039
18040                 var close = this.closeTriggerEl();
18041
18042                 if(close){
18043                     close.show();
18044                 }
18045
18046                 this.hideTouchView();
18047
18048                 this.fireEvent('select', this, r, rowIndex);
18049
18050                 return;
18051             }
18052
18053             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18054                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18055                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18056                 return;
18057             }
18058
18059             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18060             this.addItem(r.data);
18061             this.tickItems.push(r.data);
18062         }
18063     },
18064     
18065     getAutoCreateNativeIOS : function()
18066     {
18067         var cfg = {
18068             cls: 'form-group' //input-group,
18069         };
18070         
18071         var combobox =  {
18072             tag: 'select',
18073             cls : 'roo-ios-select'
18074         };
18075         
18076         if (this.name) {
18077             combobox.name = this.name;
18078         }
18079         
18080         if (this.disabled) {
18081             combobox.disabled = true;
18082         }
18083         
18084         var settings = this;
18085         
18086         ['xs','sm','md','lg'].map(function(size){
18087             if (settings[size]) {
18088                 cfg.cls += ' col-' + size + '-' + settings[size];
18089             }
18090         });
18091         
18092         cfg.cn = combobox;
18093         
18094         return cfg;
18095         
18096     },
18097     
18098     initIOSView : function()
18099     {
18100         this.store.on('load', this.onIOSViewLoad, this);
18101         
18102         return;
18103     },
18104     
18105     onIOSViewLoad : function()
18106     {
18107         if(this.store.getCount() < 1){
18108             return;
18109         }
18110         
18111         this.clearIOSView();
18112         
18113         if(this.allowBlank) {
18114             
18115             var default_text = '-- SELECT --';
18116             
18117             if(this.placeholder.length){
18118                 default_text = this.placeholder;
18119             }
18120             
18121             if(this.emptyTitle.length){
18122                 default_text += ' - ' + this.emptyTitle + ' -';
18123             }
18124             
18125             var opt = this.inputEl().createChild({
18126                 tag: 'option',
18127                 value : 0,
18128                 html : default_text
18129             });
18130             
18131             var o = {};
18132             o[this.valueField] = 0;
18133             o[this.displayField] = default_text;
18134             
18135             this.ios_options.push({
18136                 data : o,
18137                 el : opt
18138             });
18139             
18140         }
18141         
18142         this.store.data.each(function(d, rowIndex){
18143             
18144             var html = '';
18145             
18146             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18147                 html = d.data[this.displayField];
18148             }
18149             
18150             var value = '';
18151             
18152             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18153                 value = d.data[this.valueField];
18154             }
18155             
18156             var option = {
18157                 tag: 'option',
18158                 value : value,
18159                 html : html
18160             };
18161             
18162             if(this.value == d.data[this.valueField]){
18163                 option['selected'] = true;
18164             }
18165             
18166             var opt = this.inputEl().createChild(option);
18167             
18168             this.ios_options.push({
18169                 data : d.data,
18170                 el : opt
18171             });
18172             
18173         }, this);
18174         
18175         this.inputEl().on('change', function(){
18176            this.fireEvent('select', this);
18177         }, this);
18178         
18179     },
18180     
18181     clearIOSView: function()
18182     {
18183         this.inputEl().dom.innerHTML = '';
18184         
18185         this.ios_options = [];
18186     },
18187     
18188     setIOSValue: function(v)
18189     {
18190         this.value = v;
18191         
18192         if(!this.ios_options){
18193             return;
18194         }
18195         
18196         Roo.each(this.ios_options, function(opts){
18197            
18198            opts.el.dom.removeAttribute('selected');
18199            
18200            if(opts.data[this.valueField] != v){
18201                return;
18202            }
18203            
18204            opts.el.dom.setAttribute('selected', true);
18205            
18206         }, this);
18207     }
18208
18209     /** 
18210     * @cfg {Boolean} grow 
18211     * @hide 
18212     */
18213     /** 
18214     * @cfg {Number} growMin 
18215     * @hide 
18216     */
18217     /** 
18218     * @cfg {Number} growMax 
18219     * @hide 
18220     */
18221     /**
18222      * @hide
18223      * @method autoSize
18224      */
18225 });
18226
18227 Roo.apply(Roo.bootstrap.ComboBox,  {
18228     
18229     header : {
18230         tag: 'div',
18231         cls: 'modal-header',
18232         cn: [
18233             {
18234                 tag: 'h4',
18235                 cls: 'modal-title'
18236             }
18237         ]
18238     },
18239     
18240     body : {
18241         tag: 'div',
18242         cls: 'modal-body',
18243         cn: [
18244             {
18245                 tag: 'ul',
18246                 cls: 'list-group'
18247             }
18248         ]
18249     },
18250     
18251     listItemRadio : {
18252         tag: 'li',
18253         cls: 'list-group-item',
18254         cn: [
18255             {
18256                 tag: 'span',
18257                 cls: 'roo-combobox-list-group-item-value'
18258             },
18259             {
18260                 tag: 'div',
18261                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18262                 cn: [
18263                     {
18264                         tag: 'input',
18265                         type: 'radio'
18266                     },
18267                     {
18268                         tag: 'label'
18269                     }
18270                 ]
18271             }
18272         ]
18273     },
18274     
18275     listItemCheckbox : {
18276         tag: 'li',
18277         cls: 'list-group-item',
18278         cn: [
18279             {
18280                 tag: 'span',
18281                 cls: 'roo-combobox-list-group-item-value'
18282             },
18283             {
18284                 tag: 'div',
18285                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18286                 cn: [
18287                     {
18288                         tag: 'input',
18289                         type: 'checkbox'
18290                     },
18291                     {
18292                         tag: 'label'
18293                     }
18294                 ]
18295             }
18296         ]
18297     },
18298     
18299     emptyResult : {
18300         tag: 'div',
18301         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18302     },
18303     
18304     footer : {
18305         tag: 'div',
18306         cls: 'modal-footer',
18307         cn: [
18308             {
18309                 tag: 'div',
18310                 cls: 'row',
18311                 cn: [
18312                     {
18313                         tag: 'div',
18314                         cls: 'col-xs-6 text-left',
18315                         cn: {
18316                             tag: 'button',
18317                             cls: 'btn btn-danger roo-touch-view-cancel',
18318                             html: 'Cancel'
18319                         }
18320                     },
18321                     {
18322                         tag: 'div',
18323                         cls: 'col-xs-6 text-right',
18324                         cn: {
18325                             tag: 'button',
18326                             cls: 'btn btn-success roo-touch-view-ok',
18327                             html: 'OK'
18328                         }
18329                     }
18330                 ]
18331             }
18332         ]
18333         
18334     }
18335 });
18336
18337 Roo.apply(Roo.bootstrap.ComboBox,  {
18338     
18339     touchViewTemplate : {
18340         tag: 'div',
18341         cls: 'modal fade roo-combobox-touch-view',
18342         cn: [
18343             {
18344                 tag: 'div',
18345                 cls: 'modal-dialog',
18346                 style : 'position:fixed', // we have to fix position....
18347                 cn: [
18348                     {
18349                         tag: 'div',
18350                         cls: 'modal-content',
18351                         cn: [
18352                             Roo.bootstrap.ComboBox.header,
18353                             Roo.bootstrap.ComboBox.body,
18354                             Roo.bootstrap.ComboBox.footer
18355                         ]
18356                     }
18357                 ]
18358             }
18359         ]
18360     }
18361 });/*
18362  * Based on:
18363  * Ext JS Library 1.1.1
18364  * Copyright(c) 2006-2007, Ext JS, LLC.
18365  *
18366  * Originally Released Under LGPL - original licence link has changed is not relivant.
18367  *
18368  * Fork - LGPL
18369  * <script type="text/javascript">
18370  */
18371
18372 /**
18373  * @class Roo.View
18374  * @extends Roo.util.Observable
18375  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18376  * This class also supports single and multi selection modes. <br>
18377  * Create a data model bound view:
18378  <pre><code>
18379  var store = new Roo.data.Store(...);
18380
18381  var view = new Roo.View({
18382     el : "my-element",
18383     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18384  
18385     singleSelect: true,
18386     selectedClass: "ydataview-selected",
18387     store: store
18388  });
18389
18390  // listen for node click?
18391  view.on("click", function(vw, index, node, e){
18392  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18393  });
18394
18395  // load XML data
18396  dataModel.load("foobar.xml");
18397  </code></pre>
18398  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18399  * <br><br>
18400  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18401  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18402  * 
18403  * Note: old style constructor is still suported (container, template, config)
18404  * 
18405  * @constructor
18406  * Create a new View
18407  * @param {Object} config The config object
18408  * 
18409  */
18410 Roo.View = function(config, depreciated_tpl, depreciated_config){
18411     
18412     this.parent = false;
18413     
18414     if (typeof(depreciated_tpl) == 'undefined') {
18415         // new way.. - universal constructor.
18416         Roo.apply(this, config);
18417         this.el  = Roo.get(this.el);
18418     } else {
18419         // old format..
18420         this.el  = Roo.get(config);
18421         this.tpl = depreciated_tpl;
18422         Roo.apply(this, depreciated_config);
18423     }
18424     this.wrapEl  = this.el.wrap().wrap();
18425     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18426     
18427     
18428     if(typeof(this.tpl) == "string"){
18429         this.tpl = new Roo.Template(this.tpl);
18430     } else {
18431         // support xtype ctors..
18432         this.tpl = new Roo.factory(this.tpl, Roo);
18433     }
18434     
18435     
18436     this.tpl.compile();
18437     
18438     /** @private */
18439     this.addEvents({
18440         /**
18441          * @event beforeclick
18442          * Fires before a click is processed. Returns false to cancel the default action.
18443          * @param {Roo.View} this
18444          * @param {Number} index The index of the target node
18445          * @param {HTMLElement} node The target node
18446          * @param {Roo.EventObject} e The raw event object
18447          */
18448             "beforeclick" : true,
18449         /**
18450          * @event click
18451          * Fires when a template node is clicked.
18452          * @param {Roo.View} this
18453          * @param {Number} index The index of the target node
18454          * @param {HTMLElement} node The target node
18455          * @param {Roo.EventObject} e The raw event object
18456          */
18457             "click" : true,
18458         /**
18459          * @event dblclick
18460          * Fires when a template node is double clicked.
18461          * @param {Roo.View} this
18462          * @param {Number} index The index of the target node
18463          * @param {HTMLElement} node The target node
18464          * @param {Roo.EventObject} e The raw event object
18465          */
18466             "dblclick" : true,
18467         /**
18468          * @event contextmenu
18469          * Fires when a template node is right clicked.
18470          * @param {Roo.View} this
18471          * @param {Number} index The index of the target node
18472          * @param {HTMLElement} node The target node
18473          * @param {Roo.EventObject} e The raw event object
18474          */
18475             "contextmenu" : true,
18476         /**
18477          * @event selectionchange
18478          * Fires when the selected nodes change.
18479          * @param {Roo.View} this
18480          * @param {Array} selections Array of the selected nodes
18481          */
18482             "selectionchange" : true,
18483     
18484         /**
18485          * @event beforeselect
18486          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18487          * @param {Roo.View} this
18488          * @param {HTMLElement} node The node to be selected
18489          * @param {Array} selections Array of currently selected nodes
18490          */
18491             "beforeselect" : true,
18492         /**
18493          * @event preparedata
18494          * Fires on every row to render, to allow you to change the data.
18495          * @param {Roo.View} this
18496          * @param {Object} data to be rendered (change this)
18497          */
18498           "preparedata" : true
18499           
18500           
18501         });
18502
18503
18504
18505     this.el.on({
18506         "click": this.onClick,
18507         "dblclick": this.onDblClick,
18508         "contextmenu": this.onContextMenu,
18509         scope:this
18510     });
18511
18512     this.selections = [];
18513     this.nodes = [];
18514     this.cmp = new Roo.CompositeElementLite([]);
18515     if(this.store){
18516         this.store = Roo.factory(this.store, Roo.data);
18517         this.setStore(this.store, true);
18518     }
18519     
18520     if ( this.footer && this.footer.xtype) {
18521            
18522          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18523         
18524         this.footer.dataSource = this.store;
18525         this.footer.container = fctr;
18526         this.footer = Roo.factory(this.footer, Roo);
18527         fctr.insertFirst(this.el);
18528         
18529         // this is a bit insane - as the paging toolbar seems to detach the el..
18530 //        dom.parentNode.parentNode.parentNode
18531          // they get detached?
18532     }
18533     
18534     
18535     Roo.View.superclass.constructor.call(this);
18536     
18537     
18538 };
18539
18540 Roo.extend(Roo.View, Roo.util.Observable, {
18541     
18542      /**
18543      * @cfg {Roo.data.Store} store Data store to load data from.
18544      */
18545     store : false,
18546     
18547     /**
18548      * @cfg {String|Roo.Element} el The container element.
18549      */
18550     el : '',
18551     
18552     /**
18553      * @cfg {String|Roo.Template} tpl The template used by this View 
18554      */
18555     tpl : false,
18556     /**
18557      * @cfg {String} dataName the named area of the template to use as the data area
18558      *                          Works with domtemplates roo-name="name"
18559      */
18560     dataName: false,
18561     /**
18562      * @cfg {String} selectedClass The css class to add to selected nodes
18563      */
18564     selectedClass : "x-view-selected",
18565      /**
18566      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18567      */
18568     emptyText : "",
18569     
18570     /**
18571      * @cfg {String} text to display on mask (default Loading)
18572      */
18573     mask : false,
18574     /**
18575      * @cfg {Boolean} multiSelect Allow multiple selection
18576      */
18577     multiSelect : false,
18578     /**
18579      * @cfg {Boolean} singleSelect Allow single selection
18580      */
18581     singleSelect:  false,
18582     
18583     /**
18584      * @cfg {Boolean} toggleSelect - selecting 
18585      */
18586     toggleSelect : false,
18587     
18588     /**
18589      * @cfg {Boolean} tickable - selecting 
18590      */
18591     tickable : false,
18592     
18593     /**
18594      * Returns the element this view is bound to.
18595      * @return {Roo.Element}
18596      */
18597     getEl : function(){
18598         return this.wrapEl;
18599     },
18600     
18601     
18602
18603     /**
18604      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18605      */
18606     refresh : function(){
18607         //Roo.log('refresh');
18608         var t = this.tpl;
18609         
18610         // if we are using something like 'domtemplate', then
18611         // the what gets used is:
18612         // t.applySubtemplate(NAME, data, wrapping data..)
18613         // the outer template then get' applied with
18614         //     the store 'extra data'
18615         // and the body get's added to the
18616         //      roo-name="data" node?
18617         //      <span class='roo-tpl-{name}'></span> ?????
18618         
18619         
18620         
18621         this.clearSelections();
18622         this.el.update("");
18623         var html = [];
18624         var records = this.store.getRange();
18625         if(records.length < 1) {
18626             
18627             // is this valid??  = should it render a template??
18628             
18629             this.el.update(this.emptyText);
18630             return;
18631         }
18632         var el = this.el;
18633         if (this.dataName) {
18634             this.el.update(t.apply(this.store.meta)); //????
18635             el = this.el.child('.roo-tpl-' + this.dataName);
18636         }
18637         
18638         for(var i = 0, len = records.length; i < len; i++){
18639             var data = this.prepareData(records[i].data, i, records[i]);
18640             this.fireEvent("preparedata", this, data, i, records[i]);
18641             
18642             var d = Roo.apply({}, data);
18643             
18644             if(this.tickable){
18645                 Roo.apply(d, {'roo-id' : Roo.id()});
18646                 
18647                 var _this = this;
18648             
18649                 Roo.each(this.parent.item, function(item){
18650                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18651                         return;
18652                     }
18653                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18654                 });
18655             }
18656             
18657             html[html.length] = Roo.util.Format.trim(
18658                 this.dataName ?
18659                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18660                     t.apply(d)
18661             );
18662         }
18663         
18664         
18665         
18666         el.update(html.join(""));
18667         this.nodes = el.dom.childNodes;
18668         this.updateIndexes(0);
18669     },
18670     
18671
18672     /**
18673      * Function to override to reformat the data that is sent to
18674      * the template for each node.
18675      * DEPRICATED - use the preparedata event handler.
18676      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18677      * a JSON object for an UpdateManager bound view).
18678      */
18679     prepareData : function(data, index, record)
18680     {
18681         this.fireEvent("preparedata", this, data, index, record);
18682         return data;
18683     },
18684
18685     onUpdate : function(ds, record){
18686         // Roo.log('on update');   
18687         this.clearSelections();
18688         var index = this.store.indexOf(record);
18689         var n = this.nodes[index];
18690         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18691         n.parentNode.removeChild(n);
18692         this.updateIndexes(index, index);
18693     },
18694
18695     
18696     
18697 // --------- FIXME     
18698     onAdd : function(ds, records, index)
18699     {
18700         //Roo.log(['on Add', ds, records, index] );        
18701         this.clearSelections();
18702         if(this.nodes.length == 0){
18703             this.refresh();
18704             return;
18705         }
18706         var n = this.nodes[index];
18707         for(var i = 0, len = records.length; i < len; i++){
18708             var d = this.prepareData(records[i].data, i, records[i]);
18709             if(n){
18710                 this.tpl.insertBefore(n, d);
18711             }else{
18712                 
18713                 this.tpl.append(this.el, d);
18714             }
18715         }
18716         this.updateIndexes(index);
18717     },
18718
18719     onRemove : function(ds, record, index){
18720        // Roo.log('onRemove');
18721         this.clearSelections();
18722         var el = this.dataName  ?
18723             this.el.child('.roo-tpl-' + this.dataName) :
18724             this.el; 
18725         
18726         el.dom.removeChild(this.nodes[index]);
18727         this.updateIndexes(index);
18728     },
18729
18730     /**
18731      * Refresh an individual node.
18732      * @param {Number} index
18733      */
18734     refreshNode : function(index){
18735         this.onUpdate(this.store, this.store.getAt(index));
18736     },
18737
18738     updateIndexes : function(startIndex, endIndex){
18739         var ns = this.nodes;
18740         startIndex = startIndex || 0;
18741         endIndex = endIndex || ns.length - 1;
18742         for(var i = startIndex; i <= endIndex; i++){
18743             ns[i].nodeIndex = i;
18744         }
18745     },
18746
18747     /**
18748      * Changes the data store this view uses and refresh the view.
18749      * @param {Store} store
18750      */
18751     setStore : function(store, initial){
18752         if(!initial && this.store){
18753             this.store.un("datachanged", this.refresh);
18754             this.store.un("add", this.onAdd);
18755             this.store.un("remove", this.onRemove);
18756             this.store.un("update", this.onUpdate);
18757             this.store.un("clear", this.refresh);
18758             this.store.un("beforeload", this.onBeforeLoad);
18759             this.store.un("load", this.onLoad);
18760             this.store.un("loadexception", this.onLoad);
18761         }
18762         if(store){
18763           
18764             store.on("datachanged", this.refresh, this);
18765             store.on("add", this.onAdd, this);
18766             store.on("remove", this.onRemove, this);
18767             store.on("update", this.onUpdate, this);
18768             store.on("clear", this.refresh, this);
18769             store.on("beforeload", this.onBeforeLoad, this);
18770             store.on("load", this.onLoad, this);
18771             store.on("loadexception", this.onLoad, this);
18772         }
18773         
18774         if(store){
18775             this.refresh();
18776         }
18777     },
18778     /**
18779      * onbeforeLoad - masks the loading area.
18780      *
18781      */
18782     onBeforeLoad : function(store,opts)
18783     {
18784          //Roo.log('onBeforeLoad');   
18785         if (!opts.add) {
18786             this.el.update("");
18787         }
18788         this.el.mask(this.mask ? this.mask : "Loading" ); 
18789     },
18790     onLoad : function ()
18791     {
18792         this.el.unmask();
18793     },
18794     
18795
18796     /**
18797      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18798      * @param {HTMLElement} node
18799      * @return {HTMLElement} The template node
18800      */
18801     findItemFromChild : function(node){
18802         var el = this.dataName  ?
18803             this.el.child('.roo-tpl-' + this.dataName,true) :
18804             this.el.dom; 
18805         
18806         if(!node || node.parentNode == el){
18807                     return node;
18808             }
18809             var p = node.parentNode;
18810             while(p && p != el){
18811             if(p.parentNode == el){
18812                 return p;
18813             }
18814             p = p.parentNode;
18815         }
18816             return null;
18817     },
18818
18819     /** @ignore */
18820     onClick : function(e){
18821         var item = this.findItemFromChild(e.getTarget());
18822         if(item){
18823             var index = this.indexOf(item);
18824             if(this.onItemClick(item, index, e) !== false){
18825                 this.fireEvent("click", this, index, item, e);
18826             }
18827         }else{
18828             this.clearSelections();
18829         }
18830     },
18831
18832     /** @ignore */
18833     onContextMenu : function(e){
18834         var item = this.findItemFromChild(e.getTarget());
18835         if(item){
18836             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18837         }
18838     },
18839
18840     /** @ignore */
18841     onDblClick : function(e){
18842         var item = this.findItemFromChild(e.getTarget());
18843         if(item){
18844             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18845         }
18846     },
18847
18848     onItemClick : function(item, index, e)
18849     {
18850         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18851             return false;
18852         }
18853         if (this.toggleSelect) {
18854             var m = this.isSelected(item) ? 'unselect' : 'select';
18855             //Roo.log(m);
18856             var _t = this;
18857             _t[m](item, true, false);
18858             return true;
18859         }
18860         if(this.multiSelect || this.singleSelect){
18861             if(this.multiSelect && e.shiftKey && this.lastSelection){
18862                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18863             }else{
18864                 this.select(item, this.multiSelect && e.ctrlKey);
18865                 this.lastSelection = item;
18866             }
18867             
18868             if(!this.tickable){
18869                 e.preventDefault();
18870             }
18871             
18872         }
18873         return true;
18874     },
18875
18876     /**
18877      * Get the number of selected nodes.
18878      * @return {Number}
18879      */
18880     getSelectionCount : function(){
18881         return this.selections.length;
18882     },
18883
18884     /**
18885      * Get the currently selected nodes.
18886      * @return {Array} An array of HTMLElements
18887      */
18888     getSelectedNodes : function(){
18889         return this.selections;
18890     },
18891
18892     /**
18893      * Get the indexes of the selected nodes.
18894      * @return {Array}
18895      */
18896     getSelectedIndexes : function(){
18897         var indexes = [], s = this.selections;
18898         for(var i = 0, len = s.length; i < len; i++){
18899             indexes.push(s[i].nodeIndex);
18900         }
18901         return indexes;
18902     },
18903
18904     /**
18905      * Clear all selections
18906      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18907      */
18908     clearSelections : function(suppressEvent){
18909         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18910             this.cmp.elements = this.selections;
18911             this.cmp.removeClass(this.selectedClass);
18912             this.selections = [];
18913             if(!suppressEvent){
18914                 this.fireEvent("selectionchange", this, this.selections);
18915             }
18916         }
18917     },
18918
18919     /**
18920      * Returns true if the passed node is selected
18921      * @param {HTMLElement/Number} node The node or node index
18922      * @return {Boolean}
18923      */
18924     isSelected : function(node){
18925         var s = this.selections;
18926         if(s.length < 1){
18927             return false;
18928         }
18929         node = this.getNode(node);
18930         return s.indexOf(node) !== -1;
18931     },
18932
18933     /**
18934      * Selects nodes.
18935      * @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
18936      * @param {Boolean} keepExisting (optional) true to keep existing selections
18937      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18938      */
18939     select : function(nodeInfo, keepExisting, suppressEvent){
18940         if(nodeInfo instanceof Array){
18941             if(!keepExisting){
18942                 this.clearSelections(true);
18943             }
18944             for(var i = 0, len = nodeInfo.length; i < len; i++){
18945                 this.select(nodeInfo[i], true, true);
18946             }
18947             return;
18948         } 
18949         var node = this.getNode(nodeInfo);
18950         if(!node || this.isSelected(node)){
18951             return; // already selected.
18952         }
18953         if(!keepExisting){
18954             this.clearSelections(true);
18955         }
18956         
18957         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18958             Roo.fly(node).addClass(this.selectedClass);
18959             this.selections.push(node);
18960             if(!suppressEvent){
18961                 this.fireEvent("selectionchange", this, this.selections);
18962             }
18963         }
18964         
18965         
18966     },
18967       /**
18968      * Unselects nodes.
18969      * @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
18970      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18971      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18972      */
18973     unselect : function(nodeInfo, keepExisting, suppressEvent)
18974     {
18975         if(nodeInfo instanceof Array){
18976             Roo.each(this.selections, function(s) {
18977                 this.unselect(s, nodeInfo);
18978             }, this);
18979             return;
18980         }
18981         var node = this.getNode(nodeInfo);
18982         if(!node || !this.isSelected(node)){
18983             //Roo.log("not selected");
18984             return; // not selected.
18985         }
18986         // fireevent???
18987         var ns = [];
18988         Roo.each(this.selections, function(s) {
18989             if (s == node ) {
18990                 Roo.fly(node).removeClass(this.selectedClass);
18991
18992                 return;
18993             }
18994             ns.push(s);
18995         },this);
18996         
18997         this.selections= ns;
18998         this.fireEvent("selectionchange", this, this.selections);
18999     },
19000
19001     /**
19002      * Gets a template node.
19003      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19004      * @return {HTMLElement} The node or null if it wasn't found
19005      */
19006     getNode : function(nodeInfo){
19007         if(typeof nodeInfo == "string"){
19008             return document.getElementById(nodeInfo);
19009         }else if(typeof nodeInfo == "number"){
19010             return this.nodes[nodeInfo];
19011         }
19012         return nodeInfo;
19013     },
19014
19015     /**
19016      * Gets a range template nodes.
19017      * @param {Number} startIndex
19018      * @param {Number} endIndex
19019      * @return {Array} An array of nodes
19020      */
19021     getNodes : function(start, end){
19022         var ns = this.nodes;
19023         start = start || 0;
19024         end = typeof end == "undefined" ? ns.length - 1 : end;
19025         var nodes = [];
19026         if(start <= end){
19027             for(var i = start; i <= end; i++){
19028                 nodes.push(ns[i]);
19029             }
19030         } else{
19031             for(var i = start; i >= end; i--){
19032                 nodes.push(ns[i]);
19033             }
19034         }
19035         return nodes;
19036     },
19037
19038     /**
19039      * Finds the index of the passed node
19040      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19041      * @return {Number} The index of the node or -1
19042      */
19043     indexOf : function(node){
19044         node = this.getNode(node);
19045         if(typeof node.nodeIndex == "number"){
19046             return node.nodeIndex;
19047         }
19048         var ns = this.nodes;
19049         for(var i = 0, len = ns.length; i < len; i++){
19050             if(ns[i] == node){
19051                 return i;
19052             }
19053         }
19054         return -1;
19055     }
19056 });
19057 /*
19058  * - LGPL
19059  *
19060  * based on jquery fullcalendar
19061  * 
19062  */
19063
19064 Roo.bootstrap = Roo.bootstrap || {};
19065 /**
19066  * @class Roo.bootstrap.Calendar
19067  * @extends Roo.bootstrap.Component
19068  * Bootstrap Calendar class
19069  * @cfg {Boolean} loadMask (true|false) default false
19070  * @cfg {Object} header generate the user specific header of the calendar, default false
19071
19072  * @constructor
19073  * Create a new Container
19074  * @param {Object} config The config object
19075  */
19076
19077
19078
19079 Roo.bootstrap.Calendar = function(config){
19080     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19081      this.addEvents({
19082         /**
19083              * @event select
19084              * Fires when a date is selected
19085              * @param {DatePicker} this
19086              * @param {Date} date The selected date
19087              */
19088         'select': true,
19089         /**
19090              * @event monthchange
19091              * Fires when the displayed month changes 
19092              * @param {DatePicker} this
19093              * @param {Date} date The selected month
19094              */
19095         'monthchange': true,
19096         /**
19097              * @event evententer
19098              * Fires when mouse over an event
19099              * @param {Calendar} this
19100              * @param {event} Event
19101              */
19102         'evententer': true,
19103         /**
19104              * @event eventleave
19105              * Fires when the mouse leaves an
19106              * @param {Calendar} this
19107              * @param {event}
19108              */
19109         'eventleave': true,
19110         /**
19111              * @event eventclick
19112              * Fires when the mouse click an
19113              * @param {Calendar} this
19114              * @param {event}
19115              */
19116         'eventclick': true
19117         
19118     });
19119
19120 };
19121
19122 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19123     
19124      /**
19125      * @cfg {Number} startDay
19126      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19127      */
19128     startDay : 0,
19129     
19130     loadMask : false,
19131     
19132     header : false,
19133       
19134     getAutoCreate : function(){
19135         
19136         
19137         var fc_button = function(name, corner, style, content ) {
19138             return Roo.apply({},{
19139                 tag : 'span',
19140                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19141                          (corner.length ?
19142                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19143                             ''
19144                         ),
19145                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19146                 unselectable: 'on'
19147             });
19148         };
19149         
19150         var header = {};
19151         
19152         if(!this.header){
19153             header = {
19154                 tag : 'table',
19155                 cls : 'fc-header',
19156                 style : 'width:100%',
19157                 cn : [
19158                     {
19159                         tag: 'tr',
19160                         cn : [
19161                             {
19162                                 tag : 'td',
19163                                 cls : 'fc-header-left',
19164                                 cn : [
19165                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19166                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19167                                     { tag: 'span', cls: 'fc-header-space' },
19168                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19169
19170
19171                                 ]
19172                             },
19173
19174                             {
19175                                 tag : 'td',
19176                                 cls : 'fc-header-center',
19177                                 cn : [
19178                                     {
19179                                         tag: 'span',
19180                                         cls: 'fc-header-title',
19181                                         cn : {
19182                                             tag: 'H2',
19183                                             html : 'month / year'
19184                                         }
19185                                     }
19186
19187                                 ]
19188                             },
19189                             {
19190                                 tag : 'td',
19191                                 cls : 'fc-header-right',
19192                                 cn : [
19193                               /*      fc_button('month', 'left', '', 'month' ),
19194                                     fc_button('week', '', '', 'week' ),
19195                                     fc_button('day', 'right', '', 'day' )
19196                                 */    
19197
19198                                 ]
19199                             }
19200
19201                         ]
19202                     }
19203                 ]
19204             };
19205         }
19206         
19207         header = this.header;
19208         
19209        
19210         var cal_heads = function() {
19211             var ret = [];
19212             // fixme - handle this.
19213             
19214             for (var i =0; i < Date.dayNames.length; i++) {
19215                 var d = Date.dayNames[i];
19216                 ret.push({
19217                     tag: 'th',
19218                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19219                     html : d.substring(0,3)
19220                 });
19221                 
19222             }
19223             ret[0].cls += ' fc-first';
19224             ret[6].cls += ' fc-last';
19225             return ret;
19226         };
19227         var cal_cell = function(n) {
19228             return  {
19229                 tag: 'td',
19230                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19231                 cn : [
19232                     {
19233                         cn : [
19234                             {
19235                                 cls: 'fc-day-number',
19236                                 html: 'D'
19237                             },
19238                             {
19239                                 cls: 'fc-day-content',
19240                              
19241                                 cn : [
19242                                      {
19243                                         style: 'position: relative;' // height: 17px;
19244                                     }
19245                                 ]
19246                             }
19247                             
19248                             
19249                         ]
19250                     }
19251                 ]
19252                 
19253             }
19254         };
19255         var cal_rows = function() {
19256             
19257             var ret = [];
19258             for (var r = 0; r < 6; r++) {
19259                 var row= {
19260                     tag : 'tr',
19261                     cls : 'fc-week',
19262                     cn : []
19263                 };
19264                 
19265                 for (var i =0; i < Date.dayNames.length; i++) {
19266                     var d = Date.dayNames[i];
19267                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19268
19269                 }
19270                 row.cn[0].cls+=' fc-first';
19271                 row.cn[0].cn[0].style = 'min-height:90px';
19272                 row.cn[6].cls+=' fc-last';
19273                 ret.push(row);
19274                 
19275             }
19276             ret[0].cls += ' fc-first';
19277             ret[4].cls += ' fc-prev-last';
19278             ret[5].cls += ' fc-last';
19279             return ret;
19280             
19281         };
19282         
19283         var cal_table = {
19284             tag: 'table',
19285             cls: 'fc-border-separate',
19286             style : 'width:100%',
19287             cellspacing  : 0,
19288             cn : [
19289                 { 
19290                     tag: 'thead',
19291                     cn : [
19292                         { 
19293                             tag: 'tr',
19294                             cls : 'fc-first fc-last',
19295                             cn : cal_heads()
19296                         }
19297                     ]
19298                 },
19299                 { 
19300                     tag: 'tbody',
19301                     cn : cal_rows()
19302                 }
19303                   
19304             ]
19305         };
19306          
19307          var cfg = {
19308             cls : 'fc fc-ltr',
19309             cn : [
19310                 header,
19311                 {
19312                     cls : 'fc-content',
19313                     style : "position: relative;",
19314                     cn : [
19315                         {
19316                             cls : 'fc-view fc-view-month fc-grid',
19317                             style : 'position: relative',
19318                             unselectable : 'on',
19319                             cn : [
19320                                 {
19321                                     cls : 'fc-event-container',
19322                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19323                                 },
19324                                 cal_table
19325                             ]
19326                         }
19327                     ]
19328     
19329                 }
19330            ] 
19331             
19332         };
19333         
19334          
19335         
19336         return cfg;
19337     },
19338     
19339     
19340     initEvents : function()
19341     {
19342         if(!this.store){
19343             throw "can not find store for calendar";
19344         }
19345         
19346         var mark = {
19347             tag: "div",
19348             cls:"x-dlg-mask",
19349             style: "text-align:center",
19350             cn: [
19351                 {
19352                     tag: "div",
19353                     style: "background-color:white;width:50%;margin:250 auto",
19354                     cn: [
19355                         {
19356                             tag: "img",
19357                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19358                         },
19359                         {
19360                             tag: "span",
19361                             html: "Loading"
19362                         }
19363                         
19364                     ]
19365                 }
19366             ]
19367         };
19368         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19369         
19370         var size = this.el.select('.fc-content', true).first().getSize();
19371         this.maskEl.setSize(size.width, size.height);
19372         this.maskEl.enableDisplayMode("block");
19373         if(!this.loadMask){
19374             this.maskEl.hide();
19375         }
19376         
19377         this.store = Roo.factory(this.store, Roo.data);
19378         this.store.on('load', this.onLoad, this);
19379         this.store.on('beforeload', this.onBeforeLoad, this);
19380         
19381         this.resize();
19382         
19383         this.cells = this.el.select('.fc-day',true);
19384         //Roo.log(this.cells);
19385         this.textNodes = this.el.query('.fc-day-number');
19386         this.cells.addClassOnOver('fc-state-hover');
19387         
19388         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19389         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19390         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19391         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19392         
19393         this.on('monthchange', this.onMonthChange, this);
19394         
19395         this.update(new Date().clearTime());
19396     },
19397     
19398     resize : function() {
19399         var sz  = this.el.getSize();
19400         
19401         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19402         this.el.select('.fc-day-content div',true).setHeight(34);
19403     },
19404     
19405     
19406     // private
19407     showPrevMonth : function(e){
19408         this.update(this.activeDate.add("mo", -1));
19409     },
19410     showToday : function(e){
19411         this.update(new Date().clearTime());
19412     },
19413     // private
19414     showNextMonth : function(e){
19415         this.update(this.activeDate.add("mo", 1));
19416     },
19417
19418     // private
19419     showPrevYear : function(){
19420         this.update(this.activeDate.add("y", -1));
19421     },
19422
19423     // private
19424     showNextYear : function(){
19425         this.update(this.activeDate.add("y", 1));
19426     },
19427
19428     
19429    // private
19430     update : function(date)
19431     {
19432         var vd = this.activeDate;
19433         this.activeDate = date;
19434 //        if(vd && this.el){
19435 //            var t = date.getTime();
19436 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19437 //                Roo.log('using add remove');
19438 //                
19439 //                this.fireEvent('monthchange', this, date);
19440 //                
19441 //                this.cells.removeClass("fc-state-highlight");
19442 //                this.cells.each(function(c){
19443 //                   if(c.dateValue == t){
19444 //                       c.addClass("fc-state-highlight");
19445 //                       setTimeout(function(){
19446 //                            try{c.dom.firstChild.focus();}catch(e){}
19447 //                       }, 50);
19448 //                       return false;
19449 //                   }
19450 //                   return true;
19451 //                });
19452 //                return;
19453 //            }
19454 //        }
19455         
19456         var days = date.getDaysInMonth();
19457         
19458         var firstOfMonth = date.getFirstDateOfMonth();
19459         var startingPos = firstOfMonth.getDay()-this.startDay;
19460         
19461         if(startingPos < this.startDay){
19462             startingPos += 7;
19463         }
19464         
19465         var pm = date.add(Date.MONTH, -1);
19466         var prevStart = pm.getDaysInMonth()-startingPos;
19467 //        
19468         this.cells = this.el.select('.fc-day',true);
19469         this.textNodes = this.el.query('.fc-day-number');
19470         this.cells.addClassOnOver('fc-state-hover');
19471         
19472         var cells = this.cells.elements;
19473         var textEls = this.textNodes;
19474         
19475         Roo.each(cells, function(cell){
19476             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19477         });
19478         
19479         days += startingPos;
19480
19481         // convert everything to numbers so it's fast
19482         var day = 86400000;
19483         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19484         //Roo.log(d);
19485         //Roo.log(pm);
19486         //Roo.log(prevStart);
19487         
19488         var today = new Date().clearTime().getTime();
19489         var sel = date.clearTime().getTime();
19490         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19491         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19492         var ddMatch = this.disabledDatesRE;
19493         var ddText = this.disabledDatesText;
19494         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19495         var ddaysText = this.disabledDaysText;
19496         var format = this.format;
19497         
19498         var setCellClass = function(cal, cell){
19499             cell.row = 0;
19500             cell.events = [];
19501             cell.more = [];
19502             //Roo.log('set Cell Class');
19503             cell.title = "";
19504             var t = d.getTime();
19505             
19506             //Roo.log(d);
19507             
19508             cell.dateValue = t;
19509             if(t == today){
19510                 cell.className += " fc-today";
19511                 cell.className += " fc-state-highlight";
19512                 cell.title = cal.todayText;
19513             }
19514             if(t == sel){
19515                 // disable highlight in other month..
19516                 //cell.className += " fc-state-highlight";
19517                 
19518             }
19519             // disabling
19520             if(t < min) {
19521                 cell.className = " fc-state-disabled";
19522                 cell.title = cal.minText;
19523                 return;
19524             }
19525             if(t > max) {
19526                 cell.className = " fc-state-disabled";
19527                 cell.title = cal.maxText;
19528                 return;
19529             }
19530             if(ddays){
19531                 if(ddays.indexOf(d.getDay()) != -1){
19532                     cell.title = ddaysText;
19533                     cell.className = " fc-state-disabled";
19534                 }
19535             }
19536             if(ddMatch && format){
19537                 var fvalue = d.dateFormat(format);
19538                 if(ddMatch.test(fvalue)){
19539                     cell.title = ddText.replace("%0", fvalue);
19540                     cell.className = " fc-state-disabled";
19541                 }
19542             }
19543             
19544             if (!cell.initialClassName) {
19545                 cell.initialClassName = cell.dom.className;
19546             }
19547             
19548             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19549         };
19550
19551         var i = 0;
19552         
19553         for(; i < startingPos; i++) {
19554             textEls[i].innerHTML = (++prevStart);
19555             d.setDate(d.getDate()+1);
19556             
19557             cells[i].className = "fc-past fc-other-month";
19558             setCellClass(this, cells[i]);
19559         }
19560         
19561         var intDay = 0;
19562         
19563         for(; i < days; i++){
19564             intDay = i - startingPos + 1;
19565             textEls[i].innerHTML = (intDay);
19566             d.setDate(d.getDate()+1);
19567             
19568             cells[i].className = ''; // "x-date-active";
19569             setCellClass(this, cells[i]);
19570         }
19571         var extraDays = 0;
19572         
19573         for(; i < 42; i++) {
19574             textEls[i].innerHTML = (++extraDays);
19575             d.setDate(d.getDate()+1);
19576             
19577             cells[i].className = "fc-future fc-other-month";
19578             setCellClass(this, cells[i]);
19579         }
19580         
19581         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19582         
19583         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19584         
19585         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19586         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19587         
19588         if(totalRows != 6){
19589             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19590             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19591         }
19592         
19593         this.fireEvent('monthchange', this, date);
19594         
19595         
19596         /*
19597         if(!this.internalRender){
19598             var main = this.el.dom.firstChild;
19599             var w = main.offsetWidth;
19600             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19601             Roo.fly(main).setWidth(w);
19602             this.internalRender = true;
19603             // opera does not respect the auto grow header center column
19604             // then, after it gets a width opera refuses to recalculate
19605             // without a second pass
19606             if(Roo.isOpera && !this.secondPass){
19607                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19608                 this.secondPass = true;
19609                 this.update.defer(10, this, [date]);
19610             }
19611         }
19612         */
19613         
19614     },
19615     
19616     findCell : function(dt) {
19617         dt = dt.clearTime().getTime();
19618         var ret = false;
19619         this.cells.each(function(c){
19620             //Roo.log("check " +c.dateValue + '?=' + dt);
19621             if(c.dateValue == dt){
19622                 ret = c;
19623                 return false;
19624             }
19625             return true;
19626         });
19627         
19628         return ret;
19629     },
19630     
19631     findCells : function(ev) {
19632         var s = ev.start.clone().clearTime().getTime();
19633        // Roo.log(s);
19634         var e= ev.end.clone().clearTime().getTime();
19635        // Roo.log(e);
19636         var ret = [];
19637         this.cells.each(function(c){
19638              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19639             
19640             if(c.dateValue > e){
19641                 return ;
19642             }
19643             if(c.dateValue < s){
19644                 return ;
19645             }
19646             ret.push(c);
19647         });
19648         
19649         return ret;    
19650     },
19651     
19652 //    findBestRow: function(cells)
19653 //    {
19654 //        var ret = 0;
19655 //        
19656 //        for (var i =0 ; i < cells.length;i++) {
19657 //            ret  = Math.max(cells[i].rows || 0,ret);
19658 //        }
19659 //        return ret;
19660 //        
19661 //    },
19662     
19663     
19664     addItem : function(ev)
19665     {
19666         // look for vertical location slot in
19667         var cells = this.findCells(ev);
19668         
19669 //        ev.row = this.findBestRow(cells);
19670         
19671         // work out the location.
19672         
19673         var crow = false;
19674         var rows = [];
19675         for(var i =0; i < cells.length; i++) {
19676             
19677             cells[i].row = cells[0].row;
19678             
19679             if(i == 0){
19680                 cells[i].row = cells[i].row + 1;
19681             }
19682             
19683             if (!crow) {
19684                 crow = {
19685                     start : cells[i],
19686                     end :  cells[i]
19687                 };
19688                 continue;
19689             }
19690             if (crow.start.getY() == cells[i].getY()) {
19691                 // on same row.
19692                 crow.end = cells[i];
19693                 continue;
19694             }
19695             // different row.
19696             rows.push(crow);
19697             crow = {
19698                 start: cells[i],
19699                 end : cells[i]
19700             };
19701             
19702         }
19703         
19704         rows.push(crow);
19705         ev.els = [];
19706         ev.rows = rows;
19707         ev.cells = cells;
19708         
19709         cells[0].events.push(ev);
19710         
19711         this.calevents.push(ev);
19712     },
19713     
19714     clearEvents: function() {
19715         
19716         if(!this.calevents){
19717             return;
19718         }
19719         
19720         Roo.each(this.cells.elements, function(c){
19721             c.row = 0;
19722             c.events = [];
19723             c.more = [];
19724         });
19725         
19726         Roo.each(this.calevents, function(e) {
19727             Roo.each(e.els, function(el) {
19728                 el.un('mouseenter' ,this.onEventEnter, this);
19729                 el.un('mouseleave' ,this.onEventLeave, this);
19730                 el.remove();
19731             },this);
19732         },this);
19733         
19734         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19735             e.remove();
19736         });
19737         
19738     },
19739     
19740     renderEvents: function()
19741     {   
19742         var _this = this;
19743         
19744         this.cells.each(function(c) {
19745             
19746             if(c.row < 5){
19747                 return;
19748             }
19749             
19750             var ev = c.events;
19751             
19752             var r = 4;
19753             if(c.row != c.events.length){
19754                 r = 4 - (4 - (c.row - c.events.length));
19755             }
19756             
19757             c.events = ev.slice(0, r);
19758             c.more = ev.slice(r);
19759             
19760             if(c.more.length && c.more.length == 1){
19761                 c.events.push(c.more.pop());
19762             }
19763             
19764             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19765             
19766         });
19767             
19768         this.cells.each(function(c) {
19769             
19770             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19771             
19772             
19773             for (var e = 0; e < c.events.length; e++){
19774                 var ev = c.events[e];
19775                 var rows = ev.rows;
19776                 
19777                 for(var i = 0; i < rows.length; i++) {
19778                 
19779                     // how many rows should it span..
19780
19781                     var  cfg = {
19782                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19783                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19784
19785                         unselectable : "on",
19786                         cn : [
19787                             {
19788                                 cls: 'fc-event-inner',
19789                                 cn : [
19790     //                                {
19791     //                                  tag:'span',
19792     //                                  cls: 'fc-event-time',
19793     //                                  html : cells.length > 1 ? '' : ev.time
19794     //                                },
19795                                     {
19796                                       tag:'span',
19797                                       cls: 'fc-event-title',
19798                                       html : String.format('{0}', ev.title)
19799                                     }
19800
19801
19802                                 ]
19803                             },
19804                             {
19805                                 cls: 'ui-resizable-handle ui-resizable-e',
19806                                 html : '&nbsp;&nbsp;&nbsp'
19807                             }
19808
19809                         ]
19810                     };
19811
19812                     if (i == 0) {
19813                         cfg.cls += ' fc-event-start';
19814                     }
19815                     if ((i+1) == rows.length) {
19816                         cfg.cls += ' fc-event-end';
19817                     }
19818
19819                     var ctr = _this.el.select('.fc-event-container',true).first();
19820                     var cg = ctr.createChild(cfg);
19821
19822                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19823                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19824
19825                     var r = (c.more.length) ? 1 : 0;
19826                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19827                     cg.setWidth(ebox.right - sbox.x -2);
19828
19829                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19830                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19831                     cg.on('click', _this.onEventClick, _this, ev);
19832
19833                     ev.els.push(cg);
19834                     
19835                 }
19836                 
19837             }
19838             
19839             
19840             if(c.more.length){
19841                 var  cfg = {
19842                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19843                     style : 'position: absolute',
19844                     unselectable : "on",
19845                     cn : [
19846                         {
19847                             cls: 'fc-event-inner',
19848                             cn : [
19849                                 {
19850                                   tag:'span',
19851                                   cls: 'fc-event-title',
19852                                   html : 'More'
19853                                 }
19854
19855
19856                             ]
19857                         },
19858                         {
19859                             cls: 'ui-resizable-handle ui-resizable-e',
19860                             html : '&nbsp;&nbsp;&nbsp'
19861                         }
19862
19863                     ]
19864                 };
19865
19866                 var ctr = _this.el.select('.fc-event-container',true).first();
19867                 var cg = ctr.createChild(cfg);
19868
19869                 var sbox = c.select('.fc-day-content',true).first().getBox();
19870                 var ebox = c.select('.fc-day-content',true).first().getBox();
19871                 //Roo.log(cg);
19872                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19873                 cg.setWidth(ebox.right - sbox.x -2);
19874
19875                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19876                 
19877             }
19878             
19879         });
19880         
19881         
19882         
19883     },
19884     
19885     onEventEnter: function (e, el,event,d) {
19886         this.fireEvent('evententer', this, el, event);
19887     },
19888     
19889     onEventLeave: function (e, el,event,d) {
19890         this.fireEvent('eventleave', this, el, event);
19891     },
19892     
19893     onEventClick: function (e, el,event,d) {
19894         this.fireEvent('eventclick', this, el, event);
19895     },
19896     
19897     onMonthChange: function () {
19898         this.store.load();
19899     },
19900     
19901     onMoreEventClick: function(e, el, more)
19902     {
19903         var _this = this;
19904         
19905         this.calpopover.placement = 'right';
19906         this.calpopover.setTitle('More');
19907         
19908         this.calpopover.setContent('');
19909         
19910         var ctr = this.calpopover.el.select('.popover-content', true).first();
19911         
19912         Roo.each(more, function(m){
19913             var cfg = {
19914                 cls : 'fc-event-hori fc-event-draggable',
19915                 html : m.title
19916             };
19917             var cg = ctr.createChild(cfg);
19918             
19919             cg.on('click', _this.onEventClick, _this, m);
19920         });
19921         
19922         this.calpopover.show(el);
19923         
19924         
19925     },
19926     
19927     onLoad: function () 
19928     {   
19929         this.calevents = [];
19930         var cal = this;
19931         
19932         if(this.store.getCount() > 0){
19933             this.store.data.each(function(d){
19934                cal.addItem({
19935                     id : d.data.id,
19936                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19937                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19938                     time : d.data.start_time,
19939                     title : d.data.title,
19940                     description : d.data.description,
19941                     venue : d.data.venue
19942                 });
19943             });
19944         }
19945         
19946         this.renderEvents();
19947         
19948         if(this.calevents.length && this.loadMask){
19949             this.maskEl.hide();
19950         }
19951     },
19952     
19953     onBeforeLoad: function()
19954     {
19955         this.clearEvents();
19956         if(this.loadMask){
19957             this.maskEl.show();
19958         }
19959     }
19960 });
19961
19962  
19963  /*
19964  * - LGPL
19965  *
19966  * element
19967  * 
19968  */
19969
19970 /**
19971  * @class Roo.bootstrap.Popover
19972  * @extends Roo.bootstrap.Component
19973  * Bootstrap Popover class
19974  * @cfg {String} html contents of the popover   (or false to use children..)
19975  * @cfg {String} title of popover (or false to hide)
19976  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19977  * @cfg {String} trigger click || hover (or false to trigger manually)
19978  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19979  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19980  *      - if false and it has a 'parent' then it will be automatically added to that element
19981  *      - if string - Roo.get  will be called 
19982  * @cfg {Number} delay - delay before showing
19983  
19984  * @constructor
19985  * Create a new Popover
19986  * @param {Object} config The config object
19987  */
19988
19989 Roo.bootstrap.Popover = function(config){
19990     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19991     
19992     this.addEvents({
19993         // raw events
19994          /**
19995          * @event show
19996          * After the popover show
19997          * 
19998          * @param {Roo.bootstrap.Popover} this
19999          */
20000         "show" : true,
20001         /**
20002          * @event hide
20003          * After the popover hide
20004          * 
20005          * @param {Roo.bootstrap.Popover} this
20006          */
20007         "hide" : true
20008     });
20009 };
20010
20011 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20012     
20013     title: false,
20014     html: false,
20015     
20016     placement : 'right',
20017     trigger : 'hover', // hover
20018     modal : false,
20019     delay : 0,
20020     
20021     over: false,
20022     
20023     can_build_overlaid : false,
20024     
20025     maskEl : false, // the mask element
20026     headerEl : false,
20027     contentEl : false,
20028     alignEl : false, // when show is called with an element - this get's stored.
20029     
20030     getChildContainer : function()
20031     {
20032         return this.contentEl;
20033         
20034     },
20035     getPopoverHeader : function()
20036     {
20037         this.title = true; // flag not to hide it..
20038         this.headerEl.addClass('p-0');
20039         return this.headerEl
20040     },
20041     
20042     
20043     getAutoCreate : function(){
20044          
20045         var cfg = {
20046            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20047            style: 'display:block',
20048            cn : [
20049                 {
20050                     cls : 'arrow'
20051                 },
20052                 {
20053                     cls : 'popover-inner ',
20054                     cn : [
20055                         {
20056                             tag: 'h3',
20057                             cls: 'popover-title popover-header',
20058                             html : this.title === false ? '' : this.title
20059                         },
20060                         {
20061                             cls : 'popover-content popover-body '  + (this.cls || ''),
20062                             html : this.html || ''
20063                         }
20064                     ]
20065                     
20066                 }
20067            ]
20068         };
20069         
20070         return cfg;
20071     },
20072     /**
20073      * @param {string} the title
20074      */
20075     setTitle: function(str)
20076     {
20077         this.title = str;
20078         if (this.el) {
20079             this.headerEl.dom.innerHTML = str;
20080         }
20081         
20082     },
20083     /**
20084      * @param {string} the body content
20085      */
20086     setContent: function(str)
20087     {
20088         this.html = str;
20089         if (this.contentEl) {
20090             this.contentEl.dom.innerHTML = str;
20091         }
20092         
20093     },
20094     // as it get's added to the bottom of the page.
20095     onRender : function(ct, position)
20096     {
20097         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20098         
20099         
20100         
20101         if(!this.el){
20102             var cfg = Roo.apply({},  this.getAutoCreate());
20103             cfg.id = Roo.id();
20104             
20105             if (this.cls) {
20106                 cfg.cls += ' ' + this.cls;
20107             }
20108             if (this.style) {
20109                 cfg.style = this.style;
20110             }
20111             //Roo.log("adding to ");
20112             this.el = Roo.get(document.body).createChild(cfg, position);
20113 //            Roo.log(this.el);
20114         }
20115         
20116         this.contentEl = this.el.select('.popover-content',true).first();
20117         this.headerEl =  this.el.select('.popover-title',true).first();
20118         
20119         var nitems = [];
20120         if(typeof(this.items) != 'undefined'){
20121             var items = this.items;
20122             delete this.items;
20123
20124             for(var i =0;i < items.length;i++) {
20125                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20126             }
20127         }
20128
20129         this.items = nitems;
20130         
20131         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20132         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20133         
20134         
20135         
20136         this.initEvents();
20137     },
20138     
20139     resizeMask : function()
20140     {
20141         this.maskEl.setSize(
20142             Roo.lib.Dom.getViewWidth(true),
20143             Roo.lib.Dom.getViewHeight(true)
20144         );
20145     },
20146     
20147     initEvents : function()
20148     {
20149         
20150         if (!this.modal) { 
20151             Roo.bootstrap.Popover.register(this);
20152         }
20153          
20154         this.arrowEl = this.el.select('.arrow',true).first();
20155         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20156         this.el.enableDisplayMode('block');
20157         this.el.hide();
20158  
20159         
20160         if (this.over === false && !this.parent()) {
20161             return; 
20162         }
20163         if (this.triggers === false) {
20164             return;
20165         }
20166          
20167         // support parent
20168         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20169         var triggers = this.trigger ? this.trigger.split(' ') : [];
20170         Roo.each(triggers, function(trigger) {
20171         
20172             if (trigger == 'click') {
20173                 on_el.on('click', this.toggle, this);
20174             } else if (trigger != 'manual') {
20175                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20176                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20177       
20178                 on_el.on(eventIn  ,this.enter, this);
20179                 on_el.on(eventOut, this.leave, this);
20180             }
20181         }, this);
20182     },
20183     
20184     
20185     // private
20186     timeout : null,
20187     hoverState : null,
20188     
20189     toggle : function () {
20190         this.hoverState == 'in' ? this.leave() : this.enter();
20191     },
20192     
20193     enter : function () {
20194         
20195         clearTimeout(this.timeout);
20196     
20197         this.hoverState = 'in';
20198     
20199         if (!this.delay || !this.delay.show) {
20200             this.show();
20201             return;
20202         }
20203         var _t = this;
20204         this.timeout = setTimeout(function () {
20205             if (_t.hoverState == 'in') {
20206                 _t.show();
20207             }
20208         }, this.delay.show)
20209     },
20210     
20211     leave : function() {
20212         clearTimeout(this.timeout);
20213     
20214         this.hoverState = 'out';
20215     
20216         if (!this.delay || !this.delay.hide) {
20217             this.hide();
20218             return;
20219         }
20220         var _t = this;
20221         this.timeout = setTimeout(function () {
20222             if (_t.hoverState == 'out') {
20223                 _t.hide();
20224             }
20225         }, this.delay.hide)
20226     },
20227     /**
20228      * Show the popover
20229      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20230      * @param {string} (left|right|top|bottom) position
20231      */
20232     show : function (on_el, placement)
20233     {
20234         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20235         on_el = on_el || false; // default to false
20236          
20237         if (!on_el) {
20238             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20239                 on_el = this.parent().el;
20240             } else if (this.over) {
20241                 on_el = Roo.get(this.over);
20242             }
20243             
20244         }
20245         
20246         this.alignEl = Roo.get( on_el );
20247
20248         if (!this.el) {
20249             this.render(document.body);
20250         }
20251         
20252         
20253          
20254         
20255         if (this.title === false) {
20256             this.headerEl.hide();
20257         }
20258         
20259        
20260         this.el.show();
20261         this.el.dom.style.display = 'block';
20262          
20263  
20264         if (this.alignEl) {
20265             this.updatePosition(this.placement, true);
20266              
20267         } else {
20268             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20269             var es = this.el.getSize();
20270             var x = Roo.lib.Dom.getViewWidth()/2;
20271             var y = Roo.lib.Dom.getViewHeight()/2;
20272             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20273             
20274         }
20275
20276         
20277         //var arrow = this.el.select('.arrow',true).first();
20278         //arrow.set(align[2], 
20279         
20280         this.el.addClass('in');
20281         
20282          
20283         
20284         this.hoverState = 'in';
20285         
20286         if (this.modal) {
20287             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20288             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20289             this.maskEl.dom.style.display = 'block';
20290             this.maskEl.addClass('show');
20291         }
20292         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20293  
20294         this.fireEvent('show', this);
20295         
20296     },
20297     /**
20298      * fire this manually after loading a grid in the table for example
20299      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20300      * @param {Boolean} try and move it if we cant get right position.
20301      */
20302     updatePosition : function(placement, try_move)
20303     {
20304         // allow for calling with no parameters
20305         placement = placement   ? placement :  this.placement;
20306         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20307         
20308         this.el.removeClass([
20309             'fade','top','bottom', 'left', 'right','in',
20310             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20311         ]);
20312         this.el.addClass(placement + ' bs-popover-' + placement);
20313         
20314         if (!this.alignEl ) {
20315             return false;
20316         }
20317         
20318         switch (placement) {
20319             case 'right':
20320                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20321                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20322                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20323                     //normal display... or moved up/down.
20324                     this.el.setXY(offset);
20325                     var xy = this.alignEl.getAnchorXY('tr', false);
20326                     xy[0]+=2;xy[1]+=5;
20327                     this.arrowEl.setXY(xy);
20328                     return true;
20329                 }
20330                 // continue through...
20331                 return this.updatePosition('left', false);
20332                 
20333             
20334             case 'left':
20335                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20336                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20337                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20338                     //normal display... or moved up/down.
20339                     this.el.setXY(offset);
20340                     var xy = this.alignEl.getAnchorXY('tl', false);
20341                     xy[0]-=10;xy[1]+=5; // << fix me
20342                     this.arrowEl.setXY(xy);
20343                     return true;
20344                 }
20345                 // call self...
20346                 return this.updatePosition('right', false);
20347             
20348             case 'top':
20349                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20350                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20351                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20352                     //normal display... or moved up/down.
20353                     this.el.setXY(offset);
20354                     var xy = this.alignEl.getAnchorXY('t', false);
20355                     xy[1]-=10; // << fix me
20356                     this.arrowEl.setXY(xy);
20357                     return true;
20358                 }
20359                 // fall through
20360                return this.updatePosition('bottom', false);
20361             
20362             case 'bottom':
20363                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20364                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20365                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20366                     //normal display... or moved up/down.
20367                     this.el.setXY(offset);
20368                     var xy = this.alignEl.getAnchorXY('b', false);
20369                      xy[1]+=2; // << fix me
20370                     this.arrowEl.setXY(xy);
20371                     return true;
20372                 }
20373                 // fall through
20374                 return this.updatePosition('top', false);
20375                 
20376             
20377         }
20378         
20379         
20380         return false;
20381     },
20382     
20383     hide : function()
20384     {
20385         this.el.setXY([0,0]);
20386         this.el.removeClass('in');
20387         this.el.hide();
20388         this.hoverState = null;
20389         this.maskEl.hide(); // always..
20390         this.fireEvent('hide', this);
20391     }
20392     
20393 });
20394
20395
20396 Roo.apply(Roo.bootstrap.Popover, {
20397
20398     alignment : {
20399         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20400         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20401         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20402         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20403     },
20404     
20405     zIndex : 20001,
20406
20407     clickHander : false,
20408     
20409
20410     onMouseDown : function(e)
20411     {
20412         if (!e.getTarget(".roo-popover")) {
20413             this.hideAll();
20414         }
20415          
20416     },
20417     
20418     popups : [],
20419     
20420     register : function(popup)
20421     {
20422         if (!Roo.bootstrap.Popover.clickHandler) {
20423             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20424         }
20425         // hide other popups.
20426         this.hideAll();
20427         this.popups.push(popup);
20428     },
20429     hideAll : function()
20430     {
20431         this.popups.forEach(function(p) {
20432             p.hide();
20433         });
20434     }
20435
20436 });/*
20437  * - LGPL
20438  *
20439  * Card header - holder for the card header elements.
20440  * 
20441  */
20442
20443 /**
20444  * @class Roo.bootstrap.PopoverNav
20445  * @extends Roo.bootstrap.NavGroup
20446  * Bootstrap Popover header navigation class
20447  * @constructor
20448  * Create a new Popover Header Navigation 
20449  * @param {Object} config The config object
20450  */
20451
20452 Roo.bootstrap.PopoverNav = function(config){
20453     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20454 };
20455
20456 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20457     
20458     
20459     container_method : 'getPopoverHeader' 
20460     
20461      
20462     
20463     
20464    
20465 });
20466
20467  
20468
20469  /*
20470  * - LGPL
20471  *
20472  * Progress
20473  * 
20474  */
20475
20476 /**
20477  * @class Roo.bootstrap.Progress
20478  * @extends Roo.bootstrap.Component
20479  * Bootstrap Progress class
20480  * @cfg {Boolean} striped striped of the progress bar
20481  * @cfg {Boolean} active animated of the progress bar
20482  * 
20483  * 
20484  * @constructor
20485  * Create a new Progress
20486  * @param {Object} config The config object
20487  */
20488
20489 Roo.bootstrap.Progress = function(config){
20490     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20491 };
20492
20493 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20494     
20495     striped : false,
20496     active: false,
20497     
20498     getAutoCreate : function(){
20499         var cfg = {
20500             tag: 'div',
20501             cls: 'progress'
20502         };
20503         
20504         
20505         if(this.striped){
20506             cfg.cls += ' progress-striped';
20507         }
20508       
20509         if(this.active){
20510             cfg.cls += ' active';
20511         }
20512         
20513         
20514         return cfg;
20515     }
20516    
20517 });
20518
20519  
20520
20521  /*
20522  * - LGPL
20523  *
20524  * ProgressBar
20525  * 
20526  */
20527
20528 /**
20529  * @class Roo.bootstrap.ProgressBar
20530  * @extends Roo.bootstrap.Component
20531  * Bootstrap ProgressBar class
20532  * @cfg {Number} aria_valuenow aria-value now
20533  * @cfg {Number} aria_valuemin aria-value min
20534  * @cfg {Number} aria_valuemax aria-value max
20535  * @cfg {String} label label for the progress bar
20536  * @cfg {String} panel (success | info | warning | danger )
20537  * @cfg {String} role role of the progress bar
20538  * @cfg {String} sr_only text
20539  * 
20540  * 
20541  * @constructor
20542  * Create a new ProgressBar
20543  * @param {Object} config The config object
20544  */
20545
20546 Roo.bootstrap.ProgressBar = function(config){
20547     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20548 };
20549
20550 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20551     
20552     aria_valuenow : 0,
20553     aria_valuemin : 0,
20554     aria_valuemax : 100,
20555     label : false,
20556     panel : false,
20557     role : false,
20558     sr_only: false,
20559     
20560     getAutoCreate : function()
20561     {
20562         
20563         var cfg = {
20564             tag: 'div',
20565             cls: 'progress-bar',
20566             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20567         };
20568         
20569         if(this.sr_only){
20570             cfg.cn = {
20571                 tag: 'span',
20572                 cls: 'sr-only',
20573                 html: this.sr_only
20574             }
20575         }
20576         
20577         if(this.role){
20578             cfg.role = this.role;
20579         }
20580         
20581         if(this.aria_valuenow){
20582             cfg['aria-valuenow'] = this.aria_valuenow;
20583         }
20584         
20585         if(this.aria_valuemin){
20586             cfg['aria-valuemin'] = this.aria_valuemin;
20587         }
20588         
20589         if(this.aria_valuemax){
20590             cfg['aria-valuemax'] = this.aria_valuemax;
20591         }
20592         
20593         if(this.label && !this.sr_only){
20594             cfg.html = this.label;
20595         }
20596         
20597         if(this.panel){
20598             cfg.cls += ' progress-bar-' + this.panel;
20599         }
20600         
20601         return cfg;
20602     },
20603     
20604     update : function(aria_valuenow)
20605     {
20606         this.aria_valuenow = aria_valuenow;
20607         
20608         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20609     }
20610    
20611 });
20612
20613  
20614
20615  /*
20616  * - LGPL
20617  *
20618  * column
20619  * 
20620  */
20621
20622 /**
20623  * @class Roo.bootstrap.TabGroup
20624  * @extends Roo.bootstrap.Column
20625  * Bootstrap Column class
20626  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20627  * @cfg {Boolean} carousel true to make the group behave like a carousel
20628  * @cfg {Boolean} bullets show bullets for the panels
20629  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20630  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20631  * @cfg {Boolean} showarrow (true|false) show arrow default true
20632  * 
20633  * @constructor
20634  * Create a new TabGroup
20635  * @param {Object} config The config object
20636  */
20637
20638 Roo.bootstrap.TabGroup = function(config){
20639     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20640     if (!this.navId) {
20641         this.navId = Roo.id();
20642     }
20643     this.tabs = [];
20644     Roo.bootstrap.TabGroup.register(this);
20645     
20646 };
20647
20648 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20649     
20650     carousel : false,
20651     transition : false,
20652     bullets : 0,
20653     timer : 0,
20654     autoslide : false,
20655     slideFn : false,
20656     slideOnTouch : false,
20657     showarrow : true,
20658     
20659     getAutoCreate : function()
20660     {
20661         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20662         
20663         cfg.cls += ' tab-content';
20664         
20665         if (this.carousel) {
20666             cfg.cls += ' carousel slide';
20667             
20668             cfg.cn = [{
20669                cls : 'carousel-inner',
20670                cn : []
20671             }];
20672         
20673             if(this.bullets  && !Roo.isTouch){
20674                 
20675                 var bullets = {
20676                     cls : 'carousel-bullets',
20677                     cn : []
20678                 };
20679                
20680                 if(this.bullets_cls){
20681                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20682                 }
20683                 
20684                 bullets.cn.push({
20685                     cls : 'clear'
20686                 });
20687                 
20688                 cfg.cn[0].cn.push(bullets);
20689             }
20690             
20691             if(this.showarrow){
20692                 cfg.cn[0].cn.push({
20693                     tag : 'div',
20694                     class : 'carousel-arrow',
20695                     cn : [
20696                         {
20697                             tag : 'div',
20698                             class : 'carousel-prev',
20699                             cn : [
20700                                 {
20701                                     tag : 'i',
20702                                     class : 'fa fa-chevron-left'
20703                                 }
20704                             ]
20705                         },
20706                         {
20707                             tag : 'div',
20708                             class : 'carousel-next',
20709                             cn : [
20710                                 {
20711                                     tag : 'i',
20712                                     class : 'fa fa-chevron-right'
20713                                 }
20714                             ]
20715                         }
20716                     ]
20717                 });
20718             }
20719             
20720         }
20721         
20722         return cfg;
20723     },
20724     
20725     initEvents:  function()
20726     {
20727 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20728 //            this.el.on("touchstart", this.onTouchStart, this);
20729 //        }
20730         
20731         if(this.autoslide){
20732             var _this = this;
20733             
20734             this.slideFn = window.setInterval(function() {
20735                 _this.showPanelNext();
20736             }, this.timer);
20737         }
20738         
20739         if(this.showarrow){
20740             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20741             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20742         }
20743         
20744         
20745     },
20746     
20747 //    onTouchStart : function(e, el, o)
20748 //    {
20749 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20750 //            return;
20751 //        }
20752 //        
20753 //        this.showPanelNext();
20754 //    },
20755     
20756     
20757     getChildContainer : function()
20758     {
20759         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20760     },
20761     
20762     /**
20763     * register a Navigation item
20764     * @param {Roo.bootstrap.NavItem} the navitem to add
20765     */
20766     register : function(item)
20767     {
20768         this.tabs.push( item);
20769         item.navId = this.navId; // not really needed..
20770         this.addBullet();
20771     
20772     },
20773     
20774     getActivePanel : function()
20775     {
20776         var r = false;
20777         Roo.each(this.tabs, function(t) {
20778             if (t.active) {
20779                 r = t;
20780                 return false;
20781             }
20782             return null;
20783         });
20784         return r;
20785         
20786     },
20787     getPanelByName : function(n)
20788     {
20789         var r = false;
20790         Roo.each(this.tabs, function(t) {
20791             if (t.tabId == n) {
20792                 r = t;
20793                 return false;
20794             }
20795             return null;
20796         });
20797         return r;
20798     },
20799     indexOfPanel : function(p)
20800     {
20801         var r = false;
20802         Roo.each(this.tabs, function(t,i) {
20803             if (t.tabId == p.tabId) {
20804                 r = i;
20805                 return false;
20806             }
20807             return null;
20808         });
20809         return r;
20810     },
20811     /**
20812      * show a specific panel
20813      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20814      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20815      */
20816     showPanel : function (pan)
20817     {
20818         if(this.transition || typeof(pan) == 'undefined'){
20819             Roo.log("waiting for the transitionend");
20820             return false;
20821         }
20822         
20823         if (typeof(pan) == 'number') {
20824             pan = this.tabs[pan];
20825         }
20826         
20827         if (typeof(pan) == 'string') {
20828             pan = this.getPanelByName(pan);
20829         }
20830         
20831         var cur = this.getActivePanel();
20832         
20833         if(!pan || !cur){
20834             Roo.log('pan or acitve pan is undefined');
20835             return false;
20836         }
20837         
20838         if (pan.tabId == this.getActivePanel().tabId) {
20839             return true;
20840         }
20841         
20842         if (false === cur.fireEvent('beforedeactivate')) {
20843             return false;
20844         }
20845         
20846         if(this.bullets > 0 && !Roo.isTouch){
20847             this.setActiveBullet(this.indexOfPanel(pan));
20848         }
20849         
20850         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20851             
20852             //class="carousel-item carousel-item-next carousel-item-left"
20853             
20854             this.transition = true;
20855             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20856             var lr = dir == 'next' ? 'left' : 'right';
20857             pan.el.addClass(dir); // or prev
20858             pan.el.addClass('carousel-item-' + dir); // or prev
20859             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20860             cur.el.addClass(lr); // or right
20861             pan.el.addClass(lr);
20862             cur.el.addClass('carousel-item-' +lr); // or right
20863             pan.el.addClass('carousel-item-' +lr);
20864             
20865             
20866             var _this = this;
20867             cur.el.on('transitionend', function() {
20868                 Roo.log("trans end?");
20869                 
20870                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20871                 pan.setActive(true);
20872                 
20873                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20874                 cur.setActive(false);
20875                 
20876                 _this.transition = false;
20877                 
20878             }, this, { single:  true } );
20879             
20880             return true;
20881         }
20882         
20883         cur.setActive(false);
20884         pan.setActive(true);
20885         
20886         return true;
20887         
20888     },
20889     showPanelNext : function()
20890     {
20891         var i = this.indexOfPanel(this.getActivePanel());
20892         
20893         if (i >= this.tabs.length - 1 && !this.autoslide) {
20894             return;
20895         }
20896         
20897         if (i >= this.tabs.length - 1 && this.autoslide) {
20898             i = -1;
20899         }
20900         
20901         this.showPanel(this.tabs[i+1]);
20902     },
20903     
20904     showPanelPrev : function()
20905     {
20906         var i = this.indexOfPanel(this.getActivePanel());
20907         
20908         if (i  < 1 && !this.autoslide) {
20909             return;
20910         }
20911         
20912         if (i < 1 && this.autoslide) {
20913             i = this.tabs.length;
20914         }
20915         
20916         this.showPanel(this.tabs[i-1]);
20917     },
20918     
20919     
20920     addBullet: function()
20921     {
20922         if(!this.bullets || Roo.isTouch){
20923             return;
20924         }
20925         var ctr = this.el.select('.carousel-bullets',true).first();
20926         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20927         var bullet = ctr.createChild({
20928             cls : 'bullet bullet-' + i
20929         },ctr.dom.lastChild);
20930         
20931         
20932         var _this = this;
20933         
20934         bullet.on('click', (function(e, el, o, ii, t){
20935
20936             e.preventDefault();
20937
20938             this.showPanel(ii);
20939
20940             if(this.autoslide && this.slideFn){
20941                 clearInterval(this.slideFn);
20942                 this.slideFn = window.setInterval(function() {
20943                     _this.showPanelNext();
20944                 }, this.timer);
20945             }
20946
20947         }).createDelegate(this, [i, bullet], true));
20948                 
20949         
20950     },
20951      
20952     setActiveBullet : function(i)
20953     {
20954         if(Roo.isTouch){
20955             return;
20956         }
20957         
20958         Roo.each(this.el.select('.bullet', true).elements, function(el){
20959             el.removeClass('selected');
20960         });
20961
20962         var bullet = this.el.select('.bullet-' + i, true).first();
20963         
20964         if(!bullet){
20965             return;
20966         }
20967         
20968         bullet.addClass('selected');
20969     }
20970     
20971     
20972   
20973 });
20974
20975  
20976
20977  
20978  
20979 Roo.apply(Roo.bootstrap.TabGroup, {
20980     
20981     groups: {},
20982      /**
20983     * register a Navigation Group
20984     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20985     */
20986     register : function(navgrp)
20987     {
20988         this.groups[navgrp.navId] = navgrp;
20989         
20990     },
20991     /**
20992     * fetch a Navigation Group based on the navigation ID
20993     * if one does not exist , it will get created.
20994     * @param {string} the navgroup to add
20995     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20996     */
20997     get: function(navId) {
20998         if (typeof(this.groups[navId]) == 'undefined') {
20999             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21000         }
21001         return this.groups[navId] ;
21002     }
21003     
21004     
21005     
21006 });
21007
21008  /*
21009  * - LGPL
21010  *
21011  * TabPanel
21012  * 
21013  */
21014
21015 /**
21016  * @class Roo.bootstrap.TabPanel
21017  * @extends Roo.bootstrap.Component
21018  * Bootstrap TabPanel class
21019  * @cfg {Boolean} active panel active
21020  * @cfg {String} html panel content
21021  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21022  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21023  * @cfg {String} href click to link..
21024  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21025  * 
21026  * 
21027  * @constructor
21028  * Create a new TabPanel
21029  * @param {Object} config The config object
21030  */
21031
21032 Roo.bootstrap.TabPanel = function(config){
21033     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21034     this.addEvents({
21035         /**
21036              * @event changed
21037              * Fires when the active status changes
21038              * @param {Roo.bootstrap.TabPanel} this
21039              * @param {Boolean} state the new state
21040             
21041          */
21042         'changed': true,
21043         /**
21044              * @event beforedeactivate
21045              * Fires before a tab is de-activated - can be used to do validation on a form.
21046              * @param {Roo.bootstrap.TabPanel} this
21047              * @return {Boolean} false if there is an error
21048             
21049          */
21050         'beforedeactivate': true
21051      });
21052     
21053     this.tabId = this.tabId || Roo.id();
21054   
21055 };
21056
21057 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21058     
21059     active: false,
21060     html: false,
21061     tabId: false,
21062     navId : false,
21063     href : '',
21064     touchSlide : false,
21065     getAutoCreate : function(){
21066         
21067         
21068         var cfg = {
21069             tag: 'div',
21070             // item is needed for carousel - not sure if it has any effect otherwise
21071             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21072             html: this.html || ''
21073         };
21074         
21075         if(this.active){
21076             cfg.cls += ' active';
21077         }
21078         
21079         if(this.tabId){
21080             cfg.tabId = this.tabId;
21081         }
21082         
21083         
21084         
21085         return cfg;
21086     },
21087     
21088     initEvents:  function()
21089     {
21090         var p = this.parent();
21091         
21092         this.navId = this.navId || p.navId;
21093         
21094         if (typeof(this.navId) != 'undefined') {
21095             // not really needed.. but just in case.. parent should be a NavGroup.
21096             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21097             
21098             tg.register(this);
21099             
21100             var i = tg.tabs.length - 1;
21101             
21102             if(this.active && tg.bullets > 0 && i < tg.bullets){
21103                 tg.setActiveBullet(i);
21104             }
21105         }
21106         
21107         this.el.on('click', this.onClick, this);
21108         
21109         if(Roo.isTouch && this.touchSlide){
21110             this.el.on("touchstart", this.onTouchStart, this);
21111             this.el.on("touchmove", this.onTouchMove, this);
21112             this.el.on("touchend", this.onTouchEnd, this);
21113         }
21114         
21115     },
21116     
21117     onRender : function(ct, position)
21118     {
21119         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21120     },
21121     
21122     setActive : function(state)
21123     {
21124         Roo.log("panel - set active " + this.tabId + "=" + state);
21125         
21126         this.active = state;
21127         if (!state) {
21128             this.el.removeClass('active');
21129             
21130         } else  if (!this.el.hasClass('active')) {
21131             this.el.addClass('active');
21132         }
21133         
21134         this.fireEvent('changed', this, state);
21135     },
21136     
21137     onClick : function(e)
21138     {
21139         e.preventDefault();
21140         
21141         if(!this.href.length){
21142             return;
21143         }
21144         
21145         window.location.href = this.href;
21146     },
21147     
21148     startX : 0,
21149     startY : 0,
21150     endX : 0,
21151     endY : 0,
21152     swiping : false,
21153     
21154     onTouchStart : function(e)
21155     {
21156         this.swiping = false;
21157         
21158         this.startX = e.browserEvent.touches[0].clientX;
21159         this.startY = e.browserEvent.touches[0].clientY;
21160     },
21161     
21162     onTouchMove : function(e)
21163     {
21164         this.swiping = true;
21165         
21166         this.endX = e.browserEvent.touches[0].clientX;
21167         this.endY = e.browserEvent.touches[0].clientY;
21168     },
21169     
21170     onTouchEnd : function(e)
21171     {
21172         if(!this.swiping){
21173             this.onClick(e);
21174             return;
21175         }
21176         
21177         var tabGroup = this.parent();
21178         
21179         if(this.endX > this.startX){ // swiping right
21180             tabGroup.showPanelPrev();
21181             return;
21182         }
21183         
21184         if(this.startX > this.endX){ // swiping left
21185             tabGroup.showPanelNext();
21186             return;
21187         }
21188     }
21189     
21190     
21191 });
21192  
21193
21194  
21195
21196  /*
21197  * - LGPL
21198  *
21199  * DateField
21200  * 
21201  */
21202
21203 /**
21204  * @class Roo.bootstrap.DateField
21205  * @extends Roo.bootstrap.Input
21206  * Bootstrap DateField class
21207  * @cfg {Number} weekStart default 0
21208  * @cfg {String} viewMode default empty, (months|years)
21209  * @cfg {String} minViewMode default empty, (months|years)
21210  * @cfg {Number} startDate default -Infinity
21211  * @cfg {Number} endDate default Infinity
21212  * @cfg {Boolean} todayHighlight default false
21213  * @cfg {Boolean} todayBtn default false
21214  * @cfg {Boolean} calendarWeeks default false
21215  * @cfg {Object} daysOfWeekDisabled default empty
21216  * @cfg {Boolean} singleMode default false (true | false)
21217  * 
21218  * @cfg {Boolean} keyboardNavigation default true
21219  * @cfg {String} language default en
21220  * 
21221  * @constructor
21222  * Create a new DateField
21223  * @param {Object} config The config object
21224  */
21225
21226 Roo.bootstrap.DateField = function(config){
21227     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21228      this.addEvents({
21229             /**
21230              * @event show
21231              * Fires when this field show.
21232              * @param {Roo.bootstrap.DateField} this
21233              * @param {Mixed} date The date value
21234              */
21235             show : true,
21236             /**
21237              * @event show
21238              * Fires when this field hide.
21239              * @param {Roo.bootstrap.DateField} this
21240              * @param {Mixed} date The date value
21241              */
21242             hide : true,
21243             /**
21244              * @event select
21245              * Fires when select a date.
21246              * @param {Roo.bootstrap.DateField} this
21247              * @param {Mixed} date The date value
21248              */
21249             select : true,
21250             /**
21251              * @event beforeselect
21252              * Fires when before select a date.
21253              * @param {Roo.bootstrap.DateField} this
21254              * @param {Mixed} date The date value
21255              */
21256             beforeselect : true
21257         });
21258 };
21259
21260 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21261     
21262     /**
21263      * @cfg {String} format
21264      * The default date format string which can be overriden for localization support.  The format must be
21265      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21266      */
21267     format : "m/d/y",
21268     /**
21269      * @cfg {String} altFormats
21270      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21271      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21272      */
21273     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21274     
21275     weekStart : 0,
21276     
21277     viewMode : '',
21278     
21279     minViewMode : '',
21280     
21281     todayHighlight : false,
21282     
21283     todayBtn: false,
21284     
21285     language: 'en',
21286     
21287     keyboardNavigation: true,
21288     
21289     calendarWeeks: false,
21290     
21291     startDate: -Infinity,
21292     
21293     endDate: Infinity,
21294     
21295     daysOfWeekDisabled: [],
21296     
21297     _events: [],
21298     
21299     singleMode : false,
21300     
21301     UTCDate: function()
21302     {
21303         return new Date(Date.UTC.apply(Date, arguments));
21304     },
21305     
21306     UTCToday: function()
21307     {
21308         var today = new Date();
21309         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21310     },
21311     
21312     getDate: function() {
21313             var d = this.getUTCDate();
21314             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21315     },
21316     
21317     getUTCDate: function() {
21318             return this.date;
21319     },
21320     
21321     setDate: function(d) {
21322             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21323     },
21324     
21325     setUTCDate: function(d) {
21326             this.date = d;
21327             this.setValue(this.formatDate(this.date));
21328     },
21329         
21330     onRender: function(ct, position)
21331     {
21332         
21333         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21334         
21335         this.language = this.language || 'en';
21336         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21337         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21338         
21339         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21340         this.format = this.format || 'm/d/y';
21341         this.isInline = false;
21342         this.isInput = true;
21343         this.component = this.el.select('.add-on', true).first() || false;
21344         this.component = (this.component && this.component.length === 0) ? false : this.component;
21345         this.hasInput = this.component && this.inputEl().length;
21346         
21347         if (typeof(this.minViewMode === 'string')) {
21348             switch (this.minViewMode) {
21349                 case 'months':
21350                     this.minViewMode = 1;
21351                     break;
21352                 case 'years':
21353                     this.minViewMode = 2;
21354                     break;
21355                 default:
21356                     this.minViewMode = 0;
21357                     break;
21358             }
21359         }
21360         
21361         if (typeof(this.viewMode === 'string')) {
21362             switch (this.viewMode) {
21363                 case 'months':
21364                     this.viewMode = 1;
21365                     break;
21366                 case 'years':
21367                     this.viewMode = 2;
21368                     break;
21369                 default:
21370                     this.viewMode = 0;
21371                     break;
21372             }
21373         }
21374                 
21375         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21376         
21377 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21378         
21379         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21380         
21381         this.picker().on('mousedown', this.onMousedown, this);
21382         this.picker().on('click', this.onClick, this);
21383         
21384         this.picker().addClass('datepicker-dropdown');
21385         
21386         this.startViewMode = this.viewMode;
21387         
21388         if(this.singleMode){
21389             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21390                 v.setVisibilityMode(Roo.Element.DISPLAY);
21391                 v.hide();
21392             });
21393             
21394             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21395                 v.setStyle('width', '189px');
21396             });
21397         }
21398         
21399         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21400             if(!this.calendarWeeks){
21401                 v.remove();
21402                 return;
21403             }
21404             
21405             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21406             v.attr('colspan', function(i, val){
21407                 return parseInt(val) + 1;
21408             });
21409         });
21410                         
21411         
21412         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21413         
21414         this.setStartDate(this.startDate);
21415         this.setEndDate(this.endDate);
21416         
21417         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21418         
21419         this.fillDow();
21420         this.fillMonths();
21421         this.update();
21422         this.showMode();
21423         
21424         if(this.isInline) {
21425             this.showPopup();
21426         }
21427     },
21428     
21429     picker : function()
21430     {
21431         return this.pickerEl;
21432 //        return this.el.select('.datepicker', true).first();
21433     },
21434     
21435     fillDow: function()
21436     {
21437         var dowCnt = this.weekStart;
21438         
21439         var dow = {
21440             tag: 'tr',
21441             cn: [
21442                 
21443             ]
21444         };
21445         
21446         if(this.calendarWeeks){
21447             dow.cn.push({
21448                 tag: 'th',
21449                 cls: 'cw',
21450                 html: '&nbsp;'
21451             })
21452         }
21453         
21454         while (dowCnt < this.weekStart + 7) {
21455             dow.cn.push({
21456                 tag: 'th',
21457                 cls: 'dow',
21458                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21459             });
21460         }
21461         
21462         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21463     },
21464     
21465     fillMonths: function()
21466     {    
21467         var i = 0;
21468         var months = this.picker().select('>.datepicker-months td', true).first();
21469         
21470         months.dom.innerHTML = '';
21471         
21472         while (i < 12) {
21473             var month = {
21474                 tag: 'span',
21475                 cls: 'month',
21476                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21477             };
21478             
21479             months.createChild(month);
21480         }
21481         
21482     },
21483     
21484     update: function()
21485     {
21486         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;
21487         
21488         if (this.date < this.startDate) {
21489             this.viewDate = new Date(this.startDate);
21490         } else if (this.date > this.endDate) {
21491             this.viewDate = new Date(this.endDate);
21492         } else {
21493             this.viewDate = new Date(this.date);
21494         }
21495         
21496         this.fill();
21497     },
21498     
21499     fill: function() 
21500     {
21501         var d = new Date(this.viewDate),
21502                 year = d.getUTCFullYear(),
21503                 month = d.getUTCMonth(),
21504                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21505                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21506                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21507                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21508                 currentDate = this.date && this.date.valueOf(),
21509                 today = this.UTCToday();
21510         
21511         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21512         
21513 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21514         
21515 //        this.picker.select('>tfoot th.today').
21516 //                                              .text(dates[this.language].today)
21517 //                                              .toggle(this.todayBtn !== false);
21518     
21519         this.updateNavArrows();
21520         this.fillMonths();
21521                                                 
21522         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21523         
21524         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21525          
21526         prevMonth.setUTCDate(day);
21527         
21528         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21529         
21530         var nextMonth = new Date(prevMonth);
21531         
21532         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21533         
21534         nextMonth = nextMonth.valueOf();
21535         
21536         var fillMonths = false;
21537         
21538         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21539         
21540         while(prevMonth.valueOf() <= nextMonth) {
21541             var clsName = '';
21542             
21543             if (prevMonth.getUTCDay() === this.weekStart) {
21544                 if(fillMonths){
21545                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21546                 }
21547                     
21548                 fillMonths = {
21549                     tag: 'tr',
21550                     cn: []
21551                 };
21552                 
21553                 if(this.calendarWeeks){
21554                     // ISO 8601: First week contains first thursday.
21555                     // ISO also states week starts on Monday, but we can be more abstract here.
21556                     var
21557                     // Start of current week: based on weekstart/current date
21558                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21559                     // Thursday of this week
21560                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21561                     // First Thursday of year, year from thursday
21562                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21563                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21564                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21565                     
21566                     fillMonths.cn.push({
21567                         tag: 'td',
21568                         cls: 'cw',
21569                         html: calWeek
21570                     });
21571                 }
21572             }
21573             
21574             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21575                 clsName += ' old';
21576             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21577                 clsName += ' new';
21578             }
21579             if (this.todayHighlight &&
21580                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21581                 prevMonth.getUTCMonth() == today.getMonth() &&
21582                 prevMonth.getUTCDate() == today.getDate()) {
21583                 clsName += ' today';
21584             }
21585             
21586             if (currentDate && prevMonth.valueOf() === currentDate) {
21587                 clsName += ' active';
21588             }
21589             
21590             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21591                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21592                     clsName += ' disabled';
21593             }
21594             
21595             fillMonths.cn.push({
21596                 tag: 'td',
21597                 cls: 'day ' + clsName,
21598                 html: prevMonth.getDate()
21599             });
21600             
21601             prevMonth.setDate(prevMonth.getDate()+1);
21602         }
21603           
21604         var currentYear = this.date && this.date.getUTCFullYear();
21605         var currentMonth = this.date && this.date.getUTCMonth();
21606         
21607         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21608         
21609         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21610             v.removeClass('active');
21611             
21612             if(currentYear === year && k === currentMonth){
21613                 v.addClass('active');
21614             }
21615             
21616             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21617                 v.addClass('disabled');
21618             }
21619             
21620         });
21621         
21622         
21623         year = parseInt(year/10, 10) * 10;
21624         
21625         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21626         
21627         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21628         
21629         year -= 1;
21630         for (var i = -1; i < 11; i++) {
21631             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21632                 tag: 'span',
21633                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21634                 html: year
21635             });
21636             
21637             year += 1;
21638         }
21639     },
21640     
21641     showMode: function(dir) 
21642     {
21643         if (dir) {
21644             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21645         }
21646         
21647         Roo.each(this.picker().select('>div',true).elements, function(v){
21648             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21649             v.hide();
21650         });
21651         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21652     },
21653     
21654     place: function()
21655     {
21656         if(this.isInline) {
21657             return;
21658         }
21659         
21660         this.picker().removeClass(['bottom', 'top']);
21661         
21662         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21663             /*
21664              * place to the top of element!
21665              *
21666              */
21667             
21668             this.picker().addClass('top');
21669             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21670             
21671             return;
21672         }
21673         
21674         this.picker().addClass('bottom');
21675         
21676         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21677     },
21678     
21679     parseDate : function(value)
21680     {
21681         if(!value || value instanceof Date){
21682             return value;
21683         }
21684         var v = Date.parseDate(value, this.format);
21685         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21686             v = Date.parseDate(value, 'Y-m-d');
21687         }
21688         if(!v && this.altFormats){
21689             if(!this.altFormatsArray){
21690                 this.altFormatsArray = this.altFormats.split("|");
21691             }
21692             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21693                 v = Date.parseDate(value, this.altFormatsArray[i]);
21694             }
21695         }
21696         return v;
21697     },
21698     
21699     formatDate : function(date, fmt)
21700     {   
21701         return (!date || !(date instanceof Date)) ?
21702         date : date.dateFormat(fmt || this.format);
21703     },
21704     
21705     onFocus : function()
21706     {
21707         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21708         this.showPopup();
21709     },
21710     
21711     onBlur : function()
21712     {
21713         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21714         
21715         var d = this.inputEl().getValue();
21716         
21717         this.setValue(d);
21718                 
21719         this.hidePopup();
21720     },
21721     
21722     showPopup : function()
21723     {
21724         this.picker().show();
21725         this.update();
21726         this.place();
21727         
21728         this.fireEvent('showpopup', this, this.date);
21729     },
21730     
21731     hidePopup : function()
21732     {
21733         if(this.isInline) {
21734             return;
21735         }
21736         this.picker().hide();
21737         this.viewMode = this.startViewMode;
21738         this.showMode();
21739         
21740         this.fireEvent('hidepopup', this, this.date);
21741         
21742     },
21743     
21744     onMousedown: function(e)
21745     {
21746         e.stopPropagation();
21747         e.preventDefault();
21748     },
21749     
21750     keyup: function(e)
21751     {
21752         Roo.bootstrap.DateField.superclass.keyup.call(this);
21753         this.update();
21754     },
21755
21756     setValue: function(v)
21757     {
21758         if(this.fireEvent('beforeselect', this, v) !== false){
21759             var d = new Date(this.parseDate(v) ).clearTime();
21760         
21761             if(isNaN(d.getTime())){
21762                 this.date = this.viewDate = '';
21763                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21764                 return;
21765             }
21766
21767             v = this.formatDate(d);
21768
21769             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21770
21771             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21772
21773             this.update();
21774
21775             this.fireEvent('select', this, this.date);
21776         }
21777     },
21778     
21779     getValue: function()
21780     {
21781         return this.formatDate(this.date);
21782     },
21783     
21784     fireKey: function(e)
21785     {
21786         if (!this.picker().isVisible()){
21787             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21788                 this.showPopup();
21789             }
21790             return;
21791         }
21792         
21793         var dateChanged = false,
21794         dir, day, month,
21795         newDate, newViewDate;
21796         
21797         switch(e.keyCode){
21798             case 27: // escape
21799                 this.hidePopup();
21800                 e.preventDefault();
21801                 break;
21802             case 37: // left
21803             case 39: // right
21804                 if (!this.keyboardNavigation) {
21805                     break;
21806                 }
21807                 dir = e.keyCode == 37 ? -1 : 1;
21808                 
21809                 if (e.ctrlKey){
21810                     newDate = this.moveYear(this.date, dir);
21811                     newViewDate = this.moveYear(this.viewDate, dir);
21812                 } else if (e.shiftKey){
21813                     newDate = this.moveMonth(this.date, dir);
21814                     newViewDate = this.moveMonth(this.viewDate, dir);
21815                 } else {
21816                     newDate = new Date(this.date);
21817                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21818                     newViewDate = new Date(this.viewDate);
21819                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21820                 }
21821                 if (this.dateWithinRange(newDate)){
21822                     this.date = newDate;
21823                     this.viewDate = newViewDate;
21824                     this.setValue(this.formatDate(this.date));
21825 //                    this.update();
21826                     e.preventDefault();
21827                     dateChanged = true;
21828                 }
21829                 break;
21830             case 38: // up
21831             case 40: // down
21832                 if (!this.keyboardNavigation) {
21833                     break;
21834                 }
21835                 dir = e.keyCode == 38 ? -1 : 1;
21836                 if (e.ctrlKey){
21837                     newDate = this.moveYear(this.date, dir);
21838                     newViewDate = this.moveYear(this.viewDate, dir);
21839                 } else if (e.shiftKey){
21840                     newDate = this.moveMonth(this.date, dir);
21841                     newViewDate = this.moveMonth(this.viewDate, dir);
21842                 } else {
21843                     newDate = new Date(this.date);
21844                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21845                     newViewDate = new Date(this.viewDate);
21846                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21847                 }
21848                 if (this.dateWithinRange(newDate)){
21849                     this.date = newDate;
21850                     this.viewDate = newViewDate;
21851                     this.setValue(this.formatDate(this.date));
21852 //                    this.update();
21853                     e.preventDefault();
21854                     dateChanged = true;
21855                 }
21856                 break;
21857             case 13: // enter
21858                 this.setValue(this.formatDate(this.date));
21859                 this.hidePopup();
21860                 e.preventDefault();
21861                 break;
21862             case 9: // tab
21863                 this.setValue(this.formatDate(this.date));
21864                 this.hidePopup();
21865                 break;
21866             case 16: // shift
21867             case 17: // ctrl
21868             case 18: // alt
21869                 break;
21870             default :
21871                 this.hidePopup();
21872                 
21873         }
21874     },
21875     
21876     
21877     onClick: function(e) 
21878     {
21879         e.stopPropagation();
21880         e.preventDefault();
21881         
21882         var target = e.getTarget();
21883         
21884         if(target.nodeName.toLowerCase() === 'i'){
21885             target = Roo.get(target).dom.parentNode;
21886         }
21887         
21888         var nodeName = target.nodeName;
21889         var className = target.className;
21890         var html = target.innerHTML;
21891         //Roo.log(nodeName);
21892         
21893         switch(nodeName.toLowerCase()) {
21894             case 'th':
21895                 switch(className) {
21896                     case 'switch':
21897                         this.showMode(1);
21898                         break;
21899                     case 'prev':
21900                     case 'next':
21901                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21902                         switch(this.viewMode){
21903                                 case 0:
21904                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21905                                         break;
21906                                 case 1:
21907                                 case 2:
21908                                         this.viewDate = this.moveYear(this.viewDate, dir);
21909                                         break;
21910                         }
21911                         this.fill();
21912                         break;
21913                     case 'today':
21914                         var date = new Date();
21915                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21916 //                        this.fill()
21917                         this.setValue(this.formatDate(this.date));
21918                         
21919                         this.hidePopup();
21920                         break;
21921                 }
21922                 break;
21923             case 'span':
21924                 if (className.indexOf('disabled') < 0) {
21925                     this.viewDate.setUTCDate(1);
21926                     if (className.indexOf('month') > -1) {
21927                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21928                     } else {
21929                         var year = parseInt(html, 10) || 0;
21930                         this.viewDate.setUTCFullYear(year);
21931                         
21932                     }
21933                     
21934                     if(this.singleMode){
21935                         this.setValue(this.formatDate(this.viewDate));
21936                         this.hidePopup();
21937                         return;
21938                     }
21939                     
21940                     this.showMode(-1);
21941                     this.fill();
21942                 }
21943                 break;
21944                 
21945             case 'td':
21946                 //Roo.log(className);
21947                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21948                     var day = parseInt(html, 10) || 1;
21949                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21950                         month = (this.viewDate || new Date()).getUTCMonth();
21951
21952                     if (className.indexOf('old') > -1) {
21953                         if(month === 0 ){
21954                             month = 11;
21955                             year -= 1;
21956                         }else{
21957                             month -= 1;
21958                         }
21959                     } else if (className.indexOf('new') > -1) {
21960                         if (month == 11) {
21961                             month = 0;
21962                             year += 1;
21963                         } else {
21964                             month += 1;
21965                         }
21966                     }
21967                     //Roo.log([year,month,day]);
21968                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21969                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21970 //                    this.fill();
21971                     //Roo.log(this.formatDate(this.date));
21972                     this.setValue(this.formatDate(this.date));
21973                     this.hidePopup();
21974                 }
21975                 break;
21976         }
21977     },
21978     
21979     setStartDate: function(startDate)
21980     {
21981         this.startDate = startDate || -Infinity;
21982         if (this.startDate !== -Infinity) {
21983             this.startDate = this.parseDate(this.startDate);
21984         }
21985         this.update();
21986         this.updateNavArrows();
21987     },
21988
21989     setEndDate: function(endDate)
21990     {
21991         this.endDate = endDate || Infinity;
21992         if (this.endDate !== Infinity) {
21993             this.endDate = this.parseDate(this.endDate);
21994         }
21995         this.update();
21996         this.updateNavArrows();
21997     },
21998     
21999     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22000     {
22001         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22002         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22003             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22004         }
22005         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22006             return parseInt(d, 10);
22007         });
22008         this.update();
22009         this.updateNavArrows();
22010     },
22011     
22012     updateNavArrows: function() 
22013     {
22014         if(this.singleMode){
22015             return;
22016         }
22017         
22018         var d = new Date(this.viewDate),
22019         year = d.getUTCFullYear(),
22020         month = d.getUTCMonth();
22021         
22022         Roo.each(this.picker().select('.prev', true).elements, function(v){
22023             v.show();
22024             switch (this.viewMode) {
22025                 case 0:
22026
22027                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22028                         v.hide();
22029                     }
22030                     break;
22031                 case 1:
22032                 case 2:
22033                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22034                         v.hide();
22035                     }
22036                     break;
22037             }
22038         });
22039         
22040         Roo.each(this.picker().select('.next', true).elements, function(v){
22041             v.show();
22042             switch (this.viewMode) {
22043                 case 0:
22044
22045                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22046                         v.hide();
22047                     }
22048                     break;
22049                 case 1:
22050                 case 2:
22051                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22052                         v.hide();
22053                     }
22054                     break;
22055             }
22056         })
22057     },
22058     
22059     moveMonth: function(date, dir)
22060     {
22061         if (!dir) {
22062             return date;
22063         }
22064         var new_date = new Date(date.valueOf()),
22065         day = new_date.getUTCDate(),
22066         month = new_date.getUTCMonth(),
22067         mag = Math.abs(dir),
22068         new_month, test;
22069         dir = dir > 0 ? 1 : -1;
22070         if (mag == 1){
22071             test = dir == -1
22072             // If going back one month, make sure month is not current month
22073             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22074             ? function(){
22075                 return new_date.getUTCMonth() == month;
22076             }
22077             // If going forward one month, make sure month is as expected
22078             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22079             : function(){
22080                 return new_date.getUTCMonth() != new_month;
22081             };
22082             new_month = month + dir;
22083             new_date.setUTCMonth(new_month);
22084             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22085             if (new_month < 0 || new_month > 11) {
22086                 new_month = (new_month + 12) % 12;
22087             }
22088         } else {
22089             // For magnitudes >1, move one month at a time...
22090             for (var i=0; i<mag; i++) {
22091                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22092                 new_date = this.moveMonth(new_date, dir);
22093             }
22094             // ...then reset the day, keeping it in the new month
22095             new_month = new_date.getUTCMonth();
22096             new_date.setUTCDate(day);
22097             test = function(){
22098                 return new_month != new_date.getUTCMonth();
22099             };
22100         }
22101         // Common date-resetting loop -- if date is beyond end of month, make it
22102         // end of month
22103         while (test()){
22104             new_date.setUTCDate(--day);
22105             new_date.setUTCMonth(new_month);
22106         }
22107         return new_date;
22108     },
22109
22110     moveYear: function(date, dir)
22111     {
22112         return this.moveMonth(date, dir*12);
22113     },
22114
22115     dateWithinRange: function(date)
22116     {
22117         return date >= this.startDate && date <= this.endDate;
22118     },
22119
22120     
22121     remove: function() 
22122     {
22123         this.picker().remove();
22124     },
22125     
22126     validateValue : function(value)
22127     {
22128         if(this.getVisibilityEl().hasClass('hidden')){
22129             return true;
22130         }
22131         
22132         if(value.length < 1)  {
22133             if(this.allowBlank){
22134                 return true;
22135             }
22136             return false;
22137         }
22138         
22139         if(value.length < this.minLength){
22140             return false;
22141         }
22142         if(value.length > this.maxLength){
22143             return false;
22144         }
22145         if(this.vtype){
22146             var vt = Roo.form.VTypes;
22147             if(!vt[this.vtype](value, this)){
22148                 return false;
22149             }
22150         }
22151         if(typeof this.validator == "function"){
22152             var msg = this.validator(value);
22153             if(msg !== true){
22154                 return false;
22155             }
22156         }
22157         
22158         if(this.regex && !this.regex.test(value)){
22159             return false;
22160         }
22161         
22162         if(typeof(this.parseDate(value)) == 'undefined'){
22163             return false;
22164         }
22165         
22166         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22167             return false;
22168         }      
22169         
22170         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22171             return false;
22172         } 
22173         
22174         
22175         return true;
22176     },
22177     
22178     reset : function()
22179     {
22180         this.date = this.viewDate = '';
22181         
22182         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22183     }
22184    
22185 });
22186
22187 Roo.apply(Roo.bootstrap.DateField,  {
22188     
22189     head : {
22190         tag: 'thead',
22191         cn: [
22192         {
22193             tag: 'tr',
22194             cn: [
22195             {
22196                 tag: 'th',
22197                 cls: 'prev',
22198                 html: '<i class="fa fa-arrow-left"/>'
22199             },
22200             {
22201                 tag: 'th',
22202                 cls: 'switch',
22203                 colspan: '5'
22204             },
22205             {
22206                 tag: 'th',
22207                 cls: 'next',
22208                 html: '<i class="fa fa-arrow-right"/>'
22209             }
22210
22211             ]
22212         }
22213         ]
22214     },
22215     
22216     content : {
22217         tag: 'tbody',
22218         cn: [
22219         {
22220             tag: 'tr',
22221             cn: [
22222             {
22223                 tag: 'td',
22224                 colspan: '7'
22225             }
22226             ]
22227         }
22228         ]
22229     },
22230     
22231     footer : {
22232         tag: 'tfoot',
22233         cn: [
22234         {
22235             tag: 'tr',
22236             cn: [
22237             {
22238                 tag: 'th',
22239                 colspan: '7',
22240                 cls: 'today'
22241             }
22242                     
22243             ]
22244         }
22245         ]
22246     },
22247     
22248     dates:{
22249         en: {
22250             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22251             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22252             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22253             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22254             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22255             today: "Today"
22256         }
22257     },
22258     
22259     modes: [
22260     {
22261         clsName: 'days',
22262         navFnc: 'Month',
22263         navStep: 1
22264     },
22265     {
22266         clsName: 'months',
22267         navFnc: 'FullYear',
22268         navStep: 1
22269     },
22270     {
22271         clsName: 'years',
22272         navFnc: 'FullYear',
22273         navStep: 10
22274     }]
22275 });
22276
22277 Roo.apply(Roo.bootstrap.DateField,  {
22278   
22279     template : {
22280         tag: 'div',
22281         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22282         cn: [
22283         {
22284             tag: 'div',
22285             cls: 'datepicker-days',
22286             cn: [
22287             {
22288                 tag: 'table',
22289                 cls: 'table-condensed',
22290                 cn:[
22291                 Roo.bootstrap.DateField.head,
22292                 {
22293                     tag: 'tbody'
22294                 },
22295                 Roo.bootstrap.DateField.footer
22296                 ]
22297             }
22298             ]
22299         },
22300         {
22301             tag: 'div',
22302             cls: 'datepicker-months',
22303             cn: [
22304             {
22305                 tag: 'table',
22306                 cls: 'table-condensed',
22307                 cn:[
22308                 Roo.bootstrap.DateField.head,
22309                 Roo.bootstrap.DateField.content,
22310                 Roo.bootstrap.DateField.footer
22311                 ]
22312             }
22313             ]
22314         },
22315         {
22316             tag: 'div',
22317             cls: 'datepicker-years',
22318             cn: [
22319             {
22320                 tag: 'table',
22321                 cls: 'table-condensed',
22322                 cn:[
22323                 Roo.bootstrap.DateField.head,
22324                 Roo.bootstrap.DateField.content,
22325                 Roo.bootstrap.DateField.footer
22326                 ]
22327             }
22328             ]
22329         }
22330         ]
22331     }
22332 });
22333
22334  
22335
22336  /*
22337  * - LGPL
22338  *
22339  * TimeField
22340  * 
22341  */
22342
22343 /**
22344  * @class Roo.bootstrap.TimeField
22345  * @extends Roo.bootstrap.Input
22346  * Bootstrap DateField class
22347  * 
22348  * 
22349  * @constructor
22350  * Create a new TimeField
22351  * @param {Object} config The config object
22352  */
22353
22354 Roo.bootstrap.TimeField = function(config){
22355     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22356     this.addEvents({
22357             /**
22358              * @event show
22359              * Fires when this field show.
22360              * @param {Roo.bootstrap.DateField} thisthis
22361              * @param {Mixed} date The date value
22362              */
22363             show : true,
22364             /**
22365              * @event show
22366              * Fires when this field hide.
22367              * @param {Roo.bootstrap.DateField} this
22368              * @param {Mixed} date The date value
22369              */
22370             hide : true,
22371             /**
22372              * @event select
22373              * Fires when select a date.
22374              * @param {Roo.bootstrap.DateField} this
22375              * @param {Mixed} date The date value
22376              */
22377             select : true
22378         });
22379 };
22380
22381 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22382     
22383     /**
22384      * @cfg {String} format
22385      * The default time format string which can be overriden for localization support.  The format must be
22386      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22387      */
22388     format : "H:i",
22389
22390     getAutoCreate : function()
22391     {
22392         this.after = '<i class="fa far fa-clock"></i>';
22393         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22394         
22395          
22396     },
22397     onRender: function(ct, position)
22398     {
22399         
22400         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22401                 
22402         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22403         
22404         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22405         
22406         this.pop = this.picker().select('>.datepicker-time',true).first();
22407         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22408         
22409         this.picker().on('mousedown', this.onMousedown, this);
22410         this.picker().on('click', this.onClick, this);
22411         
22412         this.picker().addClass('datepicker-dropdown');
22413     
22414         this.fillTime();
22415         this.update();
22416             
22417         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22418         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22419         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22420         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22421         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22422         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22423
22424     },
22425     
22426     fireKey: function(e){
22427         if (!this.picker().isVisible()){
22428             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22429                 this.show();
22430             }
22431             return;
22432         }
22433
22434         e.preventDefault();
22435         
22436         switch(e.keyCode){
22437             case 27: // escape
22438                 this.hide();
22439                 break;
22440             case 37: // left
22441             case 39: // right
22442                 this.onTogglePeriod();
22443                 break;
22444             case 38: // up
22445                 this.onIncrementMinutes();
22446                 break;
22447             case 40: // down
22448                 this.onDecrementMinutes();
22449                 break;
22450             case 13: // enter
22451             case 9: // tab
22452                 this.setTime();
22453                 break;
22454         }
22455     },
22456     
22457     onClick: function(e) {
22458         e.stopPropagation();
22459         e.preventDefault();
22460     },
22461     
22462     picker : function()
22463     {
22464         return this.pickerEl;
22465     },
22466     
22467     fillTime: function()
22468     {    
22469         var time = this.pop.select('tbody', true).first();
22470         
22471         time.dom.innerHTML = '';
22472         
22473         time.createChild({
22474             tag: 'tr',
22475             cn: [
22476                 {
22477                     tag: 'td',
22478                     cn: [
22479                         {
22480                             tag: 'a',
22481                             href: '#',
22482                             cls: 'btn',
22483                             cn: [
22484                                 {
22485                                     tag: 'i',
22486                                     cls: 'hours-up fa fas fa-chevron-up'
22487                                 }
22488                             ]
22489                         } 
22490                     ]
22491                 },
22492                 {
22493                     tag: 'td',
22494                     cls: 'separator'
22495                 },
22496                 {
22497                     tag: 'td',
22498                     cn: [
22499                         {
22500                             tag: 'a',
22501                             href: '#',
22502                             cls: 'btn',
22503                             cn: [
22504                                 {
22505                                     tag: 'i',
22506                                     cls: 'minutes-up fa fas fa-chevron-up'
22507                                 }
22508                             ]
22509                         }
22510                     ]
22511                 },
22512                 {
22513                     tag: 'td',
22514                     cls: 'separator'
22515                 }
22516             ]
22517         });
22518         
22519         time.createChild({
22520             tag: 'tr',
22521             cn: [
22522                 {
22523                     tag: 'td',
22524                     cn: [
22525                         {
22526                             tag: 'span',
22527                             cls: 'timepicker-hour',
22528                             html: '00'
22529                         }  
22530                     ]
22531                 },
22532                 {
22533                     tag: 'td',
22534                     cls: 'separator',
22535                     html: ':'
22536                 },
22537                 {
22538                     tag: 'td',
22539                     cn: [
22540                         {
22541                             tag: 'span',
22542                             cls: 'timepicker-minute',
22543                             html: '00'
22544                         }  
22545                     ]
22546                 },
22547                 {
22548                     tag: 'td',
22549                     cls: 'separator'
22550                 },
22551                 {
22552                     tag: 'td',
22553                     cn: [
22554                         {
22555                             tag: 'button',
22556                             type: 'button',
22557                             cls: 'btn btn-primary period',
22558                             html: 'AM'
22559                             
22560                         }
22561                     ]
22562                 }
22563             ]
22564         });
22565         
22566         time.createChild({
22567             tag: 'tr',
22568             cn: [
22569                 {
22570                     tag: 'td',
22571                     cn: [
22572                         {
22573                             tag: 'a',
22574                             href: '#',
22575                             cls: 'btn',
22576                             cn: [
22577                                 {
22578                                     tag: 'span',
22579                                     cls: 'hours-down fa fas fa-chevron-down'
22580                                 }
22581                             ]
22582                         }
22583                     ]
22584                 },
22585                 {
22586                     tag: 'td',
22587                     cls: 'separator'
22588                 },
22589                 {
22590                     tag: 'td',
22591                     cn: [
22592                         {
22593                             tag: 'a',
22594                             href: '#',
22595                             cls: 'btn',
22596                             cn: [
22597                                 {
22598                                     tag: 'span',
22599                                     cls: 'minutes-down fa fas fa-chevron-down'
22600                                 }
22601                             ]
22602                         }
22603                     ]
22604                 },
22605                 {
22606                     tag: 'td',
22607                     cls: 'separator'
22608                 }
22609             ]
22610         });
22611         
22612     },
22613     
22614     update: function()
22615     {
22616         
22617         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22618         
22619         this.fill();
22620     },
22621     
22622     fill: function() 
22623     {
22624         var hours = this.time.getHours();
22625         var minutes = this.time.getMinutes();
22626         var period = 'AM';
22627         
22628         if(hours > 11){
22629             period = 'PM';
22630         }
22631         
22632         if(hours == 0){
22633             hours = 12;
22634         }
22635         
22636         
22637         if(hours > 12){
22638             hours = hours - 12;
22639         }
22640         
22641         if(hours < 10){
22642             hours = '0' + hours;
22643         }
22644         
22645         if(minutes < 10){
22646             minutes = '0' + minutes;
22647         }
22648         
22649         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22650         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22651         this.pop.select('button', true).first().dom.innerHTML = period;
22652         
22653     },
22654     
22655     place: function()
22656     {   
22657         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22658         
22659         var cls = ['bottom'];
22660         
22661         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22662             cls.pop();
22663             cls.push('top');
22664         }
22665         
22666         cls.push('right');
22667         
22668         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22669             cls.pop();
22670             cls.push('left');
22671         }
22672         //this.picker().setXY(20000,20000);
22673         this.picker().addClass(cls.join('-'));
22674         
22675         var _this = this;
22676         
22677         Roo.each(cls, function(c){
22678             if(c == 'bottom'){
22679                 (function() {
22680                  //  
22681                 }).defer(200);
22682                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22683                 //_this.picker().setTop(_this.inputEl().getHeight());
22684                 return;
22685             }
22686             if(c == 'top'){
22687                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22688                 
22689                 //_this.picker().setTop(0 - _this.picker().getHeight());
22690                 return;
22691             }
22692             /*
22693             if(c == 'left'){
22694                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22695                 return;
22696             }
22697             if(c == 'right'){
22698                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22699                 return;
22700             }
22701             */
22702         });
22703         
22704     },
22705   
22706     onFocus : function()
22707     {
22708         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22709         this.show();
22710     },
22711     
22712     onBlur : function()
22713     {
22714         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22715         this.hide();
22716     },
22717     
22718     show : function()
22719     {
22720         this.picker().show();
22721         this.pop.show();
22722         this.update();
22723         this.place();
22724         
22725         this.fireEvent('show', this, this.date);
22726     },
22727     
22728     hide : function()
22729     {
22730         this.picker().hide();
22731         this.pop.hide();
22732         
22733         this.fireEvent('hide', this, this.date);
22734     },
22735     
22736     setTime : function()
22737     {
22738         this.hide();
22739         this.setValue(this.time.format(this.format));
22740         
22741         this.fireEvent('select', this, this.date);
22742         
22743         
22744     },
22745     
22746     onMousedown: function(e){
22747         e.stopPropagation();
22748         e.preventDefault();
22749     },
22750     
22751     onIncrementHours: function()
22752     {
22753         Roo.log('onIncrementHours');
22754         this.time = this.time.add(Date.HOUR, 1);
22755         this.update();
22756         
22757     },
22758     
22759     onDecrementHours: function()
22760     {
22761         Roo.log('onDecrementHours');
22762         this.time = this.time.add(Date.HOUR, -1);
22763         this.update();
22764     },
22765     
22766     onIncrementMinutes: function()
22767     {
22768         Roo.log('onIncrementMinutes');
22769         this.time = this.time.add(Date.MINUTE, 1);
22770         this.update();
22771     },
22772     
22773     onDecrementMinutes: function()
22774     {
22775         Roo.log('onDecrementMinutes');
22776         this.time = this.time.add(Date.MINUTE, -1);
22777         this.update();
22778     },
22779     
22780     onTogglePeriod: function()
22781     {
22782         Roo.log('onTogglePeriod');
22783         this.time = this.time.add(Date.HOUR, 12);
22784         this.update();
22785     }
22786     
22787    
22788 });
22789  
22790
22791 Roo.apply(Roo.bootstrap.TimeField,  {
22792   
22793     template : {
22794         tag: 'div',
22795         cls: 'datepicker dropdown-menu',
22796         cn: [
22797             {
22798                 tag: 'div',
22799                 cls: 'datepicker-time',
22800                 cn: [
22801                 {
22802                     tag: 'table',
22803                     cls: 'table-condensed',
22804                     cn:[
22805                         {
22806                             tag: 'tbody',
22807                             cn: [
22808                                 {
22809                                     tag: 'tr',
22810                                     cn: [
22811                                     {
22812                                         tag: 'td',
22813                                         colspan: '7'
22814                                     }
22815                                     ]
22816                                 }
22817                             ]
22818                         },
22819                         {
22820                             tag: 'tfoot',
22821                             cn: [
22822                                 {
22823                                     tag: 'tr',
22824                                     cn: [
22825                                     {
22826                                         tag: 'th',
22827                                         colspan: '7',
22828                                         cls: '',
22829                                         cn: [
22830                                             {
22831                                                 tag: 'button',
22832                                                 cls: 'btn btn-info ok',
22833                                                 html: 'OK'
22834                                             }
22835                                         ]
22836                                     }
22837                     
22838                                     ]
22839                                 }
22840                             ]
22841                         }
22842                     ]
22843                 }
22844                 ]
22845             }
22846         ]
22847     }
22848 });
22849
22850  
22851
22852  /*
22853  * - LGPL
22854  *
22855  * MonthField
22856  * 
22857  */
22858
22859 /**
22860  * @class Roo.bootstrap.MonthField
22861  * @extends Roo.bootstrap.Input
22862  * Bootstrap MonthField class
22863  * 
22864  * @cfg {String} language default en
22865  * 
22866  * @constructor
22867  * Create a new MonthField
22868  * @param {Object} config The config object
22869  */
22870
22871 Roo.bootstrap.MonthField = function(config){
22872     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22873     
22874     this.addEvents({
22875         /**
22876          * @event show
22877          * Fires when this field show.
22878          * @param {Roo.bootstrap.MonthField} this
22879          * @param {Mixed} date The date value
22880          */
22881         show : true,
22882         /**
22883          * @event show
22884          * Fires when this field hide.
22885          * @param {Roo.bootstrap.MonthField} this
22886          * @param {Mixed} date The date value
22887          */
22888         hide : true,
22889         /**
22890          * @event select
22891          * Fires when select a date.
22892          * @param {Roo.bootstrap.MonthField} this
22893          * @param {String} oldvalue The old value
22894          * @param {String} newvalue The new value
22895          */
22896         select : true
22897     });
22898 };
22899
22900 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22901     
22902     onRender: function(ct, position)
22903     {
22904         
22905         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22906         
22907         this.language = this.language || 'en';
22908         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22909         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22910         
22911         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22912         this.isInline = false;
22913         this.isInput = true;
22914         this.component = this.el.select('.add-on', true).first() || false;
22915         this.component = (this.component && this.component.length === 0) ? false : this.component;
22916         this.hasInput = this.component && this.inputEL().length;
22917         
22918         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22919         
22920         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22921         
22922         this.picker().on('mousedown', this.onMousedown, this);
22923         this.picker().on('click', this.onClick, this);
22924         
22925         this.picker().addClass('datepicker-dropdown');
22926         
22927         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22928             v.setStyle('width', '189px');
22929         });
22930         
22931         this.fillMonths();
22932         
22933         this.update();
22934         
22935         if(this.isInline) {
22936             this.show();
22937         }
22938         
22939     },
22940     
22941     setValue: function(v, suppressEvent)
22942     {   
22943         var o = this.getValue();
22944         
22945         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22946         
22947         this.update();
22948
22949         if(suppressEvent !== true){
22950             this.fireEvent('select', this, o, v);
22951         }
22952         
22953     },
22954     
22955     getValue: function()
22956     {
22957         return this.value;
22958     },
22959     
22960     onClick: function(e) 
22961     {
22962         e.stopPropagation();
22963         e.preventDefault();
22964         
22965         var target = e.getTarget();
22966         
22967         if(target.nodeName.toLowerCase() === 'i'){
22968             target = Roo.get(target).dom.parentNode;
22969         }
22970         
22971         var nodeName = target.nodeName;
22972         var className = target.className;
22973         var html = target.innerHTML;
22974         
22975         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22976             return;
22977         }
22978         
22979         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22980         
22981         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22982         
22983         this.hide();
22984                         
22985     },
22986     
22987     picker : function()
22988     {
22989         return this.pickerEl;
22990     },
22991     
22992     fillMonths: function()
22993     {    
22994         var i = 0;
22995         var months = this.picker().select('>.datepicker-months td', true).first();
22996         
22997         months.dom.innerHTML = '';
22998         
22999         while (i < 12) {
23000             var month = {
23001                 tag: 'span',
23002                 cls: 'month',
23003                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23004             };
23005             
23006             months.createChild(month);
23007         }
23008         
23009     },
23010     
23011     update: function()
23012     {
23013         var _this = this;
23014         
23015         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23016             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23017         }
23018         
23019         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23020             e.removeClass('active');
23021             
23022             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23023                 e.addClass('active');
23024             }
23025         })
23026     },
23027     
23028     place: function()
23029     {
23030         if(this.isInline) {
23031             return;
23032         }
23033         
23034         this.picker().removeClass(['bottom', 'top']);
23035         
23036         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23037             /*
23038              * place to the top of element!
23039              *
23040              */
23041             
23042             this.picker().addClass('top');
23043             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23044             
23045             return;
23046         }
23047         
23048         this.picker().addClass('bottom');
23049         
23050         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23051     },
23052     
23053     onFocus : function()
23054     {
23055         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23056         this.show();
23057     },
23058     
23059     onBlur : function()
23060     {
23061         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23062         
23063         var d = this.inputEl().getValue();
23064         
23065         this.setValue(d);
23066                 
23067         this.hide();
23068     },
23069     
23070     show : function()
23071     {
23072         this.picker().show();
23073         this.picker().select('>.datepicker-months', true).first().show();
23074         this.update();
23075         this.place();
23076         
23077         this.fireEvent('show', this, this.date);
23078     },
23079     
23080     hide : function()
23081     {
23082         if(this.isInline) {
23083             return;
23084         }
23085         this.picker().hide();
23086         this.fireEvent('hide', this, this.date);
23087         
23088     },
23089     
23090     onMousedown: function(e)
23091     {
23092         e.stopPropagation();
23093         e.preventDefault();
23094     },
23095     
23096     keyup: function(e)
23097     {
23098         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23099         this.update();
23100     },
23101
23102     fireKey: function(e)
23103     {
23104         if (!this.picker().isVisible()){
23105             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23106                 this.show();
23107             }
23108             return;
23109         }
23110         
23111         var dir;
23112         
23113         switch(e.keyCode){
23114             case 27: // escape
23115                 this.hide();
23116                 e.preventDefault();
23117                 break;
23118             case 37: // left
23119             case 39: // right
23120                 dir = e.keyCode == 37 ? -1 : 1;
23121                 
23122                 this.vIndex = this.vIndex + dir;
23123                 
23124                 if(this.vIndex < 0){
23125                     this.vIndex = 0;
23126                 }
23127                 
23128                 if(this.vIndex > 11){
23129                     this.vIndex = 11;
23130                 }
23131                 
23132                 if(isNaN(this.vIndex)){
23133                     this.vIndex = 0;
23134                 }
23135                 
23136                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23137                 
23138                 break;
23139             case 38: // up
23140             case 40: // down
23141                 
23142                 dir = e.keyCode == 38 ? -1 : 1;
23143                 
23144                 this.vIndex = this.vIndex + dir * 4;
23145                 
23146                 if(this.vIndex < 0){
23147                     this.vIndex = 0;
23148                 }
23149                 
23150                 if(this.vIndex > 11){
23151                     this.vIndex = 11;
23152                 }
23153                 
23154                 if(isNaN(this.vIndex)){
23155                     this.vIndex = 0;
23156                 }
23157                 
23158                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23159                 break;
23160                 
23161             case 13: // enter
23162                 
23163                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23164                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23165                 }
23166                 
23167                 this.hide();
23168                 e.preventDefault();
23169                 break;
23170             case 9: // tab
23171                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23172                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23173                 }
23174                 this.hide();
23175                 break;
23176             case 16: // shift
23177             case 17: // ctrl
23178             case 18: // alt
23179                 break;
23180             default :
23181                 this.hide();
23182                 
23183         }
23184     },
23185     
23186     remove: function() 
23187     {
23188         this.picker().remove();
23189     }
23190    
23191 });
23192
23193 Roo.apply(Roo.bootstrap.MonthField,  {
23194     
23195     content : {
23196         tag: 'tbody',
23197         cn: [
23198         {
23199             tag: 'tr',
23200             cn: [
23201             {
23202                 tag: 'td',
23203                 colspan: '7'
23204             }
23205             ]
23206         }
23207         ]
23208     },
23209     
23210     dates:{
23211         en: {
23212             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23213             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23214         }
23215     }
23216 });
23217
23218 Roo.apply(Roo.bootstrap.MonthField,  {
23219   
23220     template : {
23221         tag: 'div',
23222         cls: 'datepicker dropdown-menu roo-dynamic',
23223         cn: [
23224             {
23225                 tag: 'div',
23226                 cls: 'datepicker-months',
23227                 cn: [
23228                 {
23229                     tag: 'table',
23230                     cls: 'table-condensed',
23231                     cn:[
23232                         Roo.bootstrap.DateField.content
23233                     ]
23234                 }
23235                 ]
23236             }
23237         ]
23238     }
23239 });
23240
23241  
23242
23243  
23244  /*
23245  * - LGPL
23246  *
23247  * CheckBox
23248  * 
23249  */
23250
23251 /**
23252  * @class Roo.bootstrap.CheckBox
23253  * @extends Roo.bootstrap.Input
23254  * Bootstrap CheckBox class
23255  * 
23256  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23257  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23258  * @cfg {String} boxLabel The text that appears beside the checkbox
23259  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23260  * @cfg {Boolean} checked initnal the element
23261  * @cfg {Boolean} inline inline the element (default false)
23262  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23263  * @cfg {String} tooltip label tooltip
23264  * 
23265  * @constructor
23266  * Create a new CheckBox
23267  * @param {Object} config The config object
23268  */
23269
23270 Roo.bootstrap.CheckBox = function(config){
23271     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23272    
23273     this.addEvents({
23274         /**
23275         * @event check
23276         * Fires when the element is checked or unchecked.
23277         * @param {Roo.bootstrap.CheckBox} this This input
23278         * @param {Boolean} checked The new checked value
23279         */
23280        check : true,
23281        /**
23282         * @event click
23283         * Fires when the element is click.
23284         * @param {Roo.bootstrap.CheckBox} this This input
23285         */
23286        click : true
23287     });
23288     
23289 };
23290
23291 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23292   
23293     inputType: 'checkbox',
23294     inputValue: 1,
23295     valueOff: 0,
23296     boxLabel: false,
23297     checked: false,
23298     weight : false,
23299     inline: false,
23300     tooltip : '',
23301     
23302     // checkbox success does not make any sense really.. 
23303     invalidClass : "",
23304     validClass : "",
23305     
23306     
23307     getAutoCreate : function()
23308     {
23309         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23310         
23311         var id = Roo.id();
23312         
23313         var cfg = {};
23314         
23315         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23316         
23317         if(this.inline){
23318             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23319         }
23320         
23321         var input =  {
23322             tag: 'input',
23323             id : id,
23324             type : this.inputType,
23325             value : this.inputValue,
23326             cls : 'roo-' + this.inputType, //'form-box',
23327             placeholder : this.placeholder || ''
23328             
23329         };
23330         
23331         if(this.inputType != 'radio'){
23332             var hidden =  {
23333                 tag: 'input',
23334                 type : 'hidden',
23335                 cls : 'roo-hidden-value',
23336                 value : this.checked ? this.inputValue : this.valueOff
23337             };
23338         }
23339         
23340             
23341         if (this.weight) { // Validity check?
23342             cfg.cls += " " + this.inputType + "-" + this.weight;
23343         }
23344         
23345         if (this.disabled) {
23346             input.disabled=true;
23347         }
23348         
23349         if(this.checked){
23350             input.checked = this.checked;
23351         }
23352         
23353         if (this.name) {
23354             
23355             input.name = this.name;
23356             
23357             if(this.inputType != 'radio'){
23358                 hidden.name = this.name;
23359                 input.name = '_hidden_' + this.name;
23360             }
23361         }
23362         
23363         if (this.size) {
23364             input.cls += ' input-' + this.size;
23365         }
23366         
23367         var settings=this;
23368         
23369         ['xs','sm','md','lg'].map(function(size){
23370             if (settings[size]) {
23371                 cfg.cls += ' col-' + size + '-' + settings[size];
23372             }
23373         });
23374         
23375         var inputblock = input;
23376          
23377         if (this.before || this.after) {
23378             
23379             inputblock = {
23380                 cls : 'input-group',
23381                 cn :  [] 
23382             };
23383             
23384             if (this.before) {
23385                 inputblock.cn.push({
23386                     tag :'span',
23387                     cls : 'input-group-addon',
23388                     html : this.before
23389                 });
23390             }
23391             
23392             inputblock.cn.push(input);
23393             
23394             if(this.inputType != 'radio'){
23395                 inputblock.cn.push(hidden);
23396             }
23397             
23398             if (this.after) {
23399                 inputblock.cn.push({
23400                     tag :'span',
23401                     cls : 'input-group-addon',
23402                     html : this.after
23403                 });
23404             }
23405             
23406         }
23407         var boxLabelCfg = false;
23408         
23409         if(this.boxLabel){
23410            
23411             boxLabelCfg = {
23412                 tag: 'label',
23413                 //'for': id, // box label is handled by onclick - so no for...
23414                 cls: 'box-label',
23415                 html: this.boxLabel
23416             };
23417             if(this.tooltip){
23418                 boxLabelCfg.tooltip = this.tooltip;
23419             }
23420              
23421         }
23422         
23423         
23424         if (align ==='left' && this.fieldLabel.length) {
23425 //                Roo.log("left and has label");
23426             cfg.cn = [
23427                 {
23428                     tag: 'label',
23429                     'for' :  id,
23430                     cls : 'control-label',
23431                     html : this.fieldLabel
23432                 },
23433                 {
23434                     cls : "", 
23435                     cn: [
23436                         inputblock
23437                     ]
23438                 }
23439             ];
23440             
23441             if (boxLabelCfg) {
23442                 cfg.cn[1].cn.push(boxLabelCfg);
23443             }
23444             
23445             if(this.labelWidth > 12){
23446                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23447             }
23448             
23449             if(this.labelWidth < 13 && this.labelmd == 0){
23450                 this.labelmd = this.labelWidth;
23451             }
23452             
23453             if(this.labellg > 0){
23454                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23455                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23456             }
23457             
23458             if(this.labelmd > 0){
23459                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23460                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23461             }
23462             
23463             if(this.labelsm > 0){
23464                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23465                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23466             }
23467             
23468             if(this.labelxs > 0){
23469                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23470                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23471             }
23472             
23473         } else if ( this.fieldLabel.length) {
23474 //                Roo.log(" label");
23475                 cfg.cn = [
23476                    
23477                     {
23478                         tag: this.boxLabel ? 'span' : 'label',
23479                         'for': id,
23480                         cls: 'control-label box-input-label',
23481                         //cls : 'input-group-addon',
23482                         html : this.fieldLabel
23483                     },
23484                     
23485                     inputblock
23486                     
23487                 ];
23488                 if (boxLabelCfg) {
23489                     cfg.cn.push(boxLabelCfg);
23490                 }
23491
23492         } else {
23493             
23494 //                Roo.log(" no label && no align");
23495                 cfg.cn = [  inputblock ] ;
23496                 if (boxLabelCfg) {
23497                     cfg.cn.push(boxLabelCfg);
23498                 }
23499
23500                 
23501         }
23502         
23503        
23504         
23505         if(this.inputType != 'radio'){
23506             cfg.cn.push(hidden);
23507         }
23508         
23509         return cfg;
23510         
23511     },
23512     
23513     /**
23514      * return the real input element.
23515      */
23516     inputEl: function ()
23517     {
23518         return this.el.select('input.roo-' + this.inputType,true).first();
23519     },
23520     hiddenEl: function ()
23521     {
23522         return this.el.select('input.roo-hidden-value',true).first();
23523     },
23524     
23525     labelEl: function()
23526     {
23527         return this.el.select('label.control-label',true).first();
23528     },
23529     /* depricated... */
23530     
23531     label: function()
23532     {
23533         return this.labelEl();
23534     },
23535     
23536     boxLabelEl: function()
23537     {
23538         return this.el.select('label.box-label',true).first();
23539     },
23540     
23541     initEvents : function()
23542     {
23543 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23544         
23545         this.inputEl().on('click', this.onClick,  this);
23546         
23547         if (this.boxLabel) { 
23548             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23549         }
23550         
23551         this.startValue = this.getValue();
23552         
23553         if(this.groupId){
23554             Roo.bootstrap.CheckBox.register(this);
23555         }
23556     },
23557     
23558     onClick : function(e)
23559     {   
23560         if(this.fireEvent('click', this, e) !== false){
23561             this.setChecked(!this.checked);
23562         }
23563         
23564     },
23565     
23566     setChecked : function(state,suppressEvent)
23567     {
23568         this.startValue = this.getValue();
23569
23570         if(this.inputType == 'radio'){
23571             
23572             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23573                 e.dom.checked = false;
23574             });
23575             
23576             this.inputEl().dom.checked = true;
23577             
23578             this.inputEl().dom.value = this.inputValue;
23579             
23580             if(suppressEvent !== true){
23581                 this.fireEvent('check', this, true);
23582             }
23583             
23584             this.validate();
23585             
23586             return;
23587         }
23588         
23589         this.checked = state;
23590         
23591         this.inputEl().dom.checked = state;
23592         
23593         
23594         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23595         
23596         if(suppressEvent !== true){
23597             this.fireEvent('check', this, state);
23598         }
23599         
23600         this.validate();
23601     },
23602     
23603     getValue : function()
23604     {
23605         if(this.inputType == 'radio'){
23606             return this.getGroupValue();
23607         }
23608         
23609         return this.hiddenEl().dom.value;
23610         
23611     },
23612     
23613     getGroupValue : function()
23614     {
23615         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23616             return '';
23617         }
23618         
23619         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23620     },
23621     
23622     setValue : function(v,suppressEvent)
23623     {
23624         if(this.inputType == 'radio'){
23625             this.setGroupValue(v, suppressEvent);
23626             return;
23627         }
23628         
23629         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23630         
23631         this.validate();
23632     },
23633     
23634     setGroupValue : function(v, suppressEvent)
23635     {
23636         this.startValue = this.getValue();
23637         
23638         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23639             e.dom.checked = false;
23640             
23641             if(e.dom.value == v){
23642                 e.dom.checked = true;
23643             }
23644         });
23645         
23646         if(suppressEvent !== true){
23647             this.fireEvent('check', this, true);
23648         }
23649
23650         this.validate();
23651         
23652         return;
23653     },
23654     
23655     validate : function()
23656     {
23657         if(this.getVisibilityEl().hasClass('hidden')){
23658             return true;
23659         }
23660         
23661         if(
23662                 this.disabled || 
23663                 (this.inputType == 'radio' && this.validateRadio()) ||
23664                 (this.inputType == 'checkbox' && this.validateCheckbox())
23665         ){
23666             this.markValid();
23667             return true;
23668         }
23669         
23670         this.markInvalid();
23671         return false;
23672     },
23673     
23674     validateRadio : function()
23675     {
23676         if(this.getVisibilityEl().hasClass('hidden')){
23677             return true;
23678         }
23679         
23680         if(this.allowBlank){
23681             return true;
23682         }
23683         
23684         var valid = false;
23685         
23686         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23687             if(!e.dom.checked){
23688                 return;
23689             }
23690             
23691             valid = true;
23692             
23693             return false;
23694         });
23695         
23696         return valid;
23697     },
23698     
23699     validateCheckbox : function()
23700     {
23701         if(!this.groupId){
23702             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23703             //return (this.getValue() == this.inputValue) ? true : false;
23704         }
23705         
23706         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23707         
23708         if(!group){
23709             return false;
23710         }
23711         
23712         var r = false;
23713         
23714         for(var i in group){
23715             if(group[i].el.isVisible(true)){
23716                 r = false;
23717                 break;
23718             }
23719             
23720             r = true;
23721         }
23722         
23723         for(var i in group){
23724             if(r){
23725                 break;
23726             }
23727             
23728             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23729         }
23730         
23731         return r;
23732     },
23733     
23734     /**
23735      * Mark this field as valid
23736      */
23737     markValid : function()
23738     {
23739         var _this = this;
23740         
23741         this.fireEvent('valid', this);
23742         
23743         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23744         
23745         if(this.groupId){
23746             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23747         }
23748         
23749         if(label){
23750             label.markValid();
23751         }
23752
23753         if(this.inputType == 'radio'){
23754             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23755                 var fg = e.findParent('.form-group', false, true);
23756                 if (Roo.bootstrap.version == 3) {
23757                     fg.removeClass([_this.invalidClass, _this.validClass]);
23758                     fg.addClass(_this.validClass);
23759                 } else {
23760                     fg.removeClass(['is-valid', 'is-invalid']);
23761                     fg.addClass('is-valid');
23762                 }
23763             });
23764             
23765             return;
23766         }
23767
23768         if(!this.groupId){
23769             var fg = this.el.findParent('.form-group', false, true);
23770             if (Roo.bootstrap.version == 3) {
23771                 fg.removeClass([this.invalidClass, this.validClass]);
23772                 fg.addClass(this.validClass);
23773             } else {
23774                 fg.removeClass(['is-valid', 'is-invalid']);
23775                 fg.addClass('is-valid');
23776             }
23777             return;
23778         }
23779         
23780         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23781         
23782         if(!group){
23783             return;
23784         }
23785         
23786         for(var i in group){
23787             var fg = group[i].el.findParent('.form-group', false, true);
23788             if (Roo.bootstrap.version == 3) {
23789                 fg.removeClass([this.invalidClass, this.validClass]);
23790                 fg.addClass(this.validClass);
23791             } else {
23792                 fg.removeClass(['is-valid', 'is-invalid']);
23793                 fg.addClass('is-valid');
23794             }
23795         }
23796     },
23797     
23798      /**
23799      * Mark this field as invalid
23800      * @param {String} msg The validation message
23801      */
23802     markInvalid : function(msg)
23803     {
23804         if(this.allowBlank){
23805             return;
23806         }
23807         
23808         var _this = this;
23809         
23810         this.fireEvent('invalid', this, msg);
23811         
23812         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23813         
23814         if(this.groupId){
23815             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23816         }
23817         
23818         if(label){
23819             label.markInvalid();
23820         }
23821             
23822         if(this.inputType == 'radio'){
23823             
23824             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23825                 var fg = e.findParent('.form-group', false, true);
23826                 if (Roo.bootstrap.version == 3) {
23827                     fg.removeClass([_this.invalidClass, _this.validClass]);
23828                     fg.addClass(_this.invalidClass);
23829                 } else {
23830                     fg.removeClass(['is-invalid', 'is-valid']);
23831                     fg.addClass('is-invalid');
23832                 }
23833             });
23834             
23835             return;
23836         }
23837         
23838         if(!this.groupId){
23839             var fg = this.el.findParent('.form-group', false, true);
23840             if (Roo.bootstrap.version == 3) {
23841                 fg.removeClass([_this.invalidClass, _this.validClass]);
23842                 fg.addClass(_this.invalidClass);
23843             } else {
23844                 fg.removeClass(['is-invalid', 'is-valid']);
23845                 fg.addClass('is-invalid');
23846             }
23847             return;
23848         }
23849         
23850         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23851         
23852         if(!group){
23853             return;
23854         }
23855         
23856         for(var i in group){
23857             var fg = group[i].el.findParent('.form-group', false, true);
23858             if (Roo.bootstrap.version == 3) {
23859                 fg.removeClass([_this.invalidClass, _this.validClass]);
23860                 fg.addClass(_this.invalidClass);
23861             } else {
23862                 fg.removeClass(['is-invalid', 'is-valid']);
23863                 fg.addClass('is-invalid');
23864             }
23865         }
23866         
23867     },
23868     
23869     clearInvalid : function()
23870     {
23871         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23872         
23873         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23874         
23875         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23876         
23877         if (label && label.iconEl) {
23878             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23879             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23880         }
23881     },
23882     
23883     disable : function()
23884     {
23885         if(this.inputType != 'radio'){
23886             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23887             return;
23888         }
23889         
23890         var _this = this;
23891         
23892         if(this.rendered){
23893             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23894                 _this.getActionEl().addClass(this.disabledClass);
23895                 e.dom.disabled = true;
23896             });
23897         }
23898         
23899         this.disabled = true;
23900         this.fireEvent("disable", this);
23901         return this;
23902     },
23903
23904     enable : function()
23905     {
23906         if(this.inputType != 'radio'){
23907             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23908             return;
23909         }
23910         
23911         var _this = this;
23912         
23913         if(this.rendered){
23914             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23915                 _this.getActionEl().removeClass(this.disabledClass);
23916                 e.dom.disabled = false;
23917             });
23918         }
23919         
23920         this.disabled = false;
23921         this.fireEvent("enable", this);
23922         return this;
23923     },
23924     
23925     setBoxLabel : function(v)
23926     {
23927         this.boxLabel = v;
23928         
23929         if(this.rendered){
23930             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23931         }
23932     }
23933
23934 });
23935
23936 Roo.apply(Roo.bootstrap.CheckBox, {
23937     
23938     groups: {},
23939     
23940      /**
23941     * register a CheckBox Group
23942     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23943     */
23944     register : function(checkbox)
23945     {
23946         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23947             this.groups[checkbox.groupId] = {};
23948         }
23949         
23950         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23951             return;
23952         }
23953         
23954         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23955         
23956     },
23957     /**
23958     * fetch a CheckBox Group based on the group ID
23959     * @param {string} the group ID
23960     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23961     */
23962     get: function(groupId) {
23963         if (typeof(this.groups[groupId]) == 'undefined') {
23964             return false;
23965         }
23966         
23967         return this.groups[groupId] ;
23968     }
23969     
23970     
23971 });
23972 /*
23973  * - LGPL
23974  *
23975  * RadioItem
23976  * 
23977  */
23978
23979 /**
23980  * @class Roo.bootstrap.Radio
23981  * @extends Roo.bootstrap.Component
23982  * Bootstrap Radio class
23983  * @cfg {String} boxLabel - the label associated
23984  * @cfg {String} value - the value of radio
23985  * 
23986  * @constructor
23987  * Create a new Radio
23988  * @param {Object} config The config object
23989  */
23990 Roo.bootstrap.Radio = function(config){
23991     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23992     
23993 };
23994
23995 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23996     
23997     boxLabel : '',
23998     
23999     value : '',
24000     
24001     getAutoCreate : function()
24002     {
24003         var cfg = {
24004             tag : 'div',
24005             cls : 'form-group radio',
24006             cn : [
24007                 {
24008                     tag : 'label',
24009                     cls : 'box-label',
24010                     html : this.boxLabel
24011                 }
24012             ]
24013         };
24014         
24015         return cfg;
24016     },
24017     
24018     initEvents : function() 
24019     {
24020         this.parent().register(this);
24021         
24022         this.el.on('click', this.onClick, this);
24023         
24024     },
24025     
24026     onClick : function(e)
24027     {
24028         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24029             this.setChecked(true);
24030         }
24031     },
24032     
24033     setChecked : function(state, suppressEvent)
24034     {
24035         this.parent().setValue(this.value, suppressEvent);
24036         
24037     },
24038     
24039     setBoxLabel : function(v)
24040     {
24041         this.boxLabel = v;
24042         
24043         if(this.rendered){
24044             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24045         }
24046     }
24047     
24048 });
24049  
24050
24051  /*
24052  * - LGPL
24053  *
24054  * Input
24055  * 
24056  */
24057
24058 /**
24059  * @class Roo.bootstrap.SecurePass
24060  * @extends Roo.bootstrap.Input
24061  * Bootstrap SecurePass class
24062  *
24063  * 
24064  * @constructor
24065  * Create a new SecurePass
24066  * @param {Object} config The config object
24067  */
24068  
24069 Roo.bootstrap.SecurePass = function (config) {
24070     // these go here, so the translation tool can replace them..
24071     this.errors = {
24072         PwdEmpty: "Please type a password, and then retype it to confirm.",
24073         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24074         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24075         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24076         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24077         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24078         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24079         TooWeak: "Your password is Too Weak."
24080     },
24081     this.meterLabel = "Password strength:";
24082     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24083     this.meterClass = [
24084         "roo-password-meter-tooweak", 
24085         "roo-password-meter-weak", 
24086         "roo-password-meter-medium", 
24087         "roo-password-meter-strong", 
24088         "roo-password-meter-grey"
24089     ];
24090     
24091     this.errors = {};
24092     
24093     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24094 }
24095
24096 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24097     /**
24098      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24099      * {
24100      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24101      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24102      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24103      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24104      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24105      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24106      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24107      * })
24108      */
24109     // private
24110     
24111     meterWidth: 300,
24112     errorMsg :'',    
24113     errors: false,
24114     imageRoot: '/',
24115     /**
24116      * @cfg {String/Object} Label for the strength meter (defaults to
24117      * 'Password strength:')
24118      */
24119     // private
24120     meterLabel: '',
24121     /**
24122      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24123      * ['Weak', 'Medium', 'Strong'])
24124      */
24125     // private    
24126     pwdStrengths: false,    
24127     // private
24128     strength: 0,
24129     // private
24130     _lastPwd: null,
24131     // private
24132     kCapitalLetter: 0,
24133     kSmallLetter: 1,
24134     kDigit: 2,
24135     kPunctuation: 3,
24136     
24137     insecure: false,
24138     // private
24139     initEvents: function ()
24140     {
24141         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24142
24143         if (this.el.is('input[type=password]') && Roo.isSafari) {
24144             this.el.on('keydown', this.SafariOnKeyDown, this);
24145         }
24146
24147         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24148     },
24149     // private
24150     onRender: function (ct, position)
24151     {
24152         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24153         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24154         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24155
24156         this.trigger.createChild({
24157                    cn: [
24158                     {
24159                     //id: 'PwdMeter',
24160                     tag: 'div',
24161                     cls: 'roo-password-meter-grey col-xs-12',
24162                     style: {
24163                         //width: 0,
24164                         //width: this.meterWidth + 'px'                                                
24165                         }
24166                     },
24167                     {                            
24168                          cls: 'roo-password-meter-text'                          
24169                     }
24170                 ]            
24171         });
24172
24173          
24174         if (this.hideTrigger) {
24175             this.trigger.setDisplayed(false);
24176         }
24177         this.setSize(this.width || '', this.height || '');
24178     },
24179     // private
24180     onDestroy: function ()
24181     {
24182         if (this.trigger) {
24183             this.trigger.removeAllListeners();
24184             this.trigger.remove();
24185         }
24186         if (this.wrap) {
24187             this.wrap.remove();
24188         }
24189         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24190     },
24191     // private
24192     checkStrength: function ()
24193     {
24194         var pwd = this.inputEl().getValue();
24195         if (pwd == this._lastPwd) {
24196             return;
24197         }
24198
24199         var strength;
24200         if (this.ClientSideStrongPassword(pwd)) {
24201             strength = 3;
24202         } else if (this.ClientSideMediumPassword(pwd)) {
24203             strength = 2;
24204         } else if (this.ClientSideWeakPassword(pwd)) {
24205             strength = 1;
24206         } else {
24207             strength = 0;
24208         }
24209         
24210         Roo.log('strength1: ' + strength);
24211         
24212         //var pm = this.trigger.child('div/div/div').dom;
24213         var pm = this.trigger.child('div/div');
24214         pm.removeClass(this.meterClass);
24215         pm.addClass(this.meterClass[strength]);
24216                 
24217         
24218         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24219                 
24220         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24221         
24222         this._lastPwd = pwd;
24223     },
24224     reset: function ()
24225     {
24226         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24227         
24228         this._lastPwd = '';
24229         
24230         var pm = this.trigger.child('div/div');
24231         pm.removeClass(this.meterClass);
24232         pm.addClass('roo-password-meter-grey');        
24233         
24234         
24235         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24236         
24237         pt.innerHTML = '';
24238         this.inputEl().dom.type='password';
24239     },
24240     // private
24241     validateValue: function (value)
24242     {
24243         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24244             return false;
24245         }
24246         if (value.length == 0) {
24247             if (this.allowBlank) {
24248                 this.clearInvalid();
24249                 return true;
24250             }
24251
24252             this.markInvalid(this.errors.PwdEmpty);
24253             this.errorMsg = this.errors.PwdEmpty;
24254             return false;
24255         }
24256         
24257         if(this.insecure){
24258             return true;
24259         }
24260         
24261         if (!value.match(/[\x21-\x7e]+/)) {
24262             this.markInvalid(this.errors.PwdBadChar);
24263             this.errorMsg = this.errors.PwdBadChar;
24264             return false;
24265         }
24266         if (value.length < 6) {
24267             this.markInvalid(this.errors.PwdShort);
24268             this.errorMsg = this.errors.PwdShort;
24269             return false;
24270         }
24271         if (value.length > 16) {
24272             this.markInvalid(this.errors.PwdLong);
24273             this.errorMsg = this.errors.PwdLong;
24274             return false;
24275         }
24276         var strength;
24277         if (this.ClientSideStrongPassword(value)) {
24278             strength = 3;
24279         } else if (this.ClientSideMediumPassword(value)) {
24280             strength = 2;
24281         } else if (this.ClientSideWeakPassword(value)) {
24282             strength = 1;
24283         } else {
24284             strength = 0;
24285         }
24286
24287         
24288         if (strength < 2) {
24289             //this.markInvalid(this.errors.TooWeak);
24290             this.errorMsg = this.errors.TooWeak;
24291             //return false;
24292         }
24293         
24294         
24295         console.log('strength2: ' + strength);
24296         
24297         //var pm = this.trigger.child('div/div/div').dom;
24298         
24299         var pm = this.trigger.child('div/div');
24300         pm.removeClass(this.meterClass);
24301         pm.addClass(this.meterClass[strength]);
24302                 
24303         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24304                 
24305         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24306         
24307         this.errorMsg = ''; 
24308         return true;
24309     },
24310     // private
24311     CharacterSetChecks: function (type)
24312     {
24313         this.type = type;
24314         this.fResult = false;
24315     },
24316     // private
24317     isctype: function (character, type)
24318     {
24319         switch (type) {  
24320             case this.kCapitalLetter:
24321                 if (character >= 'A' && character <= 'Z') {
24322                     return true;
24323                 }
24324                 break;
24325             
24326             case this.kSmallLetter:
24327                 if (character >= 'a' && character <= 'z') {
24328                     return true;
24329                 }
24330                 break;
24331             
24332             case this.kDigit:
24333                 if (character >= '0' && character <= '9') {
24334                     return true;
24335                 }
24336                 break;
24337             
24338             case this.kPunctuation:
24339                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24340                     return true;
24341                 }
24342                 break;
24343             
24344             default:
24345                 return false;
24346         }
24347
24348     },
24349     // private
24350     IsLongEnough: function (pwd, size)
24351     {
24352         return !(pwd == null || isNaN(size) || pwd.length < size);
24353     },
24354     // private
24355     SpansEnoughCharacterSets: function (word, nb)
24356     {
24357         if (!this.IsLongEnough(word, nb))
24358         {
24359             return false;
24360         }
24361
24362         var characterSetChecks = new Array(
24363             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24364             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24365         );
24366         
24367         for (var index = 0; index < word.length; ++index) {
24368             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24369                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24370                     characterSetChecks[nCharSet].fResult = true;
24371                     break;
24372                 }
24373             }
24374         }
24375
24376         var nCharSets = 0;
24377         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24378             if (characterSetChecks[nCharSet].fResult) {
24379                 ++nCharSets;
24380             }
24381         }
24382
24383         if (nCharSets < nb) {
24384             return false;
24385         }
24386         return true;
24387     },
24388     // private
24389     ClientSideStrongPassword: function (pwd)
24390     {
24391         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24392     },
24393     // private
24394     ClientSideMediumPassword: function (pwd)
24395     {
24396         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24397     },
24398     // private
24399     ClientSideWeakPassword: function (pwd)
24400     {
24401         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24402     }
24403           
24404 })//<script type="text/javascript">
24405
24406 /*
24407  * Based  Ext JS Library 1.1.1
24408  * Copyright(c) 2006-2007, Ext JS, LLC.
24409  * LGPL
24410  *
24411  */
24412  
24413 /**
24414  * @class Roo.HtmlEditorCore
24415  * @extends Roo.Component
24416  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24417  *
24418  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24419  */
24420
24421 Roo.HtmlEditorCore = function(config){
24422     
24423     
24424     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24425     
24426     
24427     this.addEvents({
24428         /**
24429          * @event initialize
24430          * Fires when the editor is fully initialized (including the iframe)
24431          * @param {Roo.HtmlEditorCore} this
24432          */
24433         initialize: true,
24434         /**
24435          * @event activate
24436          * Fires when the editor is first receives the focus. Any insertion must wait
24437          * until after this event.
24438          * @param {Roo.HtmlEditorCore} this
24439          */
24440         activate: true,
24441          /**
24442          * @event beforesync
24443          * Fires before the textarea is updated with content from the editor iframe. Return false
24444          * to cancel the sync.
24445          * @param {Roo.HtmlEditorCore} this
24446          * @param {String} html
24447          */
24448         beforesync: true,
24449          /**
24450          * @event beforepush
24451          * Fires before the iframe editor is updated with content from the textarea. Return false
24452          * to cancel the push.
24453          * @param {Roo.HtmlEditorCore} this
24454          * @param {String} html
24455          */
24456         beforepush: true,
24457          /**
24458          * @event sync
24459          * Fires when the textarea is updated with content from the editor iframe.
24460          * @param {Roo.HtmlEditorCore} this
24461          * @param {String} html
24462          */
24463         sync: true,
24464          /**
24465          * @event push
24466          * Fires when the iframe editor is updated with content from the textarea.
24467          * @param {Roo.HtmlEditorCore} this
24468          * @param {String} html
24469          */
24470         push: true,
24471         
24472         /**
24473          * @event editorevent
24474          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24475          * @param {Roo.HtmlEditorCore} this
24476          */
24477         editorevent: true
24478         
24479     });
24480     
24481     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24482     
24483     // defaults : white / black...
24484     this.applyBlacklists();
24485     
24486     
24487     
24488 };
24489
24490
24491 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24492
24493
24494      /**
24495      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24496      */
24497     
24498     owner : false,
24499     
24500      /**
24501      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24502      *                        Roo.resizable.
24503      */
24504     resizable : false,
24505      /**
24506      * @cfg {Number} height (in pixels)
24507      */   
24508     height: 300,
24509    /**
24510      * @cfg {Number} width (in pixels)
24511      */   
24512     width: 500,
24513     
24514     /**
24515      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24516      * 
24517      */
24518     stylesheets: false,
24519     
24520     // id of frame..
24521     frameId: false,
24522     
24523     // private properties
24524     validationEvent : false,
24525     deferHeight: true,
24526     initialized : false,
24527     activated : false,
24528     sourceEditMode : false,
24529     onFocus : Roo.emptyFn,
24530     iframePad:3,
24531     hideMode:'offsets',
24532     
24533     clearUp: true,
24534     
24535     // blacklist + whitelisted elements..
24536     black: false,
24537     white: false,
24538      
24539     bodyCls : '',
24540
24541     /**
24542      * Protected method that will not generally be called directly. It
24543      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24544      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24545      */
24546     getDocMarkup : function(){
24547         // body styles..
24548         var st = '';
24549         
24550         // inherit styels from page...?? 
24551         if (this.stylesheets === false) {
24552             
24553             Roo.get(document.head).select('style').each(function(node) {
24554                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24555             });
24556             
24557             Roo.get(document.head).select('link').each(function(node) { 
24558                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24559             });
24560             
24561         } else if (!this.stylesheets.length) {
24562                 // simple..
24563                 st = '<style type="text/css">' +
24564                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24565                    '</style>';
24566         } else {
24567             for (var i in this.stylesheets) { 
24568                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24569             }
24570             
24571         }
24572         
24573         st +=  '<style type="text/css">' +
24574             'IMG { cursor: pointer } ' +
24575         '</style>';
24576
24577         var cls = 'roo-htmleditor-body';
24578         
24579         if(this.bodyCls.length){
24580             cls += ' ' + this.bodyCls;
24581         }
24582         
24583         return '<html><head>' + st  +
24584             //<style type="text/css">' +
24585             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24586             //'</style>' +
24587             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24588     },
24589
24590     // private
24591     onRender : function(ct, position)
24592     {
24593         var _t = this;
24594         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24595         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24596         
24597         
24598         this.el.dom.style.border = '0 none';
24599         this.el.dom.setAttribute('tabIndex', -1);
24600         this.el.addClass('x-hidden hide');
24601         
24602         
24603         
24604         if(Roo.isIE){ // fix IE 1px bogus margin
24605             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24606         }
24607        
24608         
24609         this.frameId = Roo.id();
24610         
24611          
24612         
24613         var iframe = this.owner.wrap.createChild({
24614             tag: 'iframe',
24615             cls: 'form-control', // bootstrap..
24616             id: this.frameId,
24617             name: this.frameId,
24618             frameBorder : 'no',
24619             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24620         }, this.el
24621         );
24622         
24623         
24624         this.iframe = iframe.dom;
24625
24626          this.assignDocWin();
24627         
24628         this.doc.designMode = 'on';
24629        
24630         this.doc.open();
24631         this.doc.write(this.getDocMarkup());
24632         this.doc.close();
24633
24634         
24635         var task = { // must defer to wait for browser to be ready
24636             run : function(){
24637                 //console.log("run task?" + this.doc.readyState);
24638                 this.assignDocWin();
24639                 if(this.doc.body || this.doc.readyState == 'complete'){
24640                     try {
24641                         this.doc.designMode="on";
24642                     } catch (e) {
24643                         return;
24644                     }
24645                     Roo.TaskMgr.stop(task);
24646                     this.initEditor.defer(10, this);
24647                 }
24648             },
24649             interval : 10,
24650             duration: 10000,
24651             scope: this
24652         };
24653         Roo.TaskMgr.start(task);
24654
24655     },
24656
24657     // private
24658     onResize : function(w, h)
24659     {
24660          Roo.log('resize: ' +w + ',' + h );
24661         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24662         if(!this.iframe){
24663             return;
24664         }
24665         if(typeof w == 'number'){
24666             
24667             this.iframe.style.width = w + 'px';
24668         }
24669         if(typeof h == 'number'){
24670             
24671             this.iframe.style.height = h + 'px';
24672             if(this.doc){
24673                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24674             }
24675         }
24676         
24677     },
24678
24679     /**
24680      * Toggles the editor between standard and source edit mode.
24681      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24682      */
24683     toggleSourceEdit : function(sourceEditMode){
24684         
24685         this.sourceEditMode = sourceEditMode === true;
24686         
24687         if(this.sourceEditMode){
24688  
24689             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24690             
24691         }else{
24692             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24693             //this.iframe.className = '';
24694             this.deferFocus();
24695         }
24696         //this.setSize(this.owner.wrap.getSize());
24697         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24698     },
24699
24700     
24701   
24702
24703     /**
24704      * Protected method that will not generally be called directly. If you need/want
24705      * custom HTML cleanup, this is the method you should override.
24706      * @param {String} html The HTML to be cleaned
24707      * return {String} The cleaned HTML
24708      */
24709     cleanHtml : function(html){
24710         html = String(html);
24711         if(html.length > 5){
24712             if(Roo.isSafari){ // strip safari nonsense
24713                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24714             }
24715         }
24716         if(html == '&nbsp;'){
24717             html = '';
24718         }
24719         return html;
24720     },
24721
24722     /**
24723      * HTML Editor -> Textarea
24724      * Protected method that will not generally be called directly. Syncs the contents
24725      * of the editor iframe with the textarea.
24726      */
24727     syncValue : function(){
24728         if(this.initialized){
24729             var bd = (this.doc.body || this.doc.documentElement);
24730             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24731             var html = bd.innerHTML;
24732             if(Roo.isSafari){
24733                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24734                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24735                 if(m && m[1]){
24736                     html = '<div style="'+m[0]+'">' + html + '</div>';
24737                 }
24738             }
24739             html = this.cleanHtml(html);
24740             // fix up the special chars.. normaly like back quotes in word...
24741             // however we do not want to do this with chinese..
24742             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24743                 
24744                 var cc = match.charCodeAt();
24745
24746                 // Get the character value, handling surrogate pairs
24747                 if (match.length == 2) {
24748                     // It's a surrogate pair, calculate the Unicode code point
24749                     var high = match.charCodeAt(0) - 0xD800;
24750                     var low  = match.charCodeAt(1) - 0xDC00;
24751                     cc = (high * 0x400) + low + 0x10000;
24752                 }  else if (
24753                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24754                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24755                     (cc >= 0xf900 && cc < 0xfb00 )
24756                 ) {
24757                         return match;
24758                 }  
24759          
24760                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24761                 return "&#" + cc + ";";
24762                 
24763                 
24764             });
24765             
24766             
24767              
24768             if(this.owner.fireEvent('beforesync', this, html) !== false){
24769                 this.el.dom.value = html;
24770                 this.owner.fireEvent('sync', this, html);
24771             }
24772         }
24773     },
24774
24775     /**
24776      * Protected method that will not generally be called directly. Pushes the value of the textarea
24777      * into the iframe editor.
24778      */
24779     pushValue : function(){
24780         if(this.initialized){
24781             var v = this.el.dom.value.trim();
24782             
24783 //            if(v.length < 1){
24784 //                v = '&#160;';
24785 //            }
24786             
24787             if(this.owner.fireEvent('beforepush', this, v) !== false){
24788                 var d = (this.doc.body || this.doc.documentElement);
24789                 d.innerHTML = v;
24790                 this.cleanUpPaste();
24791                 this.el.dom.value = d.innerHTML;
24792                 this.owner.fireEvent('push', this, v);
24793             }
24794         }
24795     },
24796
24797     // private
24798     deferFocus : function(){
24799         this.focus.defer(10, this);
24800     },
24801
24802     // doc'ed in Field
24803     focus : function(){
24804         if(this.win && !this.sourceEditMode){
24805             this.win.focus();
24806         }else{
24807             this.el.focus();
24808         }
24809     },
24810     
24811     assignDocWin: function()
24812     {
24813         var iframe = this.iframe;
24814         
24815          if(Roo.isIE){
24816             this.doc = iframe.contentWindow.document;
24817             this.win = iframe.contentWindow;
24818         } else {
24819 //            if (!Roo.get(this.frameId)) {
24820 //                return;
24821 //            }
24822 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24823 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24824             
24825             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24826                 return;
24827             }
24828             
24829             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24830             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24831         }
24832     },
24833     
24834     // private
24835     initEditor : function(){
24836         //console.log("INIT EDITOR");
24837         this.assignDocWin();
24838         
24839         
24840         
24841         this.doc.designMode="on";
24842         this.doc.open();
24843         this.doc.write(this.getDocMarkup());
24844         this.doc.close();
24845         
24846         var dbody = (this.doc.body || this.doc.documentElement);
24847         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24848         // this copies styles from the containing element into thsi one..
24849         // not sure why we need all of this..
24850         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24851         
24852         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24853         //ss['background-attachment'] = 'fixed'; // w3c
24854         dbody.bgProperties = 'fixed'; // ie
24855         //Roo.DomHelper.applyStyles(dbody, ss);
24856         Roo.EventManager.on(this.doc, {
24857             //'mousedown': this.onEditorEvent,
24858             'mouseup': this.onEditorEvent,
24859             'dblclick': this.onEditorEvent,
24860             'click': this.onEditorEvent,
24861             'keyup': this.onEditorEvent,
24862             buffer:100,
24863             scope: this
24864         });
24865         if(Roo.isGecko){
24866             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24867         }
24868         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24869             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24870         }
24871         this.initialized = true;
24872
24873         this.owner.fireEvent('initialize', this);
24874         this.pushValue();
24875     },
24876
24877     // private
24878     onDestroy : function(){
24879         
24880         
24881         
24882         if(this.rendered){
24883             
24884             //for (var i =0; i < this.toolbars.length;i++) {
24885             //    // fixme - ask toolbars for heights?
24886             //    this.toolbars[i].onDestroy();
24887            // }
24888             
24889             //this.wrap.dom.innerHTML = '';
24890             //this.wrap.remove();
24891         }
24892     },
24893
24894     // private
24895     onFirstFocus : function(){
24896         
24897         this.assignDocWin();
24898         
24899         
24900         this.activated = true;
24901          
24902     
24903         if(Roo.isGecko){ // prevent silly gecko errors
24904             this.win.focus();
24905             var s = this.win.getSelection();
24906             if(!s.focusNode || s.focusNode.nodeType != 3){
24907                 var r = s.getRangeAt(0);
24908                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24909                 r.collapse(true);
24910                 this.deferFocus();
24911             }
24912             try{
24913                 this.execCmd('useCSS', true);
24914                 this.execCmd('styleWithCSS', false);
24915             }catch(e){}
24916         }
24917         this.owner.fireEvent('activate', this);
24918     },
24919
24920     // private
24921     adjustFont: function(btn){
24922         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24923         //if(Roo.isSafari){ // safari
24924         //    adjust *= 2;
24925        // }
24926         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24927         if(Roo.isSafari){ // safari
24928             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24929             v =  (v < 10) ? 10 : v;
24930             v =  (v > 48) ? 48 : v;
24931             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24932             
24933         }
24934         
24935         
24936         v = Math.max(1, v+adjust);
24937         
24938         this.execCmd('FontSize', v  );
24939     },
24940
24941     onEditorEvent : function(e)
24942     {
24943         this.owner.fireEvent('editorevent', this, e);
24944       //  this.updateToolbar();
24945         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24946     },
24947
24948     insertTag : function(tg)
24949     {
24950         // could be a bit smarter... -> wrap the current selected tRoo..
24951         if (tg.toLowerCase() == 'span' ||
24952             tg.toLowerCase() == 'code' ||
24953             tg.toLowerCase() == 'sup' ||
24954             tg.toLowerCase() == 'sub' 
24955             ) {
24956             
24957             range = this.createRange(this.getSelection());
24958             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24959             wrappingNode.appendChild(range.extractContents());
24960             range.insertNode(wrappingNode);
24961
24962             return;
24963             
24964             
24965             
24966         }
24967         this.execCmd("formatblock",   tg);
24968         
24969     },
24970     
24971     insertText : function(txt)
24972     {
24973         
24974         
24975         var range = this.createRange();
24976         range.deleteContents();
24977                //alert(Sender.getAttribute('label'));
24978                
24979         range.insertNode(this.doc.createTextNode(txt));
24980     } ,
24981     
24982      
24983
24984     /**
24985      * Executes a Midas editor command on the editor document and performs necessary focus and
24986      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24987      * @param {String} cmd The Midas command
24988      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24989      */
24990     relayCmd : function(cmd, value){
24991         this.win.focus();
24992         this.execCmd(cmd, value);
24993         this.owner.fireEvent('editorevent', this);
24994         //this.updateToolbar();
24995         this.owner.deferFocus();
24996     },
24997
24998     /**
24999      * Executes a Midas editor command directly on the editor document.
25000      * For visual commands, you should use {@link #relayCmd} instead.
25001      * <b>This should only be called after the editor is initialized.</b>
25002      * @param {String} cmd The Midas command
25003      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25004      */
25005     execCmd : function(cmd, value){
25006         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25007         this.syncValue();
25008     },
25009  
25010  
25011    
25012     /**
25013      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25014      * to insert tRoo.
25015      * @param {String} text | dom node.. 
25016      */
25017     insertAtCursor : function(text)
25018     {
25019         
25020         if(!this.activated){
25021             return;
25022         }
25023         /*
25024         if(Roo.isIE){
25025             this.win.focus();
25026             var r = this.doc.selection.createRange();
25027             if(r){
25028                 r.collapse(true);
25029                 r.pasteHTML(text);
25030                 this.syncValue();
25031                 this.deferFocus();
25032             
25033             }
25034             return;
25035         }
25036         */
25037         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25038             this.win.focus();
25039             
25040             
25041             // from jquery ui (MIT licenced)
25042             var range, node;
25043             var win = this.win;
25044             
25045             if (win.getSelection && win.getSelection().getRangeAt) {
25046                 range = win.getSelection().getRangeAt(0);
25047                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25048                 range.insertNode(node);
25049             } else if (win.document.selection && win.document.selection.createRange) {
25050                 // no firefox support
25051                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25052                 win.document.selection.createRange().pasteHTML(txt);
25053             } else {
25054                 // no firefox support
25055                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25056                 this.execCmd('InsertHTML', txt);
25057             } 
25058             
25059             this.syncValue();
25060             
25061             this.deferFocus();
25062         }
25063     },
25064  // private
25065     mozKeyPress : function(e){
25066         if(e.ctrlKey){
25067             var c = e.getCharCode(), cmd;
25068           
25069             if(c > 0){
25070                 c = String.fromCharCode(c).toLowerCase();
25071                 switch(c){
25072                     case 'b':
25073                         cmd = 'bold';
25074                         break;
25075                     case 'i':
25076                         cmd = 'italic';
25077                         break;
25078                     
25079                     case 'u':
25080                         cmd = 'underline';
25081                         break;
25082                     
25083                     case 'v':
25084                         this.cleanUpPaste.defer(100, this);
25085                         return;
25086                         
25087                 }
25088                 if(cmd){
25089                     this.win.focus();
25090                     this.execCmd(cmd);
25091                     this.deferFocus();
25092                     e.preventDefault();
25093                 }
25094                 
25095             }
25096         }
25097     },
25098
25099     // private
25100     fixKeys : function(){ // load time branching for fastest keydown performance
25101         if(Roo.isIE){
25102             return function(e){
25103                 var k = e.getKey(), r;
25104                 if(k == e.TAB){
25105                     e.stopEvent();
25106                     r = this.doc.selection.createRange();
25107                     if(r){
25108                         r.collapse(true);
25109                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25110                         this.deferFocus();
25111                     }
25112                     return;
25113                 }
25114                 
25115                 if(k == e.ENTER){
25116                     r = this.doc.selection.createRange();
25117                     if(r){
25118                         var target = r.parentElement();
25119                         if(!target || target.tagName.toLowerCase() != 'li'){
25120                             e.stopEvent();
25121                             r.pasteHTML('<br />');
25122                             r.collapse(false);
25123                             r.select();
25124                         }
25125                     }
25126                 }
25127                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25128                     this.cleanUpPaste.defer(100, this);
25129                     return;
25130                 }
25131                 
25132                 
25133             };
25134         }else if(Roo.isOpera){
25135             return function(e){
25136                 var k = e.getKey();
25137                 if(k == e.TAB){
25138                     e.stopEvent();
25139                     this.win.focus();
25140                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25141                     this.deferFocus();
25142                 }
25143                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25144                     this.cleanUpPaste.defer(100, this);
25145                     return;
25146                 }
25147                 
25148             };
25149         }else if(Roo.isSafari){
25150             return function(e){
25151                 var k = e.getKey();
25152                 
25153                 if(k == e.TAB){
25154                     e.stopEvent();
25155                     this.execCmd('InsertText','\t');
25156                     this.deferFocus();
25157                     return;
25158                 }
25159                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25160                     this.cleanUpPaste.defer(100, this);
25161                     return;
25162                 }
25163                 
25164              };
25165         }
25166     }(),
25167     
25168     getAllAncestors: function()
25169     {
25170         var p = this.getSelectedNode();
25171         var a = [];
25172         if (!p) {
25173             a.push(p); // push blank onto stack..
25174             p = this.getParentElement();
25175         }
25176         
25177         
25178         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25179             a.push(p);
25180             p = p.parentNode;
25181         }
25182         a.push(this.doc.body);
25183         return a;
25184     },
25185     lastSel : false,
25186     lastSelNode : false,
25187     
25188     
25189     getSelection : function() 
25190     {
25191         this.assignDocWin();
25192         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25193     },
25194     
25195     getSelectedNode: function() 
25196     {
25197         // this may only work on Gecko!!!
25198         
25199         // should we cache this!!!!
25200         
25201         
25202         
25203          
25204         var range = this.createRange(this.getSelection()).cloneRange();
25205         
25206         if (Roo.isIE) {
25207             var parent = range.parentElement();
25208             while (true) {
25209                 var testRange = range.duplicate();
25210                 testRange.moveToElementText(parent);
25211                 if (testRange.inRange(range)) {
25212                     break;
25213                 }
25214                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25215                     break;
25216                 }
25217                 parent = parent.parentElement;
25218             }
25219             return parent;
25220         }
25221         
25222         // is ancestor a text element.
25223         var ac =  range.commonAncestorContainer;
25224         if (ac.nodeType == 3) {
25225             ac = ac.parentNode;
25226         }
25227         
25228         var ar = ac.childNodes;
25229          
25230         var nodes = [];
25231         var other_nodes = [];
25232         var has_other_nodes = false;
25233         for (var i=0;i<ar.length;i++) {
25234             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25235                 continue;
25236             }
25237             // fullly contained node.
25238             
25239             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25240                 nodes.push(ar[i]);
25241                 continue;
25242             }
25243             
25244             // probably selected..
25245             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25246                 other_nodes.push(ar[i]);
25247                 continue;
25248             }
25249             // outer..
25250             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25251                 continue;
25252             }
25253             
25254             
25255             has_other_nodes = true;
25256         }
25257         if (!nodes.length && other_nodes.length) {
25258             nodes= other_nodes;
25259         }
25260         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25261             return false;
25262         }
25263         
25264         return nodes[0];
25265     },
25266     createRange: function(sel)
25267     {
25268         // this has strange effects when using with 
25269         // top toolbar - not sure if it's a great idea.
25270         //this.editor.contentWindow.focus();
25271         if (typeof sel != "undefined") {
25272             try {
25273                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25274             } catch(e) {
25275                 return this.doc.createRange();
25276             }
25277         } else {
25278             return this.doc.createRange();
25279         }
25280     },
25281     getParentElement: function()
25282     {
25283         
25284         this.assignDocWin();
25285         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25286         
25287         var range = this.createRange(sel);
25288          
25289         try {
25290             var p = range.commonAncestorContainer;
25291             while (p.nodeType == 3) { // text node
25292                 p = p.parentNode;
25293             }
25294             return p;
25295         } catch (e) {
25296             return null;
25297         }
25298     
25299     },
25300     /***
25301      *
25302      * Range intersection.. the hard stuff...
25303      *  '-1' = before
25304      *  '0' = hits..
25305      *  '1' = after.
25306      *         [ -- selected range --- ]
25307      *   [fail]                        [fail]
25308      *
25309      *    basically..
25310      *      if end is before start or  hits it. fail.
25311      *      if start is after end or hits it fail.
25312      *
25313      *   if either hits (but other is outside. - then it's not 
25314      *   
25315      *    
25316      **/
25317     
25318     
25319     // @see http://www.thismuchiknow.co.uk/?p=64.
25320     rangeIntersectsNode : function(range, node)
25321     {
25322         var nodeRange = node.ownerDocument.createRange();
25323         try {
25324             nodeRange.selectNode(node);
25325         } catch (e) {
25326             nodeRange.selectNodeContents(node);
25327         }
25328     
25329         var rangeStartRange = range.cloneRange();
25330         rangeStartRange.collapse(true);
25331     
25332         var rangeEndRange = range.cloneRange();
25333         rangeEndRange.collapse(false);
25334     
25335         var nodeStartRange = nodeRange.cloneRange();
25336         nodeStartRange.collapse(true);
25337     
25338         var nodeEndRange = nodeRange.cloneRange();
25339         nodeEndRange.collapse(false);
25340     
25341         return rangeStartRange.compareBoundaryPoints(
25342                  Range.START_TO_START, nodeEndRange) == -1 &&
25343                rangeEndRange.compareBoundaryPoints(
25344                  Range.START_TO_START, nodeStartRange) == 1;
25345         
25346          
25347     },
25348     rangeCompareNode : function(range, node)
25349     {
25350         var nodeRange = node.ownerDocument.createRange();
25351         try {
25352             nodeRange.selectNode(node);
25353         } catch (e) {
25354             nodeRange.selectNodeContents(node);
25355         }
25356         
25357         
25358         range.collapse(true);
25359     
25360         nodeRange.collapse(true);
25361      
25362         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25363         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25364          
25365         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25366         
25367         var nodeIsBefore   =  ss == 1;
25368         var nodeIsAfter    = ee == -1;
25369         
25370         if (nodeIsBefore && nodeIsAfter) {
25371             return 0; // outer
25372         }
25373         if (!nodeIsBefore && nodeIsAfter) {
25374             return 1; //right trailed.
25375         }
25376         
25377         if (nodeIsBefore && !nodeIsAfter) {
25378             return 2;  // left trailed.
25379         }
25380         // fully contined.
25381         return 3;
25382     },
25383
25384     // private? - in a new class?
25385     cleanUpPaste :  function()
25386     {
25387         // cleans up the whole document..
25388         Roo.log('cleanuppaste');
25389         
25390         this.cleanUpChildren(this.doc.body);
25391         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25392         if (clean != this.doc.body.innerHTML) {
25393             this.doc.body.innerHTML = clean;
25394         }
25395         
25396     },
25397     
25398     cleanWordChars : function(input) {// change the chars to hex code
25399         var he = Roo.HtmlEditorCore;
25400         
25401         var output = input;
25402         Roo.each(he.swapCodes, function(sw) { 
25403             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25404             
25405             output = output.replace(swapper, sw[1]);
25406         });
25407         
25408         return output;
25409     },
25410     
25411     
25412     cleanUpChildren : function (n)
25413     {
25414         if (!n.childNodes.length) {
25415             return;
25416         }
25417         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25418            this.cleanUpChild(n.childNodes[i]);
25419         }
25420     },
25421     
25422     
25423         
25424     
25425     cleanUpChild : function (node)
25426     {
25427         var ed = this;
25428         //console.log(node);
25429         if (node.nodeName == "#text") {
25430             // clean up silly Windows -- stuff?
25431             return; 
25432         }
25433         if (node.nodeName == "#comment") {
25434             node.parentNode.removeChild(node);
25435             // clean up silly Windows -- stuff?
25436             return; 
25437         }
25438         var lcname = node.tagName.toLowerCase();
25439         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25440         // whitelist of tags..
25441         
25442         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25443             // remove node.
25444             node.parentNode.removeChild(node);
25445             return;
25446             
25447         }
25448         
25449         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25450         
25451         // spans with no attributes - just remove them..
25452         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25453             remove_keep_children = true;
25454         }
25455         
25456         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25457         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25458         
25459         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25460         //    remove_keep_children = true;
25461         //}
25462         
25463         if (remove_keep_children) {
25464             this.cleanUpChildren(node);
25465             // inserts everything just before this node...
25466             while (node.childNodes.length) {
25467                 var cn = node.childNodes[0];
25468                 node.removeChild(cn);
25469                 node.parentNode.insertBefore(cn, node);
25470             }
25471             node.parentNode.removeChild(node);
25472             return;
25473         }
25474         
25475         if (!node.attributes || !node.attributes.length) {
25476             
25477           
25478             
25479             
25480             this.cleanUpChildren(node);
25481             return;
25482         }
25483         
25484         function cleanAttr(n,v)
25485         {
25486             
25487             if (v.match(/^\./) || v.match(/^\//)) {
25488                 return;
25489             }
25490             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25491                 return;
25492             }
25493             if (v.match(/^#/)) {
25494                 return;
25495             }
25496             if (v.match(/^\{/)) { // allow template editing.
25497                 return;
25498             }
25499 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25500             node.removeAttribute(n);
25501             
25502         }
25503         
25504         var cwhite = this.cwhite;
25505         var cblack = this.cblack;
25506             
25507         function cleanStyle(n,v)
25508         {
25509             if (v.match(/expression/)) { //XSS?? should we even bother..
25510                 node.removeAttribute(n);
25511                 return;
25512             }
25513             
25514             var parts = v.split(/;/);
25515             var clean = [];
25516             
25517             Roo.each(parts, function(p) {
25518                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25519                 if (!p.length) {
25520                     return true;
25521                 }
25522                 var l = p.split(':').shift().replace(/\s+/g,'');
25523                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25524                 
25525                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25526 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25527                     //node.removeAttribute(n);
25528                     return true;
25529                 }
25530                 //Roo.log()
25531                 // only allow 'c whitelisted system attributes'
25532                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25533 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25534                     //node.removeAttribute(n);
25535                     return true;
25536                 }
25537                 
25538                 
25539                  
25540                 
25541                 clean.push(p);
25542                 return true;
25543             });
25544             if (clean.length) { 
25545                 node.setAttribute(n, clean.join(';'));
25546             } else {
25547                 node.removeAttribute(n);
25548             }
25549             
25550         }
25551         
25552         
25553         for (var i = node.attributes.length-1; i > -1 ; i--) {
25554             var a = node.attributes[i];
25555             //console.log(a);
25556             
25557             if (a.name.toLowerCase().substr(0,2)=='on')  {
25558                 node.removeAttribute(a.name);
25559                 continue;
25560             }
25561             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25562                 node.removeAttribute(a.name);
25563                 continue;
25564             }
25565             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25566                 cleanAttr(a.name,a.value); // fixme..
25567                 continue;
25568             }
25569             if (a.name == 'style') {
25570                 cleanStyle(a.name,a.value);
25571                 continue;
25572             }
25573             /// clean up MS crap..
25574             // tecnically this should be a list of valid class'es..
25575             
25576             
25577             if (a.name == 'class') {
25578                 if (a.value.match(/^Mso/)) {
25579                     node.removeAttribute('class');
25580                 }
25581                 
25582                 if (a.value.match(/^body$/)) {
25583                     node.removeAttribute('class');
25584                 }
25585                 continue;
25586             }
25587             
25588             // style cleanup!?
25589             // class cleanup?
25590             
25591         }
25592         
25593         
25594         this.cleanUpChildren(node);
25595         
25596         
25597     },
25598     
25599     /**
25600      * Clean up MS wordisms...
25601      */
25602     cleanWord : function(node)
25603     {
25604         if (!node) {
25605             this.cleanWord(this.doc.body);
25606             return;
25607         }
25608         
25609         if(
25610                 node.nodeName == 'SPAN' &&
25611                 !node.hasAttributes() &&
25612                 node.childNodes.length == 1 &&
25613                 node.firstChild.nodeName == "#text"  
25614         ) {
25615             var textNode = node.firstChild;
25616             node.removeChild(textNode);
25617             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25618                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25619             }
25620             node.parentNode.insertBefore(textNode, node);
25621             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25622                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25623             }
25624             node.parentNode.removeChild(node);
25625         }
25626         
25627         if (node.nodeName == "#text") {
25628             // clean up silly Windows -- stuff?
25629             return; 
25630         }
25631         if (node.nodeName == "#comment") {
25632             node.parentNode.removeChild(node);
25633             // clean up silly Windows -- stuff?
25634             return; 
25635         }
25636         
25637         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25638             node.parentNode.removeChild(node);
25639             return;
25640         }
25641         //Roo.log(node.tagName);
25642         // remove - but keep children..
25643         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25644             //Roo.log('-- removed');
25645             while (node.childNodes.length) {
25646                 var cn = node.childNodes[0];
25647                 node.removeChild(cn);
25648                 node.parentNode.insertBefore(cn, node);
25649                 // move node to parent - and clean it..
25650                 this.cleanWord(cn);
25651             }
25652             node.parentNode.removeChild(node);
25653             /// no need to iterate chidlren = it's got none..
25654             //this.iterateChildren(node, this.cleanWord);
25655             return;
25656         }
25657         // clean styles
25658         if (node.className.length) {
25659             
25660             var cn = node.className.split(/\W+/);
25661             var cna = [];
25662             Roo.each(cn, function(cls) {
25663                 if (cls.match(/Mso[a-zA-Z]+/)) {
25664                     return;
25665                 }
25666                 cna.push(cls);
25667             });
25668             node.className = cna.length ? cna.join(' ') : '';
25669             if (!cna.length) {
25670                 node.removeAttribute("class");
25671             }
25672         }
25673         
25674         if (node.hasAttribute("lang")) {
25675             node.removeAttribute("lang");
25676         }
25677         
25678         if (node.hasAttribute("style")) {
25679             
25680             var styles = node.getAttribute("style").split(";");
25681             var nstyle = [];
25682             Roo.each(styles, function(s) {
25683                 if (!s.match(/:/)) {
25684                     return;
25685                 }
25686                 var kv = s.split(":");
25687                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25688                     return;
25689                 }
25690                 // what ever is left... we allow.
25691                 nstyle.push(s);
25692             });
25693             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25694             if (!nstyle.length) {
25695                 node.removeAttribute('style');
25696             }
25697         }
25698         this.iterateChildren(node, this.cleanWord);
25699         
25700         
25701         
25702     },
25703     /**
25704      * iterateChildren of a Node, calling fn each time, using this as the scole..
25705      * @param {DomNode} node node to iterate children of.
25706      * @param {Function} fn method of this class to call on each item.
25707      */
25708     iterateChildren : function(node, fn)
25709     {
25710         if (!node.childNodes.length) {
25711                 return;
25712         }
25713         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25714            fn.call(this, node.childNodes[i])
25715         }
25716     },
25717     
25718     
25719     /**
25720      * cleanTableWidths.
25721      *
25722      * Quite often pasting from word etc.. results in tables with column and widths.
25723      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25724      *
25725      */
25726     cleanTableWidths : function(node)
25727     {
25728          
25729          
25730         if (!node) {
25731             this.cleanTableWidths(this.doc.body);
25732             return;
25733         }
25734         
25735         // ignore list...
25736         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25737             return; 
25738         }
25739         Roo.log(node.tagName);
25740         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25741             this.iterateChildren(node, this.cleanTableWidths);
25742             return;
25743         }
25744         if (node.hasAttribute('width')) {
25745             node.removeAttribute('width');
25746         }
25747         
25748          
25749         if (node.hasAttribute("style")) {
25750             // pretty basic...
25751             
25752             var styles = node.getAttribute("style").split(";");
25753             var nstyle = [];
25754             Roo.each(styles, function(s) {
25755                 if (!s.match(/:/)) {
25756                     return;
25757                 }
25758                 var kv = s.split(":");
25759                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25760                     return;
25761                 }
25762                 // what ever is left... we allow.
25763                 nstyle.push(s);
25764             });
25765             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25766             if (!nstyle.length) {
25767                 node.removeAttribute('style');
25768             }
25769         }
25770         
25771         this.iterateChildren(node, this.cleanTableWidths);
25772         
25773         
25774     },
25775     
25776     
25777     
25778     
25779     domToHTML : function(currentElement, depth, nopadtext) {
25780         
25781         depth = depth || 0;
25782         nopadtext = nopadtext || false;
25783     
25784         if (!currentElement) {
25785             return this.domToHTML(this.doc.body);
25786         }
25787         
25788         //Roo.log(currentElement);
25789         var j;
25790         var allText = false;
25791         var nodeName = currentElement.nodeName;
25792         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25793         
25794         if  (nodeName == '#text') {
25795             
25796             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25797         }
25798         
25799         
25800         var ret = '';
25801         if (nodeName != 'BODY') {
25802              
25803             var i = 0;
25804             // Prints the node tagName, such as <A>, <IMG>, etc
25805             if (tagName) {
25806                 var attr = [];
25807                 for(i = 0; i < currentElement.attributes.length;i++) {
25808                     // quoting?
25809                     var aname = currentElement.attributes.item(i).name;
25810                     if (!currentElement.attributes.item(i).value.length) {
25811                         continue;
25812                     }
25813                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25814                 }
25815                 
25816                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25817             } 
25818             else {
25819                 
25820                 // eack
25821             }
25822         } else {
25823             tagName = false;
25824         }
25825         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25826             return ret;
25827         }
25828         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25829             nopadtext = true;
25830         }
25831         
25832         
25833         // Traverse the tree
25834         i = 0;
25835         var currentElementChild = currentElement.childNodes.item(i);
25836         var allText = true;
25837         var innerHTML  = '';
25838         lastnode = '';
25839         while (currentElementChild) {
25840             // Formatting code (indent the tree so it looks nice on the screen)
25841             var nopad = nopadtext;
25842             if (lastnode == 'SPAN') {
25843                 nopad  = true;
25844             }
25845             // text
25846             if  (currentElementChild.nodeName == '#text') {
25847                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25848                 toadd = nopadtext ? toadd : toadd.trim();
25849                 if (!nopad && toadd.length > 80) {
25850                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25851                 }
25852                 innerHTML  += toadd;
25853                 
25854                 i++;
25855                 currentElementChild = currentElement.childNodes.item(i);
25856                 lastNode = '';
25857                 continue;
25858             }
25859             allText = false;
25860             
25861             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25862                 
25863             // Recursively traverse the tree structure of the child node
25864             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25865             lastnode = currentElementChild.nodeName;
25866             i++;
25867             currentElementChild=currentElement.childNodes.item(i);
25868         }
25869         
25870         ret += innerHTML;
25871         
25872         if (!allText) {
25873                 // The remaining code is mostly for formatting the tree
25874             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25875         }
25876         
25877         
25878         if (tagName) {
25879             ret+= "</"+tagName+">";
25880         }
25881         return ret;
25882         
25883     },
25884         
25885     applyBlacklists : function()
25886     {
25887         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25888         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25889         
25890         this.white = [];
25891         this.black = [];
25892         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25893             if (b.indexOf(tag) > -1) {
25894                 return;
25895             }
25896             this.white.push(tag);
25897             
25898         }, this);
25899         
25900         Roo.each(w, function(tag) {
25901             if (b.indexOf(tag) > -1) {
25902                 return;
25903             }
25904             if (this.white.indexOf(tag) > -1) {
25905                 return;
25906             }
25907             this.white.push(tag);
25908             
25909         }, this);
25910         
25911         
25912         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25913             if (w.indexOf(tag) > -1) {
25914                 return;
25915             }
25916             this.black.push(tag);
25917             
25918         }, this);
25919         
25920         Roo.each(b, function(tag) {
25921             if (w.indexOf(tag) > -1) {
25922                 return;
25923             }
25924             if (this.black.indexOf(tag) > -1) {
25925                 return;
25926             }
25927             this.black.push(tag);
25928             
25929         }, this);
25930         
25931         
25932         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25933         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25934         
25935         this.cwhite = [];
25936         this.cblack = [];
25937         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25938             if (b.indexOf(tag) > -1) {
25939                 return;
25940             }
25941             this.cwhite.push(tag);
25942             
25943         }, this);
25944         
25945         Roo.each(w, function(tag) {
25946             if (b.indexOf(tag) > -1) {
25947                 return;
25948             }
25949             if (this.cwhite.indexOf(tag) > -1) {
25950                 return;
25951             }
25952             this.cwhite.push(tag);
25953             
25954         }, this);
25955         
25956         
25957         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25958             if (w.indexOf(tag) > -1) {
25959                 return;
25960             }
25961             this.cblack.push(tag);
25962             
25963         }, this);
25964         
25965         Roo.each(b, function(tag) {
25966             if (w.indexOf(tag) > -1) {
25967                 return;
25968             }
25969             if (this.cblack.indexOf(tag) > -1) {
25970                 return;
25971             }
25972             this.cblack.push(tag);
25973             
25974         }, this);
25975     },
25976     
25977     setStylesheets : function(stylesheets)
25978     {
25979         if(typeof(stylesheets) == 'string'){
25980             Roo.get(this.iframe.contentDocument.head).createChild({
25981                 tag : 'link',
25982                 rel : 'stylesheet',
25983                 type : 'text/css',
25984                 href : stylesheets
25985             });
25986             
25987             return;
25988         }
25989         var _this = this;
25990      
25991         Roo.each(stylesheets, function(s) {
25992             if(!s.length){
25993                 return;
25994             }
25995             
25996             Roo.get(_this.iframe.contentDocument.head).createChild({
25997                 tag : 'link',
25998                 rel : 'stylesheet',
25999                 type : 'text/css',
26000                 href : s
26001             });
26002         });
26003
26004         
26005     },
26006     
26007     removeStylesheets : function()
26008     {
26009         var _this = this;
26010         
26011         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26012             s.remove();
26013         });
26014     },
26015     
26016     setStyle : function(style)
26017     {
26018         Roo.get(this.iframe.contentDocument.head).createChild({
26019             tag : 'style',
26020             type : 'text/css',
26021             html : style
26022         });
26023
26024         return;
26025     }
26026     
26027     // hide stuff that is not compatible
26028     /**
26029      * @event blur
26030      * @hide
26031      */
26032     /**
26033      * @event change
26034      * @hide
26035      */
26036     /**
26037      * @event focus
26038      * @hide
26039      */
26040     /**
26041      * @event specialkey
26042      * @hide
26043      */
26044     /**
26045      * @cfg {String} fieldClass @hide
26046      */
26047     /**
26048      * @cfg {String} focusClass @hide
26049      */
26050     /**
26051      * @cfg {String} autoCreate @hide
26052      */
26053     /**
26054      * @cfg {String} inputType @hide
26055      */
26056     /**
26057      * @cfg {String} invalidClass @hide
26058      */
26059     /**
26060      * @cfg {String} invalidText @hide
26061      */
26062     /**
26063      * @cfg {String} msgFx @hide
26064      */
26065     /**
26066      * @cfg {String} validateOnBlur @hide
26067      */
26068 });
26069
26070 Roo.HtmlEditorCore.white = [
26071         'area', 'br', 'img', 'input', 'hr', 'wbr',
26072         
26073        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26074        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26075        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26076        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26077        'table',   'ul',         'xmp', 
26078        
26079        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26080       'thead',   'tr', 
26081      
26082       'dir', 'menu', 'ol', 'ul', 'dl',
26083        
26084       'embed',  'object'
26085 ];
26086
26087
26088 Roo.HtmlEditorCore.black = [
26089     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26090         'applet', // 
26091         'base',   'basefont', 'bgsound', 'blink',  'body', 
26092         'frame',  'frameset', 'head',    'html',   'ilayer', 
26093         'iframe', 'layer',  'link',     'meta',    'object',   
26094         'script', 'style' ,'title',  'xml' // clean later..
26095 ];
26096 Roo.HtmlEditorCore.clean = [
26097     'script', 'style', 'title', 'xml'
26098 ];
26099 Roo.HtmlEditorCore.remove = [
26100     'font'
26101 ];
26102 // attributes..
26103
26104 Roo.HtmlEditorCore.ablack = [
26105     'on'
26106 ];
26107     
26108 Roo.HtmlEditorCore.aclean = [ 
26109     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26110 ];
26111
26112 // protocols..
26113 Roo.HtmlEditorCore.pwhite= [
26114         'http',  'https',  'mailto'
26115 ];
26116
26117 // white listed style attributes.
26118 Roo.HtmlEditorCore.cwhite= [
26119       //  'text-align', /// default is to allow most things..
26120       
26121          
26122 //        'font-size'//??
26123 ];
26124
26125 // black listed style attributes.
26126 Roo.HtmlEditorCore.cblack= [
26127       //  'font-size' -- this can be set by the project 
26128 ];
26129
26130
26131 Roo.HtmlEditorCore.swapCodes   =[ 
26132     [    8211, "&#8211;" ], 
26133     [    8212, "&#8212;" ], 
26134     [    8216,  "'" ],  
26135     [    8217, "'" ],  
26136     [    8220, '"' ],  
26137     [    8221, '"' ],  
26138     [    8226, "*" ],  
26139     [    8230, "..." ]
26140 ]; 
26141
26142     /*
26143  * - LGPL
26144  *
26145  * HtmlEditor
26146  * 
26147  */
26148
26149 /**
26150  * @class Roo.bootstrap.HtmlEditor
26151  * @extends Roo.bootstrap.TextArea
26152  * Bootstrap HtmlEditor class
26153
26154  * @constructor
26155  * Create a new HtmlEditor
26156  * @param {Object} config The config object
26157  */
26158
26159 Roo.bootstrap.HtmlEditor = function(config){
26160     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26161     if (!this.toolbars) {
26162         this.toolbars = [];
26163     }
26164     
26165     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26166     this.addEvents({
26167             /**
26168              * @event initialize
26169              * Fires when the editor is fully initialized (including the iframe)
26170              * @param {HtmlEditor} this
26171              */
26172             initialize: true,
26173             /**
26174              * @event activate
26175              * Fires when the editor is first receives the focus. Any insertion must wait
26176              * until after this event.
26177              * @param {HtmlEditor} this
26178              */
26179             activate: true,
26180              /**
26181              * @event beforesync
26182              * Fires before the textarea is updated with content from the editor iframe. Return false
26183              * to cancel the sync.
26184              * @param {HtmlEditor} this
26185              * @param {String} html
26186              */
26187             beforesync: true,
26188              /**
26189              * @event beforepush
26190              * Fires before the iframe editor is updated with content from the textarea. Return false
26191              * to cancel the push.
26192              * @param {HtmlEditor} this
26193              * @param {String} html
26194              */
26195             beforepush: true,
26196              /**
26197              * @event sync
26198              * Fires when the textarea is updated with content from the editor iframe.
26199              * @param {HtmlEditor} this
26200              * @param {String} html
26201              */
26202             sync: true,
26203              /**
26204              * @event push
26205              * Fires when the iframe editor is updated with content from the textarea.
26206              * @param {HtmlEditor} this
26207              * @param {String} html
26208              */
26209             push: true,
26210              /**
26211              * @event editmodechange
26212              * Fires when the editor switches edit modes
26213              * @param {HtmlEditor} this
26214              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26215              */
26216             editmodechange: true,
26217             /**
26218              * @event editorevent
26219              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26220              * @param {HtmlEditor} this
26221              */
26222             editorevent: true,
26223             /**
26224              * @event firstfocus
26225              * Fires when on first focus - needed by toolbars..
26226              * @param {HtmlEditor} this
26227              */
26228             firstfocus: true,
26229             /**
26230              * @event autosave
26231              * Auto save the htmlEditor value as a file into Events
26232              * @param {HtmlEditor} this
26233              */
26234             autosave: true,
26235             /**
26236              * @event savedpreview
26237              * preview the saved version of htmlEditor
26238              * @param {HtmlEditor} this
26239              */
26240             savedpreview: true
26241         });
26242 };
26243
26244
26245 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26246     
26247     
26248       /**
26249      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26250      */
26251     toolbars : false,
26252     
26253      /**
26254     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26255     */
26256     btns : [],
26257    
26258      /**
26259      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26260      *                        Roo.resizable.
26261      */
26262     resizable : false,
26263      /**
26264      * @cfg {Number} height (in pixels)
26265      */   
26266     height: 300,
26267    /**
26268      * @cfg {Number} width (in pixels)
26269      */   
26270     width: false,
26271     
26272     /**
26273      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26274      * 
26275      */
26276     stylesheets: false,
26277     
26278     // id of frame..
26279     frameId: false,
26280     
26281     // private properties
26282     validationEvent : false,
26283     deferHeight: true,
26284     initialized : false,
26285     activated : false,
26286     
26287     onFocus : Roo.emptyFn,
26288     iframePad:3,
26289     hideMode:'offsets',
26290     
26291     tbContainer : false,
26292     
26293     bodyCls : '',
26294     
26295     toolbarContainer :function() {
26296         return this.wrap.select('.x-html-editor-tb',true).first();
26297     },
26298
26299     /**
26300      * Protected method that will not generally be called directly. It
26301      * is called when the editor creates its toolbar. Override this method if you need to
26302      * add custom toolbar buttons.
26303      * @param {HtmlEditor} editor
26304      */
26305     createToolbar : function(){
26306         Roo.log('renewing');
26307         Roo.log("create toolbars");
26308         
26309         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26310         this.toolbars[0].render(this.toolbarContainer());
26311         
26312         return;
26313         
26314 //        if (!editor.toolbars || !editor.toolbars.length) {
26315 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26316 //        }
26317 //        
26318 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26319 //            editor.toolbars[i] = Roo.factory(
26320 //                    typeof(editor.toolbars[i]) == 'string' ?
26321 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26322 //                Roo.bootstrap.HtmlEditor);
26323 //            editor.toolbars[i].init(editor);
26324 //        }
26325     },
26326
26327      
26328     // private
26329     onRender : function(ct, position)
26330     {
26331        // Roo.log("Call onRender: " + this.xtype);
26332         var _t = this;
26333         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26334       
26335         this.wrap = this.inputEl().wrap({
26336             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26337         });
26338         
26339         this.editorcore.onRender(ct, position);
26340          
26341         if (this.resizable) {
26342             this.resizeEl = new Roo.Resizable(this.wrap, {
26343                 pinned : true,
26344                 wrap: true,
26345                 dynamic : true,
26346                 minHeight : this.height,
26347                 height: this.height,
26348                 handles : this.resizable,
26349                 width: this.width,
26350                 listeners : {
26351                     resize : function(r, w, h) {
26352                         _t.onResize(w,h); // -something
26353                     }
26354                 }
26355             });
26356             
26357         }
26358         this.createToolbar(this);
26359        
26360         
26361         if(!this.width && this.resizable){
26362             this.setSize(this.wrap.getSize());
26363         }
26364         if (this.resizeEl) {
26365             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26366             // should trigger onReize..
26367         }
26368         
26369     },
26370
26371     // private
26372     onResize : function(w, h)
26373     {
26374         Roo.log('resize: ' +w + ',' + h );
26375         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26376         var ew = false;
26377         var eh = false;
26378         
26379         if(this.inputEl() ){
26380             if(typeof w == 'number'){
26381                 var aw = w - this.wrap.getFrameWidth('lr');
26382                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26383                 ew = aw;
26384             }
26385             if(typeof h == 'number'){
26386                  var tbh = -11;  // fixme it needs to tool bar size!
26387                 for (var i =0; i < this.toolbars.length;i++) {
26388                     // fixme - ask toolbars for heights?
26389                     tbh += this.toolbars[i].el.getHeight();
26390                     //if (this.toolbars[i].footer) {
26391                     //    tbh += this.toolbars[i].footer.el.getHeight();
26392                     //}
26393                 }
26394               
26395                 
26396                 
26397                 
26398                 
26399                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26400                 ah -= 5; // knock a few pixes off for look..
26401                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26402                 var eh = ah;
26403             }
26404         }
26405         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26406         this.editorcore.onResize(ew,eh);
26407         
26408     },
26409
26410     /**
26411      * Toggles the editor between standard and source edit mode.
26412      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26413      */
26414     toggleSourceEdit : function(sourceEditMode)
26415     {
26416         this.editorcore.toggleSourceEdit(sourceEditMode);
26417         
26418         if(this.editorcore.sourceEditMode){
26419             Roo.log('editor - showing textarea');
26420             
26421 //            Roo.log('in');
26422 //            Roo.log(this.syncValue());
26423             this.syncValue();
26424             this.inputEl().removeClass(['hide', 'x-hidden']);
26425             this.inputEl().dom.removeAttribute('tabIndex');
26426             this.inputEl().focus();
26427         }else{
26428             Roo.log('editor - hiding textarea');
26429 //            Roo.log('out')
26430 //            Roo.log(this.pushValue()); 
26431             this.pushValue();
26432             
26433             this.inputEl().addClass(['hide', 'x-hidden']);
26434             this.inputEl().dom.setAttribute('tabIndex', -1);
26435             //this.deferFocus();
26436         }
26437          
26438         if(this.resizable){
26439             this.setSize(this.wrap.getSize());
26440         }
26441         
26442         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26443     },
26444  
26445     // private (for BoxComponent)
26446     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26447
26448     // private (for BoxComponent)
26449     getResizeEl : function(){
26450         return this.wrap;
26451     },
26452
26453     // private (for BoxComponent)
26454     getPositionEl : function(){
26455         return this.wrap;
26456     },
26457
26458     // private
26459     initEvents : function(){
26460         this.originalValue = this.getValue();
26461     },
26462
26463 //    /**
26464 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26465 //     * @method
26466 //     */
26467 //    markInvalid : Roo.emptyFn,
26468 //    /**
26469 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26470 //     * @method
26471 //     */
26472 //    clearInvalid : Roo.emptyFn,
26473
26474     setValue : function(v){
26475         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26476         this.editorcore.pushValue();
26477     },
26478
26479      
26480     // private
26481     deferFocus : function(){
26482         this.focus.defer(10, this);
26483     },
26484
26485     // doc'ed in Field
26486     focus : function(){
26487         this.editorcore.focus();
26488         
26489     },
26490       
26491
26492     // private
26493     onDestroy : function(){
26494         
26495         
26496         
26497         if(this.rendered){
26498             
26499             for (var i =0; i < this.toolbars.length;i++) {
26500                 // fixme - ask toolbars for heights?
26501                 this.toolbars[i].onDestroy();
26502             }
26503             
26504             this.wrap.dom.innerHTML = '';
26505             this.wrap.remove();
26506         }
26507     },
26508
26509     // private
26510     onFirstFocus : function(){
26511         //Roo.log("onFirstFocus");
26512         this.editorcore.onFirstFocus();
26513          for (var i =0; i < this.toolbars.length;i++) {
26514             this.toolbars[i].onFirstFocus();
26515         }
26516         
26517     },
26518     
26519     // private
26520     syncValue : function()
26521     {   
26522         this.editorcore.syncValue();
26523     },
26524     
26525     pushValue : function()
26526     {   
26527         this.editorcore.pushValue();
26528     }
26529      
26530     
26531     // hide stuff that is not compatible
26532     /**
26533      * @event blur
26534      * @hide
26535      */
26536     /**
26537      * @event change
26538      * @hide
26539      */
26540     /**
26541      * @event focus
26542      * @hide
26543      */
26544     /**
26545      * @event specialkey
26546      * @hide
26547      */
26548     /**
26549      * @cfg {String} fieldClass @hide
26550      */
26551     /**
26552      * @cfg {String} focusClass @hide
26553      */
26554     /**
26555      * @cfg {String} autoCreate @hide
26556      */
26557     /**
26558      * @cfg {String} inputType @hide
26559      */
26560      
26561     /**
26562      * @cfg {String} invalidText @hide
26563      */
26564     /**
26565      * @cfg {String} msgFx @hide
26566      */
26567     /**
26568      * @cfg {String} validateOnBlur @hide
26569      */
26570 });
26571  
26572     
26573    
26574    
26575    
26576       
26577 Roo.namespace('Roo.bootstrap.htmleditor');
26578 /**
26579  * @class Roo.bootstrap.HtmlEditorToolbar1
26580  * Basic Toolbar
26581  * 
26582  * @example
26583  * Usage:
26584  *
26585  new Roo.bootstrap.HtmlEditor({
26586     ....
26587     toolbars : [
26588         new Roo.bootstrap.HtmlEditorToolbar1({
26589             disable : { fonts: 1 , format: 1, ..., ... , ...],
26590             btns : [ .... ]
26591         })
26592     }
26593      
26594  * 
26595  * @cfg {Object} disable List of elements to disable..
26596  * @cfg {Array} btns List of additional buttons.
26597  * 
26598  * 
26599  * NEEDS Extra CSS? 
26600  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26601  */
26602  
26603 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26604 {
26605     
26606     Roo.apply(this, config);
26607     
26608     // default disabled, based on 'good practice'..
26609     this.disable = this.disable || {};
26610     Roo.applyIf(this.disable, {
26611         fontSize : true,
26612         colors : true,
26613         specialElements : true
26614     });
26615     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26616     
26617     this.editor = config.editor;
26618     this.editorcore = config.editor.editorcore;
26619     
26620     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26621     
26622     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26623     // dont call parent... till later.
26624 }
26625 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26626      
26627     bar : true,
26628     
26629     editor : false,
26630     editorcore : false,
26631     
26632     
26633     formats : [
26634         "p" ,  
26635         "h1","h2","h3","h4","h5","h6", 
26636         "pre", "code", 
26637         "abbr", "acronym", "address", "cite", "samp", "var",
26638         'div','span'
26639     ],
26640     
26641     onRender : function(ct, position)
26642     {
26643        // Roo.log("Call onRender: " + this.xtype);
26644         
26645        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26646        Roo.log(this.el);
26647        this.el.dom.style.marginBottom = '0';
26648        var _this = this;
26649        var editorcore = this.editorcore;
26650        var editor= this.editor;
26651        
26652        var children = [];
26653        var btn = function(id,cmd , toggle, handler, html){
26654        
26655             var  event = toggle ? 'toggle' : 'click';
26656        
26657             var a = {
26658                 size : 'sm',
26659                 xtype: 'Button',
26660                 xns: Roo.bootstrap,
26661                 //glyphicon : id,
26662                 fa: id,
26663                 cmd : id || cmd,
26664                 enableToggle:toggle !== false,
26665                 html : html || '',
26666                 pressed : toggle ? false : null,
26667                 listeners : {}
26668             };
26669             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26670                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26671             };
26672             children.push(a);
26673             return a;
26674        }
26675        
26676     //    var cb_box = function...
26677         
26678         var style = {
26679                 xtype: 'Button',
26680                 size : 'sm',
26681                 xns: Roo.bootstrap,
26682                 fa : 'font',
26683                 //html : 'submit'
26684                 menu : {
26685                     xtype: 'Menu',
26686                     xns: Roo.bootstrap,
26687                     items:  []
26688                 }
26689         };
26690         Roo.each(this.formats, function(f) {
26691             style.menu.items.push({
26692                 xtype :'MenuItem',
26693                 xns: Roo.bootstrap,
26694                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26695                 tagname : f,
26696                 listeners : {
26697                     click : function()
26698                     {
26699                         editorcore.insertTag(this.tagname);
26700                         editor.focus();
26701                     }
26702                 }
26703                 
26704             });
26705         });
26706         children.push(style);   
26707         
26708         btn('bold',false,true);
26709         btn('italic',false,true);
26710         btn('align-left', 'justifyleft',true);
26711         btn('align-center', 'justifycenter',true);
26712         btn('align-right' , 'justifyright',true);
26713         btn('link', false, false, function(btn) {
26714             //Roo.log("create link?");
26715             var url = prompt(this.createLinkText, this.defaultLinkValue);
26716             if(url && url != 'http:/'+'/'){
26717                 this.editorcore.relayCmd('createlink', url);
26718             }
26719         }),
26720         btn('list','insertunorderedlist',true);
26721         btn('pencil', false,true, function(btn){
26722                 Roo.log(this);
26723                 this.toggleSourceEdit(btn.pressed);
26724         });
26725         
26726         if (this.editor.btns.length > 0) {
26727             for (var i = 0; i<this.editor.btns.length; i++) {
26728                 children.push(this.editor.btns[i]);
26729             }
26730         }
26731         
26732         /*
26733         var cog = {
26734                 xtype: 'Button',
26735                 size : 'sm',
26736                 xns: Roo.bootstrap,
26737                 glyphicon : 'cog',
26738                 //html : 'submit'
26739                 menu : {
26740                     xtype: 'Menu',
26741                     xns: Roo.bootstrap,
26742                     items:  []
26743                 }
26744         };
26745         
26746         cog.menu.items.push({
26747             xtype :'MenuItem',
26748             xns: Roo.bootstrap,
26749             html : Clean styles,
26750             tagname : f,
26751             listeners : {
26752                 click : function()
26753                 {
26754                     editorcore.insertTag(this.tagname);
26755                     editor.focus();
26756                 }
26757             }
26758             
26759         });
26760        */
26761         
26762          
26763        this.xtype = 'NavSimplebar';
26764         
26765         for(var i=0;i< children.length;i++) {
26766             
26767             this.buttons.add(this.addxtypeChild(children[i]));
26768             
26769         }
26770         
26771         editor.on('editorevent', this.updateToolbar, this);
26772     },
26773     onBtnClick : function(id)
26774     {
26775        this.editorcore.relayCmd(id);
26776        this.editorcore.focus();
26777     },
26778     
26779     /**
26780      * Protected method that will not generally be called directly. It triggers
26781      * a toolbar update by reading the markup state of the current selection in the editor.
26782      */
26783     updateToolbar: function(){
26784
26785         if(!this.editorcore.activated){
26786             this.editor.onFirstFocus(); // is this neeed?
26787             return;
26788         }
26789
26790         var btns = this.buttons; 
26791         var doc = this.editorcore.doc;
26792         btns.get('bold').setActive(doc.queryCommandState('bold'));
26793         btns.get('italic').setActive(doc.queryCommandState('italic'));
26794         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26795         
26796         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26797         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26798         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26799         
26800         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26801         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26802          /*
26803         
26804         var ans = this.editorcore.getAllAncestors();
26805         if (this.formatCombo) {
26806             
26807             
26808             var store = this.formatCombo.store;
26809             this.formatCombo.setValue("");
26810             for (var i =0; i < ans.length;i++) {
26811                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26812                     // select it..
26813                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26814                     break;
26815                 }
26816             }
26817         }
26818         
26819         
26820         
26821         // hides menus... - so this cant be on a menu...
26822         Roo.bootstrap.MenuMgr.hideAll();
26823         */
26824         Roo.bootstrap.MenuMgr.hideAll();
26825         //this.editorsyncValue();
26826     },
26827     onFirstFocus: function() {
26828         this.buttons.each(function(item){
26829            item.enable();
26830         });
26831     },
26832     toggleSourceEdit : function(sourceEditMode){
26833         
26834           
26835         if(sourceEditMode){
26836             Roo.log("disabling buttons");
26837            this.buttons.each( function(item){
26838                 if(item.cmd != 'pencil'){
26839                     item.disable();
26840                 }
26841             });
26842           
26843         }else{
26844             Roo.log("enabling buttons");
26845             if(this.editorcore.initialized){
26846                 this.buttons.each( function(item){
26847                     item.enable();
26848                 });
26849             }
26850             
26851         }
26852         Roo.log("calling toggole on editor");
26853         // tell the editor that it's been pressed..
26854         this.editor.toggleSourceEdit(sourceEditMode);
26855        
26856     }
26857 });
26858
26859
26860
26861
26862  
26863 /*
26864  * - LGPL
26865  */
26866
26867 /**
26868  * @class Roo.bootstrap.Markdown
26869  * @extends Roo.bootstrap.TextArea
26870  * Bootstrap Showdown editable area
26871  * @cfg {string} content
26872  * 
26873  * @constructor
26874  * Create a new Showdown
26875  */
26876
26877 Roo.bootstrap.Markdown = function(config){
26878     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26879    
26880 };
26881
26882 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26883     
26884     editing :false,
26885     
26886     initEvents : function()
26887     {
26888         
26889         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26890         this.markdownEl = this.el.createChild({
26891             cls : 'roo-markdown-area'
26892         });
26893         this.inputEl().addClass('d-none');
26894         if (this.getValue() == '') {
26895             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26896             
26897         } else {
26898             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26899         }
26900         this.markdownEl.on('click', this.toggleTextEdit, this);
26901         this.on('blur', this.toggleTextEdit, this);
26902         this.on('specialkey', this.resizeTextArea, this);
26903     },
26904     
26905     toggleTextEdit : function()
26906     {
26907         var sh = this.markdownEl.getHeight();
26908         this.inputEl().addClass('d-none');
26909         this.markdownEl.addClass('d-none');
26910         if (!this.editing) {
26911             // show editor?
26912             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26913             this.inputEl().removeClass('d-none');
26914             this.inputEl().focus();
26915             this.editing = true;
26916             return;
26917         }
26918         // show showdown...
26919         this.updateMarkdown();
26920         this.markdownEl.removeClass('d-none');
26921         this.editing = false;
26922         return;
26923     },
26924     updateMarkdown : function()
26925     {
26926         if (this.getValue() == '') {
26927             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26928             return;
26929         }
26930  
26931         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26932     },
26933     
26934     resizeTextArea: function () {
26935         
26936         var sh = 100;
26937         Roo.log([sh, this.getValue().split("\n").length * 30]);
26938         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26939     },
26940     setValue : function(val)
26941     {
26942         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26943         if (!this.editing) {
26944             this.updateMarkdown();
26945         }
26946         
26947     },
26948     focus : function()
26949     {
26950         if (!this.editing) {
26951             this.toggleTextEdit();
26952         }
26953         
26954     }
26955
26956
26957 });
26958 /**
26959  * @class Roo.bootstrap.Table.AbstractSelectionModel
26960  * @extends Roo.util.Observable
26961  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26962  * implemented by descendant classes.  This class should not be directly instantiated.
26963  * @constructor
26964  */
26965 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26966     this.locked = false;
26967     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26968 };
26969
26970
26971 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26972     /** @ignore Called by the grid automatically. Do not call directly. */
26973     init : function(grid){
26974         this.grid = grid;
26975         this.initEvents();
26976     },
26977
26978     /**
26979      * Locks the selections.
26980      */
26981     lock : function(){
26982         this.locked = true;
26983     },
26984
26985     /**
26986      * Unlocks the selections.
26987      */
26988     unlock : function(){
26989         this.locked = false;
26990     },
26991
26992     /**
26993      * Returns true if the selections are locked.
26994      * @return {Boolean}
26995      */
26996     isLocked : function(){
26997         return this.locked;
26998     },
26999     
27000     
27001     initEvents : function ()
27002     {
27003         
27004     }
27005 });
27006 /**
27007  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27008  * @class Roo.bootstrap.Table.RowSelectionModel
27009  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27010  * It supports multiple selections and keyboard selection/navigation. 
27011  * @constructor
27012  * @param {Object} config
27013  */
27014
27015 Roo.bootstrap.Table.RowSelectionModel = function(config){
27016     Roo.apply(this, config);
27017     this.selections = new Roo.util.MixedCollection(false, function(o){
27018         return o.id;
27019     });
27020
27021     this.last = false;
27022     this.lastActive = false;
27023
27024     this.addEvents({
27025         /**
27026              * @event selectionchange
27027              * Fires when the selection changes
27028              * @param {SelectionModel} this
27029              */
27030             "selectionchange" : true,
27031         /**
27032              * @event afterselectionchange
27033              * Fires after the selection changes (eg. by key press or clicking)
27034              * @param {SelectionModel} this
27035              */
27036             "afterselectionchange" : true,
27037         /**
27038              * @event beforerowselect
27039              * Fires when a row is selected being selected, return false to cancel.
27040              * @param {SelectionModel} this
27041              * @param {Number} rowIndex The selected index
27042              * @param {Boolean} keepExisting False if other selections will be cleared
27043              */
27044             "beforerowselect" : true,
27045         /**
27046              * @event rowselect
27047              * Fires when a row is selected.
27048              * @param {SelectionModel} this
27049              * @param {Number} rowIndex The selected index
27050              * @param {Roo.data.Record} r The record
27051              */
27052             "rowselect" : true,
27053         /**
27054              * @event rowdeselect
27055              * Fires when a row is deselected.
27056              * @param {SelectionModel} this
27057              * @param {Number} rowIndex The selected index
27058              */
27059         "rowdeselect" : true
27060     });
27061     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27062     this.locked = false;
27063  };
27064
27065 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27066     /**
27067      * @cfg {Boolean} singleSelect
27068      * True to allow selection of only one row at a time (defaults to false)
27069      */
27070     singleSelect : false,
27071
27072     // private
27073     initEvents : function()
27074     {
27075
27076         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27077         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27078         //}else{ // allow click to work like normal
27079          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27080         //}
27081         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27082         this.grid.on("rowclick", this.handleMouseDown, this);
27083         
27084         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27085             "up" : function(e){
27086                 if(!e.shiftKey){
27087                     this.selectPrevious(e.shiftKey);
27088                 }else if(this.last !== false && this.lastActive !== false){
27089                     var last = this.last;
27090                     this.selectRange(this.last,  this.lastActive-1);
27091                     this.grid.getView().focusRow(this.lastActive);
27092                     if(last !== false){
27093                         this.last = last;
27094                     }
27095                 }else{
27096                     this.selectFirstRow();
27097                 }
27098                 this.fireEvent("afterselectionchange", this);
27099             },
27100             "down" : function(e){
27101                 if(!e.shiftKey){
27102                     this.selectNext(e.shiftKey);
27103                 }else if(this.last !== false && this.lastActive !== false){
27104                     var last = this.last;
27105                     this.selectRange(this.last,  this.lastActive+1);
27106                     this.grid.getView().focusRow(this.lastActive);
27107                     if(last !== false){
27108                         this.last = last;
27109                     }
27110                 }else{
27111                     this.selectFirstRow();
27112                 }
27113                 this.fireEvent("afterselectionchange", this);
27114             },
27115             scope: this
27116         });
27117         this.grid.store.on('load', function(){
27118             this.selections.clear();
27119         },this);
27120         /*
27121         var view = this.grid.view;
27122         view.on("refresh", this.onRefresh, this);
27123         view.on("rowupdated", this.onRowUpdated, this);
27124         view.on("rowremoved", this.onRemove, this);
27125         */
27126     },
27127
27128     // private
27129     onRefresh : function()
27130     {
27131         var ds = this.grid.store, i, v = this.grid.view;
27132         var s = this.selections;
27133         s.each(function(r){
27134             if((i = ds.indexOfId(r.id)) != -1){
27135                 v.onRowSelect(i);
27136             }else{
27137                 s.remove(r);
27138             }
27139         });
27140     },
27141
27142     // private
27143     onRemove : function(v, index, r){
27144         this.selections.remove(r);
27145     },
27146
27147     // private
27148     onRowUpdated : function(v, index, r){
27149         if(this.isSelected(r)){
27150             v.onRowSelect(index);
27151         }
27152     },
27153
27154     /**
27155      * Select records.
27156      * @param {Array} records The records to select
27157      * @param {Boolean} keepExisting (optional) True to keep existing selections
27158      */
27159     selectRecords : function(records, keepExisting)
27160     {
27161         if(!keepExisting){
27162             this.clearSelections();
27163         }
27164             var ds = this.grid.store;
27165         for(var i = 0, len = records.length; i < len; i++){
27166             this.selectRow(ds.indexOf(records[i]), true);
27167         }
27168     },
27169
27170     /**
27171      * Gets the number of selected rows.
27172      * @return {Number}
27173      */
27174     getCount : function(){
27175         return this.selections.length;
27176     },
27177
27178     /**
27179      * Selects the first row in the grid.
27180      */
27181     selectFirstRow : function(){
27182         this.selectRow(0);
27183     },
27184
27185     /**
27186      * Select the last row.
27187      * @param {Boolean} keepExisting (optional) True to keep existing selections
27188      */
27189     selectLastRow : function(keepExisting){
27190         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27191         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27192     },
27193
27194     /**
27195      * Selects the row immediately following the last selected row.
27196      * @param {Boolean} keepExisting (optional) True to keep existing selections
27197      */
27198     selectNext : function(keepExisting)
27199     {
27200             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27201             this.selectRow(this.last+1, keepExisting);
27202             this.grid.getView().focusRow(this.last);
27203         }
27204     },
27205
27206     /**
27207      * Selects the row that precedes the last selected row.
27208      * @param {Boolean} keepExisting (optional) True to keep existing selections
27209      */
27210     selectPrevious : function(keepExisting){
27211         if(this.last){
27212             this.selectRow(this.last-1, keepExisting);
27213             this.grid.getView().focusRow(this.last);
27214         }
27215     },
27216
27217     /**
27218      * Returns the selected records
27219      * @return {Array} Array of selected records
27220      */
27221     getSelections : function(){
27222         return [].concat(this.selections.items);
27223     },
27224
27225     /**
27226      * Returns the first selected record.
27227      * @return {Record}
27228      */
27229     getSelected : function(){
27230         return this.selections.itemAt(0);
27231     },
27232
27233
27234     /**
27235      * Clears all selections.
27236      */
27237     clearSelections : function(fast)
27238     {
27239         if(this.locked) {
27240             return;
27241         }
27242         if(fast !== true){
27243                 var ds = this.grid.store;
27244             var s = this.selections;
27245             s.each(function(r){
27246                 this.deselectRow(ds.indexOfId(r.id));
27247             }, this);
27248             s.clear();
27249         }else{
27250             this.selections.clear();
27251         }
27252         this.last = false;
27253     },
27254
27255
27256     /**
27257      * Selects all rows.
27258      */
27259     selectAll : function(){
27260         if(this.locked) {
27261             return;
27262         }
27263         this.selections.clear();
27264         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27265             this.selectRow(i, true);
27266         }
27267     },
27268
27269     /**
27270      * Returns True if there is a selection.
27271      * @return {Boolean}
27272      */
27273     hasSelection : function(){
27274         return this.selections.length > 0;
27275     },
27276
27277     /**
27278      * Returns True if the specified row is selected.
27279      * @param {Number/Record} record The record or index of the record to check
27280      * @return {Boolean}
27281      */
27282     isSelected : function(index){
27283             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27284         return (r && this.selections.key(r.id) ? true : false);
27285     },
27286
27287     /**
27288      * Returns True if the specified record id is selected.
27289      * @param {String} id The id of record to check
27290      * @return {Boolean}
27291      */
27292     isIdSelected : function(id){
27293         return (this.selections.key(id) ? true : false);
27294     },
27295
27296
27297     // private
27298     handleMouseDBClick : function(e, t){
27299         
27300     },
27301     // private
27302     handleMouseDown : function(e, t)
27303     {
27304             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27305         if(this.isLocked() || rowIndex < 0 ){
27306             return;
27307         };
27308         if(e.shiftKey && this.last !== false){
27309             var last = this.last;
27310             this.selectRange(last, rowIndex, e.ctrlKey);
27311             this.last = last; // reset the last
27312             t.focus();
27313     
27314         }else{
27315             var isSelected = this.isSelected(rowIndex);
27316             //Roo.log("select row:" + rowIndex);
27317             if(isSelected){
27318                 this.deselectRow(rowIndex);
27319             } else {
27320                         this.selectRow(rowIndex, true);
27321             }
27322     
27323             /*
27324                 if(e.button !== 0 && isSelected){
27325                 alert('rowIndex 2: ' + rowIndex);
27326                     view.focusRow(rowIndex);
27327                 }else if(e.ctrlKey && isSelected){
27328                     this.deselectRow(rowIndex);
27329                 }else if(!isSelected){
27330                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27331                     view.focusRow(rowIndex);
27332                 }
27333             */
27334         }
27335         this.fireEvent("afterselectionchange", this);
27336     },
27337     // private
27338     handleDragableRowClick :  function(grid, rowIndex, e) 
27339     {
27340         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27341             this.selectRow(rowIndex, false);
27342             grid.view.focusRow(rowIndex);
27343              this.fireEvent("afterselectionchange", this);
27344         }
27345     },
27346     
27347     /**
27348      * Selects multiple rows.
27349      * @param {Array} rows Array of the indexes of the row to select
27350      * @param {Boolean} keepExisting (optional) True to keep existing selections
27351      */
27352     selectRows : function(rows, keepExisting){
27353         if(!keepExisting){
27354             this.clearSelections();
27355         }
27356         for(var i = 0, len = rows.length; i < len; i++){
27357             this.selectRow(rows[i], true);
27358         }
27359     },
27360
27361     /**
27362      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27363      * @param {Number} startRow The index of the first row in the range
27364      * @param {Number} endRow The index of the last row in the range
27365      * @param {Boolean} keepExisting (optional) True to retain existing selections
27366      */
27367     selectRange : function(startRow, endRow, keepExisting){
27368         if(this.locked) {
27369             return;
27370         }
27371         if(!keepExisting){
27372             this.clearSelections();
27373         }
27374         if(startRow <= endRow){
27375             for(var i = startRow; i <= endRow; i++){
27376                 this.selectRow(i, true);
27377             }
27378         }else{
27379             for(var i = startRow; i >= endRow; i--){
27380                 this.selectRow(i, true);
27381             }
27382         }
27383     },
27384
27385     /**
27386      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27387      * @param {Number} startRow The index of the first row in the range
27388      * @param {Number} endRow The index of the last row in the range
27389      */
27390     deselectRange : function(startRow, endRow, preventViewNotify){
27391         if(this.locked) {
27392             return;
27393         }
27394         for(var i = startRow; i <= endRow; i++){
27395             this.deselectRow(i, preventViewNotify);
27396         }
27397     },
27398
27399     /**
27400      * Selects a row.
27401      * @param {Number} row The index of the row to select
27402      * @param {Boolean} keepExisting (optional) True to keep existing selections
27403      */
27404     selectRow : function(index, keepExisting, preventViewNotify)
27405     {
27406             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27407             return;
27408         }
27409         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27410             if(!keepExisting || this.singleSelect){
27411                 this.clearSelections();
27412             }
27413             
27414             var r = this.grid.store.getAt(index);
27415             //console.log('selectRow - record id :' + r.id);
27416             
27417             this.selections.add(r);
27418             this.last = this.lastActive = index;
27419             if(!preventViewNotify){
27420                 var proxy = new Roo.Element(
27421                                 this.grid.getRowDom(index)
27422                 );
27423                 proxy.addClass('bg-info info');
27424             }
27425             this.fireEvent("rowselect", this, index, r);
27426             this.fireEvent("selectionchange", this);
27427         }
27428     },
27429
27430     /**
27431      * Deselects a row.
27432      * @param {Number} row The index of the row to deselect
27433      */
27434     deselectRow : function(index, preventViewNotify)
27435     {
27436         if(this.locked) {
27437             return;
27438         }
27439         if(this.last == index){
27440             this.last = false;
27441         }
27442         if(this.lastActive == index){
27443             this.lastActive = false;
27444         }
27445         
27446         var r = this.grid.store.getAt(index);
27447         if (!r) {
27448             return;
27449         }
27450         
27451         this.selections.remove(r);
27452         //.console.log('deselectRow - record id :' + r.id);
27453         if(!preventViewNotify){
27454         
27455             var proxy = new Roo.Element(
27456                 this.grid.getRowDom(index)
27457             );
27458             proxy.removeClass('bg-info info');
27459         }
27460         this.fireEvent("rowdeselect", this, index);
27461         this.fireEvent("selectionchange", this);
27462     },
27463
27464     // private
27465     restoreLast : function(){
27466         if(this._last){
27467             this.last = this._last;
27468         }
27469     },
27470
27471     // private
27472     acceptsNav : function(row, col, cm){
27473         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27474     },
27475
27476     // private
27477     onEditorKey : function(field, e){
27478         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27479         if(k == e.TAB){
27480             e.stopEvent();
27481             ed.completeEdit();
27482             if(e.shiftKey){
27483                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27484             }else{
27485                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27486             }
27487         }else if(k == e.ENTER && !e.ctrlKey){
27488             e.stopEvent();
27489             ed.completeEdit();
27490             if(e.shiftKey){
27491                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27492             }else{
27493                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27494             }
27495         }else if(k == e.ESC){
27496             ed.cancelEdit();
27497         }
27498         if(newCell){
27499             g.startEditing(newCell[0], newCell[1]);
27500         }
27501     }
27502 });
27503 /*
27504  * Based on:
27505  * Ext JS Library 1.1.1
27506  * Copyright(c) 2006-2007, Ext JS, LLC.
27507  *
27508  * Originally Released Under LGPL - original licence link has changed is not relivant.
27509  *
27510  * Fork - LGPL
27511  * <script type="text/javascript">
27512  */
27513  
27514 /**
27515  * @class Roo.bootstrap.PagingToolbar
27516  * @extends Roo.bootstrap.NavSimplebar
27517  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27518  * @constructor
27519  * Create a new PagingToolbar
27520  * @param {Object} config The config object
27521  * @param {Roo.data.Store} store
27522  */
27523 Roo.bootstrap.PagingToolbar = function(config)
27524 {
27525     // old args format still supported... - xtype is prefered..
27526         // created from xtype...
27527     
27528     this.ds = config.dataSource;
27529     
27530     if (config.store && !this.ds) {
27531         this.store= Roo.factory(config.store, Roo.data);
27532         this.ds = this.store;
27533         this.ds.xmodule = this.xmodule || false;
27534     }
27535     
27536     this.toolbarItems = [];
27537     if (config.items) {
27538         this.toolbarItems = config.items;
27539     }
27540     
27541     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27542     
27543     this.cursor = 0;
27544     
27545     if (this.ds) { 
27546         this.bind(this.ds);
27547     }
27548     
27549     if (Roo.bootstrap.version == 4) {
27550         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27551     } else {
27552         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27553     }
27554     
27555 };
27556
27557 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27558     /**
27559      * @cfg {Roo.data.Store} dataSource
27560      * The underlying data store providing the paged data
27561      */
27562     /**
27563      * @cfg {String/HTMLElement/Element} container
27564      * container The id or element that will contain the toolbar
27565      */
27566     /**
27567      * @cfg {Boolean} displayInfo
27568      * True to display the displayMsg (defaults to false)
27569      */
27570     /**
27571      * @cfg {Number} pageSize
27572      * The number of records to display per page (defaults to 20)
27573      */
27574     pageSize: 20,
27575     /**
27576      * @cfg {String} displayMsg
27577      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27578      */
27579     displayMsg : 'Displaying {0} - {1} of {2}',
27580     /**
27581      * @cfg {String} emptyMsg
27582      * The message to display when no records are found (defaults to "No data to display")
27583      */
27584     emptyMsg : 'No data to display',
27585     /**
27586      * Customizable piece of the default paging text (defaults to "Page")
27587      * @type String
27588      */
27589     beforePageText : "Page",
27590     /**
27591      * Customizable piece of the default paging text (defaults to "of %0")
27592      * @type String
27593      */
27594     afterPageText : "of {0}",
27595     /**
27596      * Customizable piece of the default paging text (defaults to "First Page")
27597      * @type String
27598      */
27599     firstText : "First Page",
27600     /**
27601      * Customizable piece of the default paging text (defaults to "Previous Page")
27602      * @type String
27603      */
27604     prevText : "Previous Page",
27605     /**
27606      * Customizable piece of the default paging text (defaults to "Next Page")
27607      * @type String
27608      */
27609     nextText : "Next Page",
27610     /**
27611      * Customizable piece of the default paging text (defaults to "Last Page")
27612      * @type String
27613      */
27614     lastText : "Last Page",
27615     /**
27616      * Customizable piece of the default paging text (defaults to "Refresh")
27617      * @type String
27618      */
27619     refreshText : "Refresh",
27620
27621     buttons : false,
27622     // private
27623     onRender : function(ct, position) 
27624     {
27625         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27626         this.navgroup.parentId = this.id;
27627         this.navgroup.onRender(this.el, null);
27628         // add the buttons to the navgroup
27629         
27630         if(this.displayInfo){
27631             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27632             this.displayEl = this.el.select('.x-paging-info', true).first();
27633 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27634 //            this.displayEl = navel.el.select('span',true).first();
27635         }
27636         
27637         var _this = this;
27638         
27639         if(this.buttons){
27640             Roo.each(_this.buttons, function(e){ // this might need to use render????
27641                Roo.factory(e).render(_this.el);
27642             });
27643         }
27644             
27645         Roo.each(_this.toolbarItems, function(e) {
27646             _this.navgroup.addItem(e);
27647         });
27648         
27649         
27650         this.first = this.navgroup.addItem({
27651             tooltip: this.firstText,
27652             cls: "prev btn-outline-secondary",
27653             html : ' <i class="fa fa-step-backward"></i>',
27654             disabled: true,
27655             preventDefault: true,
27656             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27657         });
27658         
27659         this.prev =  this.navgroup.addItem({
27660             tooltip: this.prevText,
27661             cls: "prev btn-outline-secondary",
27662             html : ' <i class="fa fa-backward"></i>',
27663             disabled: true,
27664             preventDefault: true,
27665             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27666         });
27667     //this.addSeparator();
27668         
27669         
27670         var field = this.navgroup.addItem( {
27671             tagtype : 'span',
27672             cls : 'x-paging-position  btn-outline-secondary',
27673              disabled: true,
27674             html : this.beforePageText  +
27675                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27676                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27677          } ); //?? escaped?
27678         
27679         this.field = field.el.select('input', true).first();
27680         this.field.on("keydown", this.onPagingKeydown, this);
27681         this.field.on("focus", function(){this.dom.select();});
27682     
27683     
27684         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27685         //this.field.setHeight(18);
27686         //this.addSeparator();
27687         this.next = this.navgroup.addItem({
27688             tooltip: this.nextText,
27689             cls: "next btn-outline-secondary",
27690             html : ' <i class="fa fa-forward"></i>',
27691             disabled: true,
27692             preventDefault: true,
27693             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27694         });
27695         this.last = this.navgroup.addItem({
27696             tooltip: this.lastText,
27697             html : ' <i class="fa fa-step-forward"></i>',
27698             cls: "next btn-outline-secondary",
27699             disabled: true,
27700             preventDefault: true,
27701             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27702         });
27703     //this.addSeparator();
27704         this.loading = this.navgroup.addItem({
27705             tooltip: this.refreshText,
27706             cls: "btn-outline-secondary",
27707             html : ' <i class="fa fa-refresh"></i>',
27708             preventDefault: true,
27709             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27710         });
27711         
27712     },
27713
27714     // private
27715     updateInfo : function(){
27716         if(this.displayEl){
27717             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27718             var msg = count == 0 ?
27719                 this.emptyMsg :
27720                 String.format(
27721                     this.displayMsg,
27722                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27723                 );
27724             this.displayEl.update(msg);
27725         }
27726     },
27727
27728     // private
27729     onLoad : function(ds, r, o)
27730     {
27731         this.cursor = o.params && o.params.start ? o.params.start : 0;
27732         
27733         var d = this.getPageData(),
27734             ap = d.activePage,
27735             ps = d.pages;
27736         
27737         
27738         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27739         this.field.dom.value = ap;
27740         this.first.setDisabled(ap == 1);
27741         this.prev.setDisabled(ap == 1);
27742         this.next.setDisabled(ap == ps);
27743         this.last.setDisabled(ap == ps);
27744         this.loading.enable();
27745         this.updateInfo();
27746     },
27747
27748     // private
27749     getPageData : function(){
27750         var total = this.ds.getTotalCount();
27751         return {
27752             total : total,
27753             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27754             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27755         };
27756     },
27757
27758     // private
27759     onLoadError : function(){
27760         this.loading.enable();
27761     },
27762
27763     // private
27764     onPagingKeydown : function(e){
27765         var k = e.getKey();
27766         var d = this.getPageData();
27767         if(k == e.RETURN){
27768             var v = this.field.dom.value, pageNum;
27769             if(!v || isNaN(pageNum = parseInt(v, 10))){
27770                 this.field.dom.value = d.activePage;
27771                 return;
27772             }
27773             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27774             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27775             e.stopEvent();
27776         }
27777         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))
27778         {
27779           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27780           this.field.dom.value = pageNum;
27781           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27782           e.stopEvent();
27783         }
27784         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27785         {
27786           var v = this.field.dom.value, pageNum; 
27787           var increment = (e.shiftKey) ? 10 : 1;
27788           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27789                 increment *= -1;
27790           }
27791           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27792             this.field.dom.value = d.activePage;
27793             return;
27794           }
27795           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27796           {
27797             this.field.dom.value = parseInt(v, 10) + increment;
27798             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27799             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27800           }
27801           e.stopEvent();
27802         }
27803     },
27804
27805     // private
27806     beforeLoad : function(){
27807         if(this.loading){
27808             this.loading.disable();
27809         }
27810     },
27811
27812     // private
27813     onClick : function(which){
27814         
27815         var ds = this.ds;
27816         if (!ds) {
27817             return;
27818         }
27819         
27820         switch(which){
27821             case "first":
27822                 ds.load({params:{start: 0, limit: this.pageSize}});
27823             break;
27824             case "prev":
27825                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27826             break;
27827             case "next":
27828                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27829             break;
27830             case "last":
27831                 var total = ds.getTotalCount();
27832                 var extra = total % this.pageSize;
27833                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27834                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27835             break;
27836             case "refresh":
27837                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27838             break;
27839         }
27840     },
27841
27842     /**
27843      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27844      * @param {Roo.data.Store} store The data store to unbind
27845      */
27846     unbind : function(ds){
27847         ds.un("beforeload", this.beforeLoad, this);
27848         ds.un("load", this.onLoad, this);
27849         ds.un("loadexception", this.onLoadError, this);
27850         ds.un("remove", this.updateInfo, this);
27851         ds.un("add", this.updateInfo, this);
27852         this.ds = undefined;
27853     },
27854
27855     /**
27856      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27857      * @param {Roo.data.Store} store The data store to bind
27858      */
27859     bind : function(ds){
27860         ds.on("beforeload", this.beforeLoad, this);
27861         ds.on("load", this.onLoad, this);
27862         ds.on("loadexception", this.onLoadError, this);
27863         ds.on("remove", this.updateInfo, this);
27864         ds.on("add", this.updateInfo, this);
27865         this.ds = ds;
27866     }
27867 });/*
27868  * - LGPL
27869  *
27870  * element
27871  * 
27872  */
27873
27874 /**
27875  * @class Roo.bootstrap.MessageBar
27876  * @extends Roo.bootstrap.Component
27877  * Bootstrap MessageBar class
27878  * @cfg {String} html contents of the MessageBar
27879  * @cfg {String} weight (info | success | warning | danger) default info
27880  * @cfg {String} beforeClass insert the bar before the given class
27881  * @cfg {Boolean} closable (true | false) default false
27882  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27883  * 
27884  * @constructor
27885  * Create a new Element
27886  * @param {Object} config The config object
27887  */
27888
27889 Roo.bootstrap.MessageBar = function(config){
27890     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27891 };
27892
27893 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27894     
27895     html: '',
27896     weight: 'info',
27897     closable: false,
27898     fixed: false,
27899     beforeClass: 'bootstrap-sticky-wrap',
27900     
27901     getAutoCreate : function(){
27902         
27903         var cfg = {
27904             tag: 'div',
27905             cls: 'alert alert-dismissable alert-' + this.weight,
27906             cn: [
27907                 {
27908                     tag: 'span',
27909                     cls: 'message',
27910                     html: this.html || ''
27911                 }
27912             ]
27913         };
27914         
27915         if(this.fixed){
27916             cfg.cls += ' alert-messages-fixed';
27917         }
27918         
27919         if(this.closable){
27920             cfg.cn.push({
27921                 tag: 'button',
27922                 cls: 'close',
27923                 html: 'x'
27924             });
27925         }
27926         
27927         return cfg;
27928     },
27929     
27930     onRender : function(ct, position)
27931     {
27932         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27933         
27934         if(!this.el){
27935             var cfg = Roo.apply({},  this.getAutoCreate());
27936             cfg.id = Roo.id();
27937             
27938             if (this.cls) {
27939                 cfg.cls += ' ' + this.cls;
27940             }
27941             if (this.style) {
27942                 cfg.style = this.style;
27943             }
27944             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27945             
27946             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27947         }
27948         
27949         this.el.select('>button.close').on('click', this.hide, this);
27950         
27951     },
27952     
27953     show : function()
27954     {
27955         if (!this.rendered) {
27956             this.render();
27957         }
27958         
27959         this.el.show();
27960         
27961         this.fireEvent('show', this);
27962         
27963     },
27964     
27965     hide : function()
27966     {
27967         if (!this.rendered) {
27968             this.render();
27969         }
27970         
27971         this.el.hide();
27972         
27973         this.fireEvent('hide', this);
27974     },
27975     
27976     update : function()
27977     {
27978 //        var e = this.el.dom.firstChild;
27979 //        
27980 //        if(this.closable){
27981 //            e = e.nextSibling;
27982 //        }
27983 //        
27984 //        e.data = this.html || '';
27985
27986         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27987     }
27988    
27989 });
27990
27991  
27992
27993      /*
27994  * - LGPL
27995  *
27996  * Graph
27997  * 
27998  */
27999
28000
28001 /**
28002  * @class Roo.bootstrap.Graph
28003  * @extends Roo.bootstrap.Component
28004  * Bootstrap Graph class
28005 > Prameters
28006  -sm {number} sm 4
28007  -md {number} md 5
28008  @cfg {String} graphtype  bar | vbar | pie
28009  @cfg {number} g_x coodinator | centre x (pie)
28010  @cfg {number} g_y coodinator | centre y (pie)
28011  @cfg {number} g_r radius (pie)
28012  @cfg {number} g_height height of the chart (respected by all elements in the set)
28013  @cfg {number} g_width width of the chart (respected by all elements in the set)
28014  @cfg {Object} title The title of the chart
28015     
28016  -{Array}  values
28017  -opts (object) options for the chart 
28018      o {
28019      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28020      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28021      o vgutter (number)
28022      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.
28023      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28024      o to
28025      o stretch (boolean)
28026      o }
28027  -opts (object) options for the pie
28028      o{
28029      o cut
28030      o startAngle (number)
28031      o endAngle (number)
28032      } 
28033  *
28034  * @constructor
28035  * Create a new Input
28036  * @param {Object} config The config object
28037  */
28038
28039 Roo.bootstrap.Graph = function(config){
28040     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28041     
28042     this.addEvents({
28043         // img events
28044         /**
28045          * @event click
28046          * The img click event for the img.
28047          * @param {Roo.EventObject} e
28048          */
28049         "click" : true
28050     });
28051 };
28052
28053 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28054     
28055     sm: 4,
28056     md: 5,
28057     graphtype: 'bar',
28058     g_height: 250,
28059     g_width: 400,
28060     g_x: 50,
28061     g_y: 50,
28062     g_r: 30,
28063     opts:{
28064         //g_colors: this.colors,
28065         g_type: 'soft',
28066         g_gutter: '20%'
28067
28068     },
28069     title : false,
28070
28071     getAutoCreate : function(){
28072         
28073         var cfg = {
28074             tag: 'div',
28075             html : null
28076         };
28077         
28078         
28079         return  cfg;
28080     },
28081
28082     onRender : function(ct,position){
28083         
28084         
28085         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28086         
28087         if (typeof(Raphael) == 'undefined') {
28088             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28089             return;
28090         }
28091         
28092         this.raphael = Raphael(this.el.dom);
28093         
28094                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28095                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28096                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28097                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28098                 /*
28099                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28100                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28101                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28102                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28103                 
28104                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28105                 r.barchart(330, 10, 300, 220, data1);
28106                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28107                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28108                 */
28109                 
28110                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28111                 // r.barchart(30, 30, 560, 250,  xdata, {
28112                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28113                 //     axis : "0 0 1 1",
28114                 //     axisxlabels :  xdata
28115                 //     //yvalues : cols,
28116                    
28117                 // });
28118 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28119 //        
28120 //        this.load(null,xdata,{
28121 //                axis : "0 0 1 1",
28122 //                axisxlabels :  xdata
28123 //                });
28124
28125     },
28126
28127     load : function(graphtype,xdata,opts)
28128     {
28129         this.raphael.clear();
28130         if(!graphtype) {
28131             graphtype = this.graphtype;
28132         }
28133         if(!opts){
28134             opts = this.opts;
28135         }
28136         var r = this.raphael,
28137             fin = function () {
28138                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28139             },
28140             fout = function () {
28141                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28142             },
28143             pfin = function() {
28144                 this.sector.stop();
28145                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28146
28147                 if (this.label) {
28148                     this.label[0].stop();
28149                     this.label[0].attr({ r: 7.5 });
28150                     this.label[1].attr({ "font-weight": 800 });
28151                 }
28152             },
28153             pfout = function() {
28154                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28155
28156                 if (this.label) {
28157                     this.label[0].animate({ r: 5 }, 500, "bounce");
28158                     this.label[1].attr({ "font-weight": 400 });
28159                 }
28160             };
28161
28162         switch(graphtype){
28163             case 'bar':
28164                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28165                 break;
28166             case 'hbar':
28167                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28168                 break;
28169             case 'pie':
28170 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28171 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28172 //            
28173                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28174                 
28175                 break;
28176
28177         }
28178         
28179         if(this.title){
28180             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28181         }
28182         
28183     },
28184     
28185     setTitle: function(o)
28186     {
28187         this.title = o;
28188     },
28189     
28190     initEvents: function() {
28191         
28192         if(!this.href){
28193             this.el.on('click', this.onClick, this);
28194         }
28195     },
28196     
28197     onClick : function(e)
28198     {
28199         Roo.log('img onclick');
28200         this.fireEvent('click', this, e);
28201     }
28202    
28203 });
28204
28205  
28206 /*
28207  * - LGPL
28208  *
28209  * numberBox
28210  * 
28211  */
28212 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28213
28214 /**
28215  * @class Roo.bootstrap.dash.NumberBox
28216  * @extends Roo.bootstrap.Component
28217  * Bootstrap NumberBox class
28218  * @cfg {String} headline Box headline
28219  * @cfg {String} content Box content
28220  * @cfg {String} icon Box icon
28221  * @cfg {String} footer Footer text
28222  * @cfg {String} fhref Footer href
28223  * 
28224  * @constructor
28225  * Create a new NumberBox
28226  * @param {Object} config The config object
28227  */
28228
28229
28230 Roo.bootstrap.dash.NumberBox = function(config){
28231     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28232     
28233 };
28234
28235 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28236     
28237     headline : '',
28238     content : '',
28239     icon : '',
28240     footer : '',
28241     fhref : '',
28242     ficon : '',
28243     
28244     getAutoCreate : function(){
28245         
28246         var cfg = {
28247             tag : 'div',
28248             cls : 'small-box ',
28249             cn : [
28250                 {
28251                     tag : 'div',
28252                     cls : 'inner',
28253                     cn :[
28254                         {
28255                             tag : 'h3',
28256                             cls : 'roo-headline',
28257                             html : this.headline
28258                         },
28259                         {
28260                             tag : 'p',
28261                             cls : 'roo-content',
28262                             html : this.content
28263                         }
28264                     ]
28265                 }
28266             ]
28267         };
28268         
28269         if(this.icon){
28270             cfg.cn.push({
28271                 tag : 'div',
28272                 cls : 'icon',
28273                 cn :[
28274                     {
28275                         tag : 'i',
28276                         cls : 'ion ' + this.icon
28277                     }
28278                 ]
28279             });
28280         }
28281         
28282         if(this.footer){
28283             var footer = {
28284                 tag : 'a',
28285                 cls : 'small-box-footer',
28286                 href : this.fhref || '#',
28287                 html : this.footer
28288             };
28289             
28290             cfg.cn.push(footer);
28291             
28292         }
28293         
28294         return  cfg;
28295     },
28296
28297     onRender : function(ct,position){
28298         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28299
28300
28301        
28302                 
28303     },
28304
28305     setHeadline: function (value)
28306     {
28307         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28308     },
28309     
28310     setFooter: function (value, href)
28311     {
28312         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28313         
28314         if(href){
28315             this.el.select('a.small-box-footer',true).first().attr('href', href);
28316         }
28317         
28318     },
28319
28320     setContent: function (value)
28321     {
28322         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28323     },
28324
28325     initEvents: function() 
28326     {   
28327         
28328     }
28329     
28330 });
28331
28332  
28333 /*
28334  * - LGPL
28335  *
28336  * TabBox
28337  * 
28338  */
28339 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28340
28341 /**
28342  * @class Roo.bootstrap.dash.TabBox
28343  * @extends Roo.bootstrap.Component
28344  * Bootstrap TabBox class
28345  * @cfg {String} title Title of the TabBox
28346  * @cfg {String} icon Icon of the TabBox
28347  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28348  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28349  * 
28350  * @constructor
28351  * Create a new TabBox
28352  * @param {Object} config The config object
28353  */
28354
28355
28356 Roo.bootstrap.dash.TabBox = function(config){
28357     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28358     this.addEvents({
28359         // raw events
28360         /**
28361          * @event addpane
28362          * When a pane is added
28363          * @param {Roo.bootstrap.dash.TabPane} pane
28364          */
28365         "addpane" : true,
28366         /**
28367          * @event activatepane
28368          * When a pane is activated
28369          * @param {Roo.bootstrap.dash.TabPane} pane
28370          */
28371         "activatepane" : true
28372         
28373          
28374     });
28375     
28376     this.panes = [];
28377 };
28378
28379 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28380
28381     title : '',
28382     icon : false,
28383     showtabs : true,
28384     tabScrollable : false,
28385     
28386     getChildContainer : function()
28387     {
28388         return this.el.select('.tab-content', true).first();
28389     },
28390     
28391     getAutoCreate : function(){
28392         
28393         var header = {
28394             tag: 'li',
28395             cls: 'pull-left header',
28396             html: this.title,
28397             cn : []
28398         };
28399         
28400         if(this.icon){
28401             header.cn.push({
28402                 tag: 'i',
28403                 cls: 'fa ' + this.icon
28404             });
28405         }
28406         
28407         var h = {
28408             tag: 'ul',
28409             cls: 'nav nav-tabs pull-right',
28410             cn: [
28411                 header
28412             ]
28413         };
28414         
28415         if(this.tabScrollable){
28416             h = {
28417                 tag: 'div',
28418                 cls: 'tab-header',
28419                 cn: [
28420                     {
28421                         tag: 'ul',
28422                         cls: 'nav nav-tabs pull-right',
28423                         cn: [
28424                             header
28425                         ]
28426                     }
28427                 ]
28428             };
28429         }
28430         
28431         var cfg = {
28432             tag: 'div',
28433             cls: 'nav-tabs-custom',
28434             cn: [
28435                 h,
28436                 {
28437                     tag: 'div',
28438                     cls: 'tab-content no-padding',
28439                     cn: []
28440                 }
28441             ]
28442         };
28443
28444         return  cfg;
28445     },
28446     initEvents : function()
28447     {
28448         //Roo.log('add add pane handler');
28449         this.on('addpane', this.onAddPane, this);
28450     },
28451      /**
28452      * Updates the box title
28453      * @param {String} html to set the title to.
28454      */
28455     setTitle : function(value)
28456     {
28457         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28458     },
28459     onAddPane : function(pane)
28460     {
28461         this.panes.push(pane);
28462         //Roo.log('addpane');
28463         //Roo.log(pane);
28464         // tabs are rendere left to right..
28465         if(!this.showtabs){
28466             return;
28467         }
28468         
28469         var ctr = this.el.select('.nav-tabs', true).first();
28470          
28471          
28472         var existing = ctr.select('.nav-tab',true);
28473         var qty = existing.getCount();;
28474         
28475         
28476         var tab = ctr.createChild({
28477             tag : 'li',
28478             cls : 'nav-tab' + (qty ? '' : ' active'),
28479             cn : [
28480                 {
28481                     tag : 'a',
28482                     href:'#',
28483                     html : pane.title
28484                 }
28485             ]
28486         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28487         pane.tab = tab;
28488         
28489         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28490         if (!qty) {
28491             pane.el.addClass('active');
28492         }
28493         
28494                 
28495     },
28496     onTabClick : function(ev,un,ob,pane)
28497     {
28498         //Roo.log('tab - prev default');
28499         ev.preventDefault();
28500         
28501         
28502         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28503         pane.tab.addClass('active');
28504         //Roo.log(pane.title);
28505         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28506         // technically we should have a deactivate event.. but maybe add later.
28507         // and it should not de-activate the selected tab...
28508         this.fireEvent('activatepane', pane);
28509         pane.el.addClass('active');
28510         pane.fireEvent('activate');
28511         
28512         
28513     },
28514     
28515     getActivePane : function()
28516     {
28517         var r = false;
28518         Roo.each(this.panes, function(p) {
28519             if(p.el.hasClass('active')){
28520                 r = p;
28521                 return false;
28522             }
28523             
28524             return;
28525         });
28526         
28527         return r;
28528     }
28529     
28530     
28531 });
28532
28533  
28534 /*
28535  * - LGPL
28536  *
28537  * Tab pane
28538  * 
28539  */
28540 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28541 /**
28542  * @class Roo.bootstrap.TabPane
28543  * @extends Roo.bootstrap.Component
28544  * Bootstrap TabPane class
28545  * @cfg {Boolean} active (false | true) Default false
28546  * @cfg {String} title title of panel
28547
28548  * 
28549  * @constructor
28550  * Create a new TabPane
28551  * @param {Object} config The config object
28552  */
28553
28554 Roo.bootstrap.dash.TabPane = function(config){
28555     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28556     
28557     this.addEvents({
28558         // raw events
28559         /**
28560          * @event activate
28561          * When a pane is activated
28562          * @param {Roo.bootstrap.dash.TabPane} pane
28563          */
28564         "activate" : true
28565          
28566     });
28567 };
28568
28569 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28570     
28571     active : false,
28572     title : '',
28573     
28574     // the tabBox that this is attached to.
28575     tab : false,
28576      
28577     getAutoCreate : function() 
28578     {
28579         var cfg = {
28580             tag: 'div',
28581             cls: 'tab-pane'
28582         };
28583         
28584         if(this.active){
28585             cfg.cls += ' active';
28586         }
28587         
28588         return cfg;
28589     },
28590     initEvents  : function()
28591     {
28592         //Roo.log('trigger add pane handler');
28593         this.parent().fireEvent('addpane', this)
28594     },
28595     
28596      /**
28597      * Updates the tab title 
28598      * @param {String} html to set the title to.
28599      */
28600     setTitle: function(str)
28601     {
28602         if (!this.tab) {
28603             return;
28604         }
28605         this.title = str;
28606         this.tab.select('a', true).first().dom.innerHTML = str;
28607         
28608     }
28609     
28610     
28611     
28612 });
28613
28614  
28615
28616
28617  /*
28618  * - LGPL
28619  *
28620  * menu
28621  * 
28622  */
28623 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28624
28625 /**
28626  * @class Roo.bootstrap.menu.Menu
28627  * @extends Roo.bootstrap.Component
28628  * Bootstrap Menu class - container for Menu
28629  * @cfg {String} html Text of the menu
28630  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28631  * @cfg {String} icon Font awesome icon
28632  * @cfg {String} pos Menu align to (top | bottom) default bottom
28633  * 
28634  * 
28635  * @constructor
28636  * Create a new Menu
28637  * @param {Object} config The config object
28638  */
28639
28640
28641 Roo.bootstrap.menu.Menu = function(config){
28642     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28643     
28644     this.addEvents({
28645         /**
28646          * @event beforeshow
28647          * Fires before this menu is displayed
28648          * @param {Roo.bootstrap.menu.Menu} this
28649          */
28650         beforeshow : true,
28651         /**
28652          * @event beforehide
28653          * Fires before this menu is hidden
28654          * @param {Roo.bootstrap.menu.Menu} this
28655          */
28656         beforehide : true,
28657         /**
28658          * @event show
28659          * Fires after this menu is displayed
28660          * @param {Roo.bootstrap.menu.Menu} this
28661          */
28662         show : true,
28663         /**
28664          * @event hide
28665          * Fires after this menu is hidden
28666          * @param {Roo.bootstrap.menu.Menu} this
28667          */
28668         hide : true,
28669         /**
28670          * @event click
28671          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28672          * @param {Roo.bootstrap.menu.Menu} this
28673          * @param {Roo.EventObject} e
28674          */
28675         click : true
28676     });
28677     
28678 };
28679
28680 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28681     
28682     submenu : false,
28683     html : '',
28684     weight : 'default',
28685     icon : false,
28686     pos : 'bottom',
28687     
28688     
28689     getChildContainer : function() {
28690         if(this.isSubMenu){
28691             return this.el;
28692         }
28693         
28694         return this.el.select('ul.dropdown-menu', true).first();  
28695     },
28696     
28697     getAutoCreate : function()
28698     {
28699         var text = [
28700             {
28701                 tag : 'span',
28702                 cls : 'roo-menu-text',
28703                 html : this.html
28704             }
28705         ];
28706         
28707         if(this.icon){
28708             text.unshift({
28709                 tag : 'i',
28710                 cls : 'fa ' + this.icon
28711             })
28712         }
28713         
28714         
28715         var cfg = {
28716             tag : 'div',
28717             cls : 'btn-group',
28718             cn : [
28719                 {
28720                     tag : 'button',
28721                     cls : 'dropdown-button btn btn-' + this.weight,
28722                     cn : text
28723                 },
28724                 {
28725                     tag : 'button',
28726                     cls : 'dropdown-toggle btn btn-' + this.weight,
28727                     cn : [
28728                         {
28729                             tag : 'span',
28730                             cls : 'caret'
28731                         }
28732                     ]
28733                 },
28734                 {
28735                     tag : 'ul',
28736                     cls : 'dropdown-menu'
28737                 }
28738             ]
28739             
28740         };
28741         
28742         if(this.pos == 'top'){
28743             cfg.cls += ' dropup';
28744         }
28745         
28746         if(this.isSubMenu){
28747             cfg = {
28748                 tag : 'ul',
28749                 cls : 'dropdown-menu'
28750             }
28751         }
28752         
28753         return cfg;
28754     },
28755     
28756     onRender : function(ct, position)
28757     {
28758         this.isSubMenu = ct.hasClass('dropdown-submenu');
28759         
28760         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28761     },
28762     
28763     initEvents : function() 
28764     {
28765         if(this.isSubMenu){
28766             return;
28767         }
28768         
28769         this.hidden = true;
28770         
28771         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28772         this.triggerEl.on('click', this.onTriggerPress, this);
28773         
28774         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28775         this.buttonEl.on('click', this.onClick, this);
28776         
28777     },
28778     
28779     list : function()
28780     {
28781         if(this.isSubMenu){
28782             return this.el;
28783         }
28784         
28785         return this.el.select('ul.dropdown-menu', true).first();
28786     },
28787     
28788     onClick : function(e)
28789     {
28790         this.fireEvent("click", this, e);
28791     },
28792     
28793     onTriggerPress  : function(e)
28794     {   
28795         if (this.isVisible()) {
28796             this.hide();
28797         } else {
28798             this.show();
28799         }
28800     },
28801     
28802     isVisible : function(){
28803         return !this.hidden;
28804     },
28805     
28806     show : function()
28807     {
28808         this.fireEvent("beforeshow", this);
28809         
28810         this.hidden = false;
28811         this.el.addClass('open');
28812         
28813         Roo.get(document).on("mouseup", this.onMouseUp, this);
28814         
28815         this.fireEvent("show", this);
28816         
28817         
28818     },
28819     
28820     hide : function()
28821     {
28822         this.fireEvent("beforehide", this);
28823         
28824         this.hidden = true;
28825         this.el.removeClass('open');
28826         
28827         Roo.get(document).un("mouseup", this.onMouseUp);
28828         
28829         this.fireEvent("hide", this);
28830     },
28831     
28832     onMouseUp : function()
28833     {
28834         this.hide();
28835     }
28836     
28837 });
28838
28839  
28840  /*
28841  * - LGPL
28842  *
28843  * menu item
28844  * 
28845  */
28846 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28847
28848 /**
28849  * @class Roo.bootstrap.menu.Item
28850  * @extends Roo.bootstrap.Component
28851  * Bootstrap MenuItem class
28852  * @cfg {Boolean} submenu (true | false) default false
28853  * @cfg {String} html text of the item
28854  * @cfg {String} href the link
28855  * @cfg {Boolean} disable (true | false) default false
28856  * @cfg {Boolean} preventDefault (true | false) default true
28857  * @cfg {String} icon Font awesome icon
28858  * @cfg {String} pos Submenu align to (left | right) default right 
28859  * 
28860  * 
28861  * @constructor
28862  * Create a new Item
28863  * @param {Object} config The config object
28864  */
28865
28866
28867 Roo.bootstrap.menu.Item = function(config){
28868     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28869     this.addEvents({
28870         /**
28871          * @event mouseover
28872          * Fires when the mouse is hovering over this menu
28873          * @param {Roo.bootstrap.menu.Item} this
28874          * @param {Roo.EventObject} e
28875          */
28876         mouseover : true,
28877         /**
28878          * @event mouseout
28879          * Fires when the mouse exits this menu
28880          * @param {Roo.bootstrap.menu.Item} this
28881          * @param {Roo.EventObject} e
28882          */
28883         mouseout : true,
28884         // raw events
28885         /**
28886          * @event click
28887          * The raw click event for the entire grid.
28888          * @param {Roo.EventObject} e
28889          */
28890         click : true
28891     });
28892 };
28893
28894 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28895     
28896     submenu : false,
28897     href : '',
28898     html : '',
28899     preventDefault: true,
28900     disable : false,
28901     icon : false,
28902     pos : 'right',
28903     
28904     getAutoCreate : function()
28905     {
28906         var text = [
28907             {
28908                 tag : 'span',
28909                 cls : 'roo-menu-item-text',
28910                 html : this.html
28911             }
28912         ];
28913         
28914         if(this.icon){
28915             text.unshift({
28916                 tag : 'i',
28917                 cls : 'fa ' + this.icon
28918             })
28919         }
28920         
28921         var cfg = {
28922             tag : 'li',
28923             cn : [
28924                 {
28925                     tag : 'a',
28926                     href : this.href || '#',
28927                     cn : text
28928                 }
28929             ]
28930         };
28931         
28932         if(this.disable){
28933             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28934         }
28935         
28936         if(this.submenu){
28937             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28938             
28939             if(this.pos == 'left'){
28940                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28941             }
28942         }
28943         
28944         return cfg;
28945     },
28946     
28947     initEvents : function() 
28948     {
28949         this.el.on('mouseover', this.onMouseOver, this);
28950         this.el.on('mouseout', this.onMouseOut, this);
28951         
28952         this.el.select('a', true).first().on('click', this.onClick, this);
28953         
28954     },
28955     
28956     onClick : function(e)
28957     {
28958         if(this.preventDefault){
28959             e.preventDefault();
28960         }
28961         
28962         this.fireEvent("click", this, e);
28963     },
28964     
28965     onMouseOver : function(e)
28966     {
28967         if(this.submenu && this.pos == 'left'){
28968             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28969         }
28970         
28971         this.fireEvent("mouseover", this, e);
28972     },
28973     
28974     onMouseOut : function(e)
28975     {
28976         this.fireEvent("mouseout", this, e);
28977     }
28978 });
28979
28980  
28981
28982  /*
28983  * - LGPL
28984  *
28985  * menu separator
28986  * 
28987  */
28988 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28989
28990 /**
28991  * @class Roo.bootstrap.menu.Separator
28992  * @extends Roo.bootstrap.Component
28993  * Bootstrap Separator class
28994  * 
28995  * @constructor
28996  * Create a new Separator
28997  * @param {Object} config The config object
28998  */
28999
29000
29001 Roo.bootstrap.menu.Separator = function(config){
29002     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29003 };
29004
29005 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29006     
29007     getAutoCreate : function(){
29008         var cfg = {
29009             tag : 'li',
29010             cls: 'dropdown-divider divider'
29011         };
29012         
29013         return cfg;
29014     }
29015    
29016 });
29017
29018  
29019
29020  /*
29021  * - LGPL
29022  *
29023  * Tooltip
29024  * 
29025  */
29026
29027 /**
29028  * @class Roo.bootstrap.Tooltip
29029  * Bootstrap Tooltip class
29030  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29031  * to determine which dom element triggers the tooltip.
29032  * 
29033  * It needs to add support for additional attributes like tooltip-position
29034  * 
29035  * @constructor
29036  * Create a new Toolti
29037  * @param {Object} config The config object
29038  */
29039
29040 Roo.bootstrap.Tooltip = function(config){
29041     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29042     
29043     this.alignment = Roo.bootstrap.Tooltip.alignment;
29044     
29045     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29046         this.alignment = config.alignment;
29047     }
29048     
29049 };
29050
29051 Roo.apply(Roo.bootstrap.Tooltip, {
29052     /**
29053      * @function init initialize tooltip monitoring.
29054      * @static
29055      */
29056     currentEl : false,
29057     currentTip : false,
29058     currentRegion : false,
29059     
29060     //  init : delay?
29061     
29062     init : function()
29063     {
29064         Roo.get(document).on('mouseover', this.enter ,this);
29065         Roo.get(document).on('mouseout', this.leave, this);
29066          
29067         
29068         this.currentTip = new Roo.bootstrap.Tooltip();
29069     },
29070     
29071     enter : function(ev)
29072     {
29073         var dom = ev.getTarget();
29074         
29075         //Roo.log(['enter',dom]);
29076         var el = Roo.fly(dom);
29077         if (this.currentEl) {
29078             //Roo.log(dom);
29079             //Roo.log(this.currentEl);
29080             //Roo.log(this.currentEl.contains(dom));
29081             if (this.currentEl == el) {
29082                 return;
29083             }
29084             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29085                 return;
29086             }
29087
29088         }
29089         
29090         if (this.currentTip.el) {
29091             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29092         }    
29093         //Roo.log(ev);
29094         
29095         if(!el || el.dom == document){
29096             return;
29097         }
29098         
29099         var bindEl = el; 
29100         var pel = false;
29101         if (!el.attr('tooltip')) {
29102             pel = el.findParent("[tooltip]");
29103             if (pel) {
29104                 bindEl = Roo.get(pel);
29105             }
29106         }
29107         
29108        
29109         
29110         // you can not look for children, as if el is the body.. then everythign is the child..
29111         if (!pel && !el.attr('tooltip')) { //
29112             if (!el.select("[tooltip]").elements.length) {
29113                 return;
29114             }
29115             // is the mouse over this child...?
29116             bindEl = el.select("[tooltip]").first();
29117             var xy = ev.getXY();
29118             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29119                 //Roo.log("not in region.");
29120                 return;
29121             }
29122             //Roo.log("child element over..");
29123             
29124         }
29125         this.currentEl = el;
29126         this.currentTip.bind(bindEl);
29127         this.currentRegion = Roo.lib.Region.getRegion(dom);
29128         this.currentTip.enter();
29129         
29130     },
29131     leave : function(ev)
29132     {
29133         var dom = ev.getTarget();
29134         //Roo.log(['leave',dom]);
29135         if (!this.currentEl) {
29136             return;
29137         }
29138         
29139         
29140         if (dom != this.currentEl.dom) {
29141             return;
29142         }
29143         var xy = ev.getXY();
29144         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29145             return;
29146         }
29147         // only activate leave if mouse cursor is outside... bounding box..
29148         
29149         
29150         
29151         
29152         if (this.currentTip) {
29153             this.currentTip.leave();
29154         }
29155         //Roo.log('clear currentEl');
29156         this.currentEl = false;
29157         
29158         
29159     },
29160     alignment : {
29161         'left' : ['r-l', [-2,0], 'right'],
29162         'right' : ['l-r', [2,0], 'left'],
29163         'bottom' : ['t-b', [0,2], 'top'],
29164         'top' : [ 'b-t', [0,-2], 'bottom']
29165     }
29166     
29167 });
29168
29169
29170 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29171     
29172     
29173     bindEl : false,
29174     
29175     delay : null, // can be { show : 300 , hide: 500}
29176     
29177     timeout : null,
29178     
29179     hoverState : null, //???
29180     
29181     placement : 'bottom', 
29182     
29183     alignment : false,
29184     
29185     getAutoCreate : function(){
29186     
29187         var cfg = {
29188            cls : 'tooltip',   
29189            role : 'tooltip',
29190            cn : [
29191                 {
29192                     cls : 'tooltip-arrow arrow'
29193                 },
29194                 {
29195                     cls : 'tooltip-inner'
29196                 }
29197            ]
29198         };
29199         
29200         return cfg;
29201     },
29202     bind : function(el)
29203     {
29204         this.bindEl = el;
29205     },
29206     
29207     initEvents : function()
29208     {
29209         this.arrowEl = this.el.select('.arrow', true).first();
29210         this.innerEl = this.el.select('.tooltip-inner', true).first();
29211     },
29212     
29213     enter : function () {
29214        
29215         if (this.timeout != null) {
29216             clearTimeout(this.timeout);
29217         }
29218         
29219         this.hoverState = 'in';
29220          //Roo.log("enter - show");
29221         if (!this.delay || !this.delay.show) {
29222             this.show();
29223             return;
29224         }
29225         var _t = this;
29226         this.timeout = setTimeout(function () {
29227             if (_t.hoverState == 'in') {
29228                 _t.show();
29229             }
29230         }, this.delay.show);
29231     },
29232     leave : function()
29233     {
29234         clearTimeout(this.timeout);
29235     
29236         this.hoverState = 'out';
29237          if (!this.delay || !this.delay.hide) {
29238             this.hide();
29239             return;
29240         }
29241        
29242         var _t = this;
29243         this.timeout = setTimeout(function () {
29244             //Roo.log("leave - timeout");
29245             
29246             if (_t.hoverState == 'out') {
29247                 _t.hide();
29248                 Roo.bootstrap.Tooltip.currentEl = false;
29249             }
29250         }, delay);
29251     },
29252     
29253     show : function (msg)
29254     {
29255         if (!this.el) {
29256             this.render(document.body);
29257         }
29258         // set content.
29259         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29260         
29261         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29262         
29263         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29264         
29265         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29266                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29267         
29268         var placement = typeof this.placement == 'function' ?
29269             this.placement.call(this, this.el, on_el) :
29270             this.placement;
29271             
29272         var autoToken = /\s?auto?\s?/i;
29273         var autoPlace = autoToken.test(placement);
29274         if (autoPlace) {
29275             placement = placement.replace(autoToken, '') || 'top';
29276         }
29277         
29278         //this.el.detach()
29279         //this.el.setXY([0,0]);
29280         this.el.show();
29281         //this.el.dom.style.display='block';
29282         
29283         //this.el.appendTo(on_el);
29284         
29285         var p = this.getPosition();
29286         var box = this.el.getBox();
29287         
29288         if (autoPlace) {
29289             // fixme..
29290         }
29291         
29292         var align = this.alignment[placement];
29293         
29294         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29295         
29296         if(placement == 'top' || placement == 'bottom'){
29297             if(xy[0] < 0){
29298                 placement = 'right';
29299             }
29300             
29301             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29302                 placement = 'left';
29303             }
29304             
29305             var scroll = Roo.select('body', true).first().getScroll();
29306             
29307             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29308                 placement = 'top';
29309             }
29310             
29311             align = this.alignment[placement];
29312             
29313             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29314             
29315         }
29316         
29317         var elems = document.getElementsByTagName('div');
29318         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29319         for (var i = 0; i < elems.length; i++) {
29320           var zindex = Number.parseInt(
29321                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29322                 10
29323           );
29324           if (zindex > highest) {
29325             highest = zindex;
29326           }
29327         }
29328         
29329         
29330         
29331         this.el.dom.style.zIndex = highest;
29332         
29333         this.el.alignTo(this.bindEl, align[0],align[1]);
29334         //var arrow = this.el.select('.arrow',true).first();
29335         //arrow.set(align[2], 
29336         
29337         this.el.addClass(placement);
29338         this.el.addClass("bs-tooltip-"+ placement);
29339         
29340         this.el.addClass('in fade show');
29341         
29342         this.hoverState = null;
29343         
29344         if (this.el.hasClass('fade')) {
29345             // fade it?
29346         }
29347         
29348         
29349         
29350         
29351         
29352     },
29353     hide : function()
29354     {
29355          
29356         if (!this.el) {
29357             return;
29358         }
29359         //this.el.setXY([0,0]);
29360         this.el.removeClass(['show', 'in']);
29361         //this.el.hide();
29362         
29363     }
29364     
29365 });
29366  
29367
29368  /*
29369  * - LGPL
29370  *
29371  * Location Picker
29372  * 
29373  */
29374
29375 /**
29376  * @class Roo.bootstrap.LocationPicker
29377  * @extends Roo.bootstrap.Component
29378  * Bootstrap LocationPicker class
29379  * @cfg {Number} latitude Position when init default 0
29380  * @cfg {Number} longitude Position when init default 0
29381  * @cfg {Number} zoom default 15
29382  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29383  * @cfg {Boolean} mapTypeControl default false
29384  * @cfg {Boolean} disableDoubleClickZoom default false
29385  * @cfg {Boolean} scrollwheel default true
29386  * @cfg {Boolean} streetViewControl default false
29387  * @cfg {Number} radius default 0
29388  * @cfg {String} locationName
29389  * @cfg {Boolean} draggable default true
29390  * @cfg {Boolean} enableAutocomplete default false
29391  * @cfg {Boolean} enableReverseGeocode default true
29392  * @cfg {String} markerTitle
29393  * 
29394  * @constructor
29395  * Create a new LocationPicker
29396  * @param {Object} config The config object
29397  */
29398
29399
29400 Roo.bootstrap.LocationPicker = function(config){
29401     
29402     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29403     
29404     this.addEvents({
29405         /**
29406          * @event initial
29407          * Fires when the picker initialized.
29408          * @param {Roo.bootstrap.LocationPicker} this
29409          * @param {Google Location} location
29410          */
29411         initial : true,
29412         /**
29413          * @event positionchanged
29414          * Fires when the picker position changed.
29415          * @param {Roo.bootstrap.LocationPicker} this
29416          * @param {Google Location} location
29417          */
29418         positionchanged : true,
29419         /**
29420          * @event resize
29421          * Fires when the map resize.
29422          * @param {Roo.bootstrap.LocationPicker} this
29423          */
29424         resize : true,
29425         /**
29426          * @event show
29427          * Fires when the map show.
29428          * @param {Roo.bootstrap.LocationPicker} this
29429          */
29430         show : true,
29431         /**
29432          * @event hide
29433          * Fires when the map hide.
29434          * @param {Roo.bootstrap.LocationPicker} this
29435          */
29436         hide : true,
29437         /**
29438          * @event mapClick
29439          * Fires when click the map.
29440          * @param {Roo.bootstrap.LocationPicker} this
29441          * @param {Map event} e
29442          */
29443         mapClick : true,
29444         /**
29445          * @event mapRightClick
29446          * Fires when right click the map.
29447          * @param {Roo.bootstrap.LocationPicker} this
29448          * @param {Map event} e
29449          */
29450         mapRightClick : true,
29451         /**
29452          * @event markerClick
29453          * Fires when click the marker.
29454          * @param {Roo.bootstrap.LocationPicker} this
29455          * @param {Map event} e
29456          */
29457         markerClick : true,
29458         /**
29459          * @event markerRightClick
29460          * Fires when right click the marker.
29461          * @param {Roo.bootstrap.LocationPicker} this
29462          * @param {Map event} e
29463          */
29464         markerRightClick : true,
29465         /**
29466          * @event OverlayViewDraw
29467          * Fires when OverlayView Draw
29468          * @param {Roo.bootstrap.LocationPicker} this
29469          */
29470         OverlayViewDraw : true,
29471         /**
29472          * @event OverlayViewOnAdd
29473          * Fires when OverlayView Draw
29474          * @param {Roo.bootstrap.LocationPicker} this
29475          */
29476         OverlayViewOnAdd : true,
29477         /**
29478          * @event OverlayViewOnRemove
29479          * Fires when OverlayView Draw
29480          * @param {Roo.bootstrap.LocationPicker} this
29481          */
29482         OverlayViewOnRemove : true,
29483         /**
29484          * @event OverlayViewShow
29485          * Fires when OverlayView Draw
29486          * @param {Roo.bootstrap.LocationPicker} this
29487          * @param {Pixel} cpx
29488          */
29489         OverlayViewShow : true,
29490         /**
29491          * @event OverlayViewHide
29492          * Fires when OverlayView Draw
29493          * @param {Roo.bootstrap.LocationPicker} this
29494          */
29495         OverlayViewHide : true,
29496         /**
29497          * @event loadexception
29498          * Fires when load google lib failed.
29499          * @param {Roo.bootstrap.LocationPicker} this
29500          */
29501         loadexception : true
29502     });
29503         
29504 };
29505
29506 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29507     
29508     gMapContext: false,
29509     
29510     latitude: 0,
29511     longitude: 0,
29512     zoom: 15,
29513     mapTypeId: false,
29514     mapTypeControl: false,
29515     disableDoubleClickZoom: false,
29516     scrollwheel: true,
29517     streetViewControl: false,
29518     radius: 0,
29519     locationName: '',
29520     draggable: true,
29521     enableAutocomplete: false,
29522     enableReverseGeocode: true,
29523     markerTitle: '',
29524     
29525     getAutoCreate: function()
29526     {
29527
29528         var cfg = {
29529             tag: 'div',
29530             cls: 'roo-location-picker'
29531         };
29532         
29533         return cfg
29534     },
29535     
29536     initEvents: function(ct, position)
29537     {       
29538         if(!this.el.getWidth() || this.isApplied()){
29539             return;
29540         }
29541         
29542         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29543         
29544         this.initial();
29545     },
29546     
29547     initial: function()
29548     {
29549         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29550             this.fireEvent('loadexception', this);
29551             return;
29552         }
29553         
29554         if(!this.mapTypeId){
29555             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29556         }
29557         
29558         this.gMapContext = this.GMapContext();
29559         
29560         this.initOverlayView();
29561         
29562         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29563         
29564         var _this = this;
29565                 
29566         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29567             _this.setPosition(_this.gMapContext.marker.position);
29568         });
29569         
29570         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29571             _this.fireEvent('mapClick', this, event);
29572             
29573         });
29574
29575         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29576             _this.fireEvent('mapRightClick', this, event);
29577             
29578         });
29579         
29580         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29581             _this.fireEvent('markerClick', this, event);
29582             
29583         });
29584
29585         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29586             _this.fireEvent('markerRightClick', this, event);
29587             
29588         });
29589         
29590         this.setPosition(this.gMapContext.location);
29591         
29592         this.fireEvent('initial', this, this.gMapContext.location);
29593     },
29594     
29595     initOverlayView: function()
29596     {
29597         var _this = this;
29598         
29599         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29600             
29601             draw: function()
29602             {
29603                 _this.fireEvent('OverlayViewDraw', _this);
29604             },
29605             
29606             onAdd: function()
29607             {
29608                 _this.fireEvent('OverlayViewOnAdd', _this);
29609             },
29610             
29611             onRemove: function()
29612             {
29613                 _this.fireEvent('OverlayViewOnRemove', _this);
29614             },
29615             
29616             show: function(cpx)
29617             {
29618                 _this.fireEvent('OverlayViewShow', _this, cpx);
29619             },
29620             
29621             hide: function()
29622             {
29623                 _this.fireEvent('OverlayViewHide', _this);
29624             }
29625             
29626         });
29627     },
29628     
29629     fromLatLngToContainerPixel: function(event)
29630     {
29631         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29632     },
29633     
29634     isApplied: function() 
29635     {
29636         return this.getGmapContext() == false ? false : true;
29637     },
29638     
29639     getGmapContext: function() 
29640     {
29641         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29642     },
29643     
29644     GMapContext: function() 
29645     {
29646         var position = new google.maps.LatLng(this.latitude, this.longitude);
29647         
29648         var _map = new google.maps.Map(this.el.dom, {
29649             center: position,
29650             zoom: this.zoom,
29651             mapTypeId: this.mapTypeId,
29652             mapTypeControl: this.mapTypeControl,
29653             disableDoubleClickZoom: this.disableDoubleClickZoom,
29654             scrollwheel: this.scrollwheel,
29655             streetViewControl: this.streetViewControl,
29656             locationName: this.locationName,
29657             draggable: this.draggable,
29658             enableAutocomplete: this.enableAutocomplete,
29659             enableReverseGeocode: this.enableReverseGeocode
29660         });
29661         
29662         var _marker = new google.maps.Marker({
29663             position: position,
29664             map: _map,
29665             title: this.markerTitle,
29666             draggable: this.draggable
29667         });
29668         
29669         return {
29670             map: _map,
29671             marker: _marker,
29672             circle: null,
29673             location: position,
29674             radius: this.radius,
29675             locationName: this.locationName,
29676             addressComponents: {
29677                 formatted_address: null,
29678                 addressLine1: null,
29679                 addressLine2: null,
29680                 streetName: null,
29681                 streetNumber: null,
29682                 city: null,
29683                 district: null,
29684                 state: null,
29685                 stateOrProvince: null
29686             },
29687             settings: this,
29688             domContainer: this.el.dom,
29689             geodecoder: new google.maps.Geocoder()
29690         };
29691     },
29692     
29693     drawCircle: function(center, radius, options) 
29694     {
29695         if (this.gMapContext.circle != null) {
29696             this.gMapContext.circle.setMap(null);
29697         }
29698         if (radius > 0) {
29699             radius *= 1;
29700             options = Roo.apply({}, options, {
29701                 strokeColor: "#0000FF",
29702                 strokeOpacity: .35,
29703                 strokeWeight: 2,
29704                 fillColor: "#0000FF",
29705                 fillOpacity: .2
29706             });
29707             
29708             options.map = this.gMapContext.map;
29709             options.radius = radius;
29710             options.center = center;
29711             this.gMapContext.circle = new google.maps.Circle(options);
29712             return this.gMapContext.circle;
29713         }
29714         
29715         return null;
29716     },
29717     
29718     setPosition: function(location) 
29719     {
29720         this.gMapContext.location = location;
29721         this.gMapContext.marker.setPosition(location);
29722         this.gMapContext.map.panTo(location);
29723         this.drawCircle(location, this.gMapContext.radius, {});
29724         
29725         var _this = this;
29726         
29727         if (this.gMapContext.settings.enableReverseGeocode) {
29728             this.gMapContext.geodecoder.geocode({
29729                 latLng: this.gMapContext.location
29730             }, function(results, status) {
29731                 
29732                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29733                     _this.gMapContext.locationName = results[0].formatted_address;
29734                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29735                     
29736                     _this.fireEvent('positionchanged', this, location);
29737                 }
29738             });
29739             
29740             return;
29741         }
29742         
29743         this.fireEvent('positionchanged', this, location);
29744     },
29745     
29746     resize: function()
29747     {
29748         google.maps.event.trigger(this.gMapContext.map, "resize");
29749         
29750         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29751         
29752         this.fireEvent('resize', this);
29753     },
29754     
29755     setPositionByLatLng: function(latitude, longitude)
29756     {
29757         this.setPosition(new google.maps.LatLng(latitude, longitude));
29758     },
29759     
29760     getCurrentPosition: function() 
29761     {
29762         return {
29763             latitude: this.gMapContext.location.lat(),
29764             longitude: this.gMapContext.location.lng()
29765         };
29766     },
29767     
29768     getAddressName: function() 
29769     {
29770         return this.gMapContext.locationName;
29771     },
29772     
29773     getAddressComponents: function() 
29774     {
29775         return this.gMapContext.addressComponents;
29776     },
29777     
29778     address_component_from_google_geocode: function(address_components) 
29779     {
29780         var result = {};
29781         
29782         for (var i = 0; i < address_components.length; i++) {
29783             var component = address_components[i];
29784             if (component.types.indexOf("postal_code") >= 0) {
29785                 result.postalCode = component.short_name;
29786             } else if (component.types.indexOf("street_number") >= 0) {
29787                 result.streetNumber = component.short_name;
29788             } else if (component.types.indexOf("route") >= 0) {
29789                 result.streetName = component.short_name;
29790             } else if (component.types.indexOf("neighborhood") >= 0) {
29791                 result.city = component.short_name;
29792             } else if (component.types.indexOf("locality") >= 0) {
29793                 result.city = component.short_name;
29794             } else if (component.types.indexOf("sublocality") >= 0) {
29795                 result.district = component.short_name;
29796             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29797                 result.stateOrProvince = component.short_name;
29798             } else if (component.types.indexOf("country") >= 0) {
29799                 result.country = component.short_name;
29800             }
29801         }
29802         
29803         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29804         result.addressLine2 = "";
29805         return result;
29806     },
29807     
29808     setZoomLevel: function(zoom)
29809     {
29810         this.gMapContext.map.setZoom(zoom);
29811     },
29812     
29813     show: function()
29814     {
29815         if(!this.el){
29816             return;
29817         }
29818         
29819         this.el.show();
29820         
29821         this.resize();
29822         
29823         this.fireEvent('show', this);
29824     },
29825     
29826     hide: function()
29827     {
29828         if(!this.el){
29829             return;
29830         }
29831         
29832         this.el.hide();
29833         
29834         this.fireEvent('hide', this);
29835     }
29836     
29837 });
29838
29839 Roo.apply(Roo.bootstrap.LocationPicker, {
29840     
29841     OverlayView : function(map, options)
29842     {
29843         options = options || {};
29844         
29845         this.setMap(map);
29846     }
29847     
29848     
29849 });/**
29850  * @class Roo.bootstrap.Alert
29851  * @extends Roo.bootstrap.Component
29852  * Bootstrap Alert class - shows an alert area box
29853  * eg
29854  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29855   Enter a valid email address
29856 </div>
29857  * @licence LGPL
29858  * @cfg {String} title The title of alert
29859  * @cfg {String} html The content of alert
29860  * @cfg {String} weight (  success | info | warning | danger )
29861  * @cfg {String} fa font-awesomeicon
29862  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29863  * @cfg {Boolean} close true to show a x closer
29864  * 
29865  * 
29866  * @constructor
29867  * Create a new alert
29868  * @param {Object} config The config object
29869  */
29870
29871
29872 Roo.bootstrap.Alert = function(config){
29873     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29874     
29875 };
29876
29877 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29878     
29879     title: '',
29880     html: '',
29881     weight: false,
29882     fa: false,
29883     faicon: false, // BC
29884     close : false,
29885     
29886     
29887     getAutoCreate : function()
29888     {
29889         
29890         var cfg = {
29891             tag : 'div',
29892             cls : 'alert',
29893             cn : [
29894                 {
29895                     tag: 'button',
29896                     type :  "button",
29897                     cls: "close",
29898                     html : '×',
29899                     style : this.close ? '' : 'display:none'
29900                 },
29901                 {
29902                     tag : 'i',
29903                     cls : 'roo-alert-icon'
29904                     
29905                 },
29906                 {
29907                     tag : 'b',
29908                     cls : 'roo-alert-title',
29909                     html : this.title
29910                 },
29911                 {
29912                     tag : 'span',
29913                     cls : 'roo-alert-text',
29914                     html : this.html
29915                 }
29916             ]
29917         };
29918         
29919         if(this.faicon){
29920             cfg.cn[0].cls += ' fa ' + this.faicon;
29921         }
29922         if(this.fa){
29923             cfg.cn[0].cls += ' fa ' + this.fa;
29924         }
29925         
29926         if(this.weight){
29927             cfg.cls += ' alert-' + this.weight;
29928         }
29929         
29930         return cfg;
29931     },
29932     
29933     initEvents: function() 
29934     {
29935         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29936         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29937         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29938         if (this.seconds > 0) {
29939             this.hide.defer(this.seconds, this);
29940         }
29941     },
29942     
29943     setTitle : function(str)
29944     {
29945         this.titleEl.dom.innerHTML = str;
29946     },
29947     
29948     setText : function(str)
29949     {
29950         this.titleEl.dom.innerHTML = str;
29951     },
29952     
29953     setWeight : function(weight)
29954     {
29955         if(this.weight){
29956             this.el.removeClass('alert-' + this.weight);
29957         }
29958         
29959         this.weight = weight;
29960         
29961         this.el.addClass('alert-' + this.weight);
29962     },
29963     
29964     setIcon : function(icon)
29965     {
29966         if(this.faicon){
29967             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29968         }
29969         
29970         this.faicon = icon;
29971         
29972         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29973     },
29974     
29975     hide: function() 
29976     {
29977         this.el.hide();   
29978     },
29979     
29980     show: function() 
29981     {  
29982         this.el.show();   
29983     }
29984     
29985 });
29986
29987  
29988 /*
29989 * Licence: LGPL
29990 */
29991
29992 /**
29993  * @class Roo.bootstrap.UploadCropbox
29994  * @extends Roo.bootstrap.Component
29995  * Bootstrap UploadCropbox class
29996  * @cfg {String} emptyText show when image has been loaded
29997  * @cfg {String} rotateNotify show when image too small to rotate
29998  * @cfg {Number} errorTimeout default 3000
29999  * @cfg {Number} minWidth default 300
30000  * @cfg {Number} minHeight default 300
30001  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30002  * @cfg {Boolean} isDocument (true|false) default false
30003  * @cfg {String} url action url
30004  * @cfg {String} paramName default 'imageUpload'
30005  * @cfg {String} method default POST
30006  * @cfg {Boolean} loadMask (true|false) default true
30007  * @cfg {Boolean} loadingText default 'Loading...'
30008  * 
30009  * @constructor
30010  * Create a new UploadCropbox
30011  * @param {Object} config The config object
30012  */
30013
30014 Roo.bootstrap.UploadCropbox = function(config){
30015     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30016     
30017     this.addEvents({
30018         /**
30019          * @event beforeselectfile
30020          * Fire before select file
30021          * @param {Roo.bootstrap.UploadCropbox} this
30022          */
30023         "beforeselectfile" : true,
30024         /**
30025          * @event initial
30026          * Fire after initEvent
30027          * @param {Roo.bootstrap.UploadCropbox} this
30028          */
30029         "initial" : true,
30030         /**
30031          * @event crop
30032          * Fire after initEvent
30033          * @param {Roo.bootstrap.UploadCropbox} this
30034          * @param {String} data
30035          */
30036         "crop" : true,
30037         /**
30038          * @event prepare
30039          * Fire when preparing the file data
30040          * @param {Roo.bootstrap.UploadCropbox} this
30041          * @param {Object} file
30042          */
30043         "prepare" : true,
30044         /**
30045          * @event exception
30046          * Fire when get exception
30047          * @param {Roo.bootstrap.UploadCropbox} this
30048          * @param {XMLHttpRequest} xhr
30049          */
30050         "exception" : true,
30051         /**
30052          * @event beforeloadcanvas
30053          * Fire before load the canvas
30054          * @param {Roo.bootstrap.UploadCropbox} this
30055          * @param {String} src
30056          */
30057         "beforeloadcanvas" : true,
30058         /**
30059          * @event trash
30060          * Fire when trash image
30061          * @param {Roo.bootstrap.UploadCropbox} this
30062          */
30063         "trash" : true,
30064         /**
30065          * @event download
30066          * Fire when download the image
30067          * @param {Roo.bootstrap.UploadCropbox} this
30068          */
30069         "download" : true,
30070         /**
30071          * @event footerbuttonclick
30072          * Fire when footerbuttonclick
30073          * @param {Roo.bootstrap.UploadCropbox} this
30074          * @param {String} type
30075          */
30076         "footerbuttonclick" : true,
30077         /**
30078          * @event resize
30079          * Fire when resize
30080          * @param {Roo.bootstrap.UploadCropbox} this
30081          */
30082         "resize" : true,
30083         /**
30084          * @event rotate
30085          * Fire when rotate the image
30086          * @param {Roo.bootstrap.UploadCropbox} this
30087          * @param {String} pos
30088          */
30089         "rotate" : true,
30090         /**
30091          * @event inspect
30092          * Fire when inspect the file
30093          * @param {Roo.bootstrap.UploadCropbox} this
30094          * @param {Object} file
30095          */
30096         "inspect" : true,
30097         /**
30098          * @event upload
30099          * Fire when xhr upload the file
30100          * @param {Roo.bootstrap.UploadCropbox} this
30101          * @param {Object} data
30102          */
30103         "upload" : true,
30104         /**
30105          * @event arrange
30106          * Fire when arrange the file data
30107          * @param {Roo.bootstrap.UploadCropbox} this
30108          * @param {Object} formData
30109          */
30110         "arrange" : true
30111     });
30112     
30113     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30114 };
30115
30116 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30117     
30118     emptyText : 'Click to upload image',
30119     rotateNotify : 'Image is too small to rotate',
30120     errorTimeout : 3000,
30121     scale : 0,
30122     baseScale : 1,
30123     rotate : 0,
30124     dragable : false,
30125     pinching : false,
30126     mouseX : 0,
30127     mouseY : 0,
30128     cropData : false,
30129     minWidth : 300,
30130     minHeight : 300,
30131     file : false,
30132     exif : {},
30133     baseRotate : 1,
30134     cropType : 'image/jpeg',
30135     buttons : false,
30136     canvasLoaded : false,
30137     isDocument : false,
30138     method : 'POST',
30139     paramName : 'imageUpload',
30140     loadMask : true,
30141     loadingText : 'Loading...',
30142     maskEl : false,
30143     
30144     getAutoCreate : function()
30145     {
30146         var cfg = {
30147             tag : 'div',
30148             cls : 'roo-upload-cropbox',
30149             cn : [
30150                 {
30151                     tag : 'input',
30152                     cls : 'roo-upload-cropbox-selector',
30153                     type : 'file'
30154                 },
30155                 {
30156                     tag : 'div',
30157                     cls : 'roo-upload-cropbox-body',
30158                     style : 'cursor:pointer',
30159                     cn : [
30160                         {
30161                             tag : 'div',
30162                             cls : 'roo-upload-cropbox-preview'
30163                         },
30164                         {
30165                             tag : 'div',
30166                             cls : 'roo-upload-cropbox-thumb'
30167                         },
30168                         {
30169                             tag : 'div',
30170                             cls : 'roo-upload-cropbox-empty-notify',
30171                             html : this.emptyText
30172                         },
30173                         {
30174                             tag : 'div',
30175                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30176                             html : this.rotateNotify
30177                         }
30178                     ]
30179                 },
30180                 {
30181                     tag : 'div',
30182                     cls : 'roo-upload-cropbox-footer',
30183                     cn : {
30184                         tag : 'div',
30185                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30186                         cn : []
30187                     }
30188                 }
30189             ]
30190         };
30191         
30192         return cfg;
30193     },
30194     
30195     onRender : function(ct, position)
30196     {
30197         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30198         
30199         if (this.buttons.length) {
30200             
30201             Roo.each(this.buttons, function(bb) {
30202                 
30203                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30204                 
30205                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30206                 
30207             }, this);
30208         }
30209         
30210         if(this.loadMask){
30211             this.maskEl = this.el;
30212         }
30213     },
30214     
30215     initEvents : function()
30216     {
30217         this.urlAPI = (window.createObjectURL && window) || 
30218                                 (window.URL && URL.revokeObjectURL && URL) || 
30219                                 (window.webkitURL && webkitURL);
30220                         
30221         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30222         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30223         
30224         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30225         this.selectorEl.hide();
30226         
30227         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30228         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30229         
30230         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30231         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30232         this.thumbEl.hide();
30233         
30234         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30235         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30236         
30237         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30238         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30239         this.errorEl.hide();
30240         
30241         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30242         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30243         this.footerEl.hide();
30244         
30245         this.setThumbBoxSize();
30246         
30247         this.bind();
30248         
30249         this.resize();
30250         
30251         this.fireEvent('initial', this);
30252     },
30253
30254     bind : function()
30255     {
30256         var _this = this;
30257         
30258         window.addEventListener("resize", function() { _this.resize(); } );
30259         
30260         this.bodyEl.on('click', this.beforeSelectFile, this);
30261         
30262         if(Roo.isTouch){
30263             this.bodyEl.on('touchstart', this.onTouchStart, this);
30264             this.bodyEl.on('touchmove', this.onTouchMove, this);
30265             this.bodyEl.on('touchend', this.onTouchEnd, this);
30266         }
30267         
30268         if(!Roo.isTouch){
30269             this.bodyEl.on('mousedown', this.onMouseDown, this);
30270             this.bodyEl.on('mousemove', this.onMouseMove, this);
30271             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30272             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30273             Roo.get(document).on('mouseup', this.onMouseUp, this);
30274         }
30275         
30276         this.selectorEl.on('change', this.onFileSelected, this);
30277     },
30278     
30279     reset : function()
30280     {    
30281         this.scale = 0;
30282         this.baseScale = 1;
30283         this.rotate = 0;
30284         this.baseRotate = 1;
30285         this.dragable = false;
30286         this.pinching = false;
30287         this.mouseX = 0;
30288         this.mouseY = 0;
30289         this.cropData = false;
30290         this.notifyEl.dom.innerHTML = this.emptyText;
30291         
30292         this.selectorEl.dom.value = '';
30293         
30294     },
30295     
30296     resize : function()
30297     {
30298         if(this.fireEvent('resize', this) != false){
30299             this.setThumbBoxPosition();
30300             this.setCanvasPosition();
30301         }
30302     },
30303     
30304     onFooterButtonClick : function(e, el, o, type)
30305     {
30306         switch (type) {
30307             case 'rotate-left' :
30308                 this.onRotateLeft(e);
30309                 break;
30310             case 'rotate-right' :
30311                 this.onRotateRight(e);
30312                 break;
30313             case 'picture' :
30314                 this.beforeSelectFile(e);
30315                 break;
30316             case 'trash' :
30317                 this.trash(e);
30318                 break;
30319             case 'crop' :
30320                 this.crop(e);
30321                 break;
30322             case 'download' :
30323                 this.download(e);
30324                 break;
30325             default :
30326                 break;
30327         }
30328         
30329         this.fireEvent('footerbuttonclick', this, type);
30330     },
30331     
30332     beforeSelectFile : function(e)
30333     {
30334         e.preventDefault();
30335         
30336         if(this.fireEvent('beforeselectfile', this) != false){
30337             this.selectorEl.dom.click();
30338         }
30339     },
30340     
30341     onFileSelected : function(e)
30342     {
30343         e.preventDefault();
30344         
30345         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30346             return;
30347         }
30348         
30349         var file = this.selectorEl.dom.files[0];
30350         
30351         if(this.fireEvent('inspect', this, file) != false){
30352             this.prepare(file);
30353         }
30354         
30355     },
30356     
30357     trash : function(e)
30358     {
30359         this.fireEvent('trash', this);
30360     },
30361     
30362     download : function(e)
30363     {
30364         this.fireEvent('download', this);
30365     },
30366     
30367     loadCanvas : function(src)
30368     {   
30369         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30370             
30371             this.reset();
30372             
30373             this.imageEl = document.createElement('img');
30374             
30375             var _this = this;
30376             
30377             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30378             
30379             this.imageEl.src = src;
30380         }
30381     },
30382     
30383     onLoadCanvas : function()
30384     {   
30385         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30386         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30387         
30388         this.bodyEl.un('click', this.beforeSelectFile, this);
30389         
30390         this.notifyEl.hide();
30391         this.thumbEl.show();
30392         this.footerEl.show();
30393         
30394         this.baseRotateLevel();
30395         
30396         if(this.isDocument){
30397             this.setThumbBoxSize();
30398         }
30399         
30400         this.setThumbBoxPosition();
30401         
30402         this.baseScaleLevel();
30403         
30404         this.draw();
30405         
30406         this.resize();
30407         
30408         this.canvasLoaded = true;
30409         
30410         if(this.loadMask){
30411             this.maskEl.unmask();
30412         }
30413         
30414     },
30415     
30416     setCanvasPosition : function()
30417     {   
30418         if(!this.canvasEl){
30419             return;
30420         }
30421         
30422         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30423         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30424         
30425         this.previewEl.setLeft(pw);
30426         this.previewEl.setTop(ph);
30427         
30428     },
30429     
30430     onMouseDown : function(e)
30431     {   
30432         e.stopEvent();
30433         
30434         this.dragable = true;
30435         this.pinching = false;
30436         
30437         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30438             this.dragable = false;
30439             return;
30440         }
30441         
30442         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30443         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30444         
30445     },
30446     
30447     onMouseMove : function(e)
30448     {   
30449         e.stopEvent();
30450         
30451         if(!this.canvasLoaded){
30452             return;
30453         }
30454         
30455         if (!this.dragable){
30456             return;
30457         }
30458         
30459         var minX = Math.ceil(this.thumbEl.getLeft(true));
30460         var minY = Math.ceil(this.thumbEl.getTop(true));
30461         
30462         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30463         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30464         
30465         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30466         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30467         
30468         x = x - this.mouseX;
30469         y = y - this.mouseY;
30470         
30471         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30472         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30473         
30474         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30475         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30476         
30477         this.previewEl.setLeft(bgX);
30478         this.previewEl.setTop(bgY);
30479         
30480         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30481         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30482     },
30483     
30484     onMouseUp : function(e)
30485     {   
30486         e.stopEvent();
30487         
30488         this.dragable = false;
30489     },
30490     
30491     onMouseWheel : function(e)
30492     {   
30493         e.stopEvent();
30494         
30495         this.startScale = this.scale;
30496         
30497         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30498         
30499         if(!this.zoomable()){
30500             this.scale = this.startScale;
30501             return;
30502         }
30503         
30504         this.draw();
30505         
30506         return;
30507     },
30508     
30509     zoomable : function()
30510     {
30511         var minScale = this.thumbEl.getWidth() / this.minWidth;
30512         
30513         if(this.minWidth < this.minHeight){
30514             minScale = this.thumbEl.getHeight() / this.minHeight;
30515         }
30516         
30517         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30518         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30519         
30520         if(
30521                 this.isDocument &&
30522                 (this.rotate == 0 || this.rotate == 180) && 
30523                 (
30524                     width > this.imageEl.OriginWidth || 
30525                     height > this.imageEl.OriginHeight ||
30526                     (width < this.minWidth && height < this.minHeight)
30527                 )
30528         ){
30529             return false;
30530         }
30531         
30532         if(
30533                 this.isDocument &&
30534                 (this.rotate == 90 || this.rotate == 270) && 
30535                 (
30536                     width > this.imageEl.OriginWidth || 
30537                     height > this.imageEl.OriginHeight ||
30538                     (width < this.minHeight && height < this.minWidth)
30539                 )
30540         ){
30541             return false;
30542         }
30543         
30544         if(
30545                 !this.isDocument &&
30546                 (this.rotate == 0 || this.rotate == 180) && 
30547                 (
30548                     width < this.minWidth || 
30549                     width > this.imageEl.OriginWidth || 
30550                     height < this.minHeight || 
30551                     height > this.imageEl.OriginHeight
30552                 )
30553         ){
30554             return false;
30555         }
30556         
30557         if(
30558                 !this.isDocument &&
30559                 (this.rotate == 90 || this.rotate == 270) && 
30560                 (
30561                     width < this.minHeight || 
30562                     width > this.imageEl.OriginWidth || 
30563                     height < this.minWidth || 
30564                     height > this.imageEl.OriginHeight
30565                 )
30566         ){
30567             return false;
30568         }
30569         
30570         return true;
30571         
30572     },
30573     
30574     onRotateLeft : function(e)
30575     {   
30576         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30577             
30578             var minScale = this.thumbEl.getWidth() / this.minWidth;
30579             
30580             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30581             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30582             
30583             this.startScale = this.scale;
30584             
30585             while (this.getScaleLevel() < minScale){
30586             
30587                 this.scale = this.scale + 1;
30588                 
30589                 if(!this.zoomable()){
30590                     break;
30591                 }
30592                 
30593                 if(
30594                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30595                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30596                 ){
30597                     continue;
30598                 }
30599                 
30600                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30601
30602                 this.draw();
30603                 
30604                 return;
30605             }
30606             
30607             this.scale = this.startScale;
30608             
30609             this.onRotateFail();
30610             
30611             return false;
30612         }
30613         
30614         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30615
30616         if(this.isDocument){
30617             this.setThumbBoxSize();
30618             this.setThumbBoxPosition();
30619             this.setCanvasPosition();
30620         }
30621         
30622         this.draw();
30623         
30624         this.fireEvent('rotate', this, 'left');
30625         
30626     },
30627     
30628     onRotateRight : function(e)
30629     {
30630         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30631             
30632             var minScale = this.thumbEl.getWidth() / this.minWidth;
30633         
30634             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30635             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30636             
30637             this.startScale = this.scale;
30638             
30639             while (this.getScaleLevel() < minScale){
30640             
30641                 this.scale = this.scale + 1;
30642                 
30643                 if(!this.zoomable()){
30644                     break;
30645                 }
30646                 
30647                 if(
30648                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30649                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30650                 ){
30651                     continue;
30652                 }
30653                 
30654                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30655
30656                 this.draw();
30657                 
30658                 return;
30659             }
30660             
30661             this.scale = this.startScale;
30662             
30663             this.onRotateFail();
30664             
30665             return false;
30666         }
30667         
30668         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30669
30670         if(this.isDocument){
30671             this.setThumbBoxSize();
30672             this.setThumbBoxPosition();
30673             this.setCanvasPosition();
30674         }
30675         
30676         this.draw();
30677         
30678         this.fireEvent('rotate', this, 'right');
30679     },
30680     
30681     onRotateFail : function()
30682     {
30683         this.errorEl.show(true);
30684         
30685         var _this = this;
30686         
30687         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30688     },
30689     
30690     draw : function()
30691     {
30692         this.previewEl.dom.innerHTML = '';
30693         
30694         var canvasEl = document.createElement("canvas");
30695         
30696         var contextEl = canvasEl.getContext("2d");
30697         
30698         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30699         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30700         var center = this.imageEl.OriginWidth / 2;
30701         
30702         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30703             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30704             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30705             center = this.imageEl.OriginHeight / 2;
30706         }
30707         
30708         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30709         
30710         contextEl.translate(center, center);
30711         contextEl.rotate(this.rotate * Math.PI / 180);
30712
30713         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30714         
30715         this.canvasEl = document.createElement("canvas");
30716         
30717         this.contextEl = this.canvasEl.getContext("2d");
30718         
30719         switch (this.rotate) {
30720             case 0 :
30721                 
30722                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30723                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30724                 
30725                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30726                 
30727                 break;
30728             case 90 : 
30729                 
30730                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30731                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30732                 
30733                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30734                     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);
30735                     break;
30736                 }
30737                 
30738                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30739                 
30740                 break;
30741             case 180 :
30742                 
30743                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30744                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30745                 
30746                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30747                     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);
30748                     break;
30749                 }
30750                 
30751                 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);
30752                 
30753                 break;
30754             case 270 :
30755                 
30756                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30757                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30758         
30759                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30760                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30761                     break;
30762                 }
30763                 
30764                 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);
30765                 
30766                 break;
30767             default : 
30768                 break;
30769         }
30770         
30771         this.previewEl.appendChild(this.canvasEl);
30772         
30773         this.setCanvasPosition();
30774     },
30775     
30776     crop : function()
30777     {
30778         if(!this.canvasLoaded){
30779             return;
30780         }
30781         
30782         var imageCanvas = document.createElement("canvas");
30783         
30784         var imageContext = imageCanvas.getContext("2d");
30785         
30786         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30787         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30788         
30789         var center = imageCanvas.width / 2;
30790         
30791         imageContext.translate(center, center);
30792         
30793         imageContext.rotate(this.rotate * Math.PI / 180);
30794         
30795         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30796         
30797         var canvas = document.createElement("canvas");
30798         
30799         var context = canvas.getContext("2d");
30800                 
30801         canvas.width = this.minWidth;
30802         canvas.height = this.minHeight;
30803
30804         switch (this.rotate) {
30805             case 0 :
30806                 
30807                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30808                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30809                 
30810                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30811                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30812                 
30813                 var targetWidth = this.minWidth - 2 * x;
30814                 var targetHeight = this.minHeight - 2 * y;
30815                 
30816                 var scale = 1;
30817                 
30818                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30819                     scale = targetWidth / width;
30820                 }
30821                 
30822                 if(x > 0 && y == 0){
30823                     scale = targetHeight / height;
30824                 }
30825                 
30826                 if(x > 0 && y > 0){
30827                     scale = targetWidth / width;
30828                     
30829                     if(width < height){
30830                         scale = targetHeight / height;
30831                     }
30832                 }
30833                 
30834                 context.scale(scale, scale);
30835                 
30836                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30837                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30838
30839                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30840                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30841
30842                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30843                 
30844                 break;
30845             case 90 : 
30846                 
30847                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30848                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30849                 
30850                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30851                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30852                 
30853                 var targetWidth = this.minWidth - 2 * x;
30854                 var targetHeight = this.minHeight - 2 * y;
30855                 
30856                 var scale = 1;
30857                 
30858                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30859                     scale = targetWidth / width;
30860                 }
30861                 
30862                 if(x > 0 && y == 0){
30863                     scale = targetHeight / height;
30864                 }
30865                 
30866                 if(x > 0 && y > 0){
30867                     scale = targetWidth / width;
30868                     
30869                     if(width < height){
30870                         scale = targetHeight / height;
30871                     }
30872                 }
30873                 
30874                 context.scale(scale, scale);
30875                 
30876                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30877                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30878
30879                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30880                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30881                 
30882                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30883                 
30884                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30885                 
30886                 break;
30887             case 180 :
30888                 
30889                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30890                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30891                 
30892                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30893                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30894                 
30895                 var targetWidth = this.minWidth - 2 * x;
30896                 var targetHeight = this.minHeight - 2 * y;
30897                 
30898                 var scale = 1;
30899                 
30900                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30901                     scale = targetWidth / width;
30902                 }
30903                 
30904                 if(x > 0 && y == 0){
30905                     scale = targetHeight / height;
30906                 }
30907                 
30908                 if(x > 0 && y > 0){
30909                     scale = targetWidth / width;
30910                     
30911                     if(width < height){
30912                         scale = targetHeight / height;
30913                     }
30914                 }
30915                 
30916                 context.scale(scale, scale);
30917                 
30918                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30919                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30920
30921                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30922                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30923
30924                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30925                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30926                 
30927                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30928                 
30929                 break;
30930             case 270 :
30931                 
30932                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30933                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30934                 
30935                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30936                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30937                 
30938                 var targetWidth = this.minWidth - 2 * x;
30939                 var targetHeight = this.minHeight - 2 * y;
30940                 
30941                 var scale = 1;
30942                 
30943                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30944                     scale = targetWidth / width;
30945                 }
30946                 
30947                 if(x > 0 && y == 0){
30948                     scale = targetHeight / height;
30949                 }
30950                 
30951                 if(x > 0 && y > 0){
30952                     scale = targetWidth / width;
30953                     
30954                     if(width < height){
30955                         scale = targetHeight / height;
30956                     }
30957                 }
30958                 
30959                 context.scale(scale, scale);
30960                 
30961                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30962                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30963
30964                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30965                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30966                 
30967                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30968                 
30969                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30970                 
30971                 break;
30972             default : 
30973                 break;
30974         }
30975         
30976         this.cropData = canvas.toDataURL(this.cropType);
30977         
30978         if(this.fireEvent('crop', this, this.cropData) !== false){
30979             this.process(this.file, this.cropData);
30980         }
30981         
30982         return;
30983         
30984     },
30985     
30986     setThumbBoxSize : function()
30987     {
30988         var width, height;
30989         
30990         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30991             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30992             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30993             
30994             this.minWidth = width;
30995             this.minHeight = height;
30996             
30997             if(this.rotate == 90 || this.rotate == 270){
30998                 this.minWidth = height;
30999                 this.minHeight = width;
31000             }
31001         }
31002         
31003         height = 300;
31004         width = Math.ceil(this.minWidth * height / this.minHeight);
31005         
31006         if(this.minWidth > this.minHeight){
31007             width = 300;
31008             height = Math.ceil(this.minHeight * width / this.minWidth);
31009         }
31010         
31011         this.thumbEl.setStyle({
31012             width : width + 'px',
31013             height : height + 'px'
31014         });
31015
31016         return;
31017             
31018     },
31019     
31020     setThumbBoxPosition : function()
31021     {
31022         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31023         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31024         
31025         this.thumbEl.setLeft(x);
31026         this.thumbEl.setTop(y);
31027         
31028     },
31029     
31030     baseRotateLevel : function()
31031     {
31032         this.baseRotate = 1;
31033         
31034         if(
31035                 typeof(this.exif) != 'undefined' &&
31036                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31037                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31038         ){
31039             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31040         }
31041         
31042         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31043         
31044     },
31045     
31046     baseScaleLevel : function()
31047     {
31048         var width, height;
31049         
31050         if(this.isDocument){
31051             
31052             if(this.baseRotate == 6 || this.baseRotate == 8){
31053             
31054                 height = this.thumbEl.getHeight();
31055                 this.baseScale = height / this.imageEl.OriginWidth;
31056
31057                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31058                     width = this.thumbEl.getWidth();
31059                     this.baseScale = width / this.imageEl.OriginHeight;
31060                 }
31061
31062                 return;
31063             }
31064
31065             height = this.thumbEl.getHeight();
31066             this.baseScale = height / this.imageEl.OriginHeight;
31067
31068             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31069                 width = this.thumbEl.getWidth();
31070                 this.baseScale = width / this.imageEl.OriginWidth;
31071             }
31072
31073             return;
31074         }
31075         
31076         if(this.baseRotate == 6 || this.baseRotate == 8){
31077             
31078             width = this.thumbEl.getHeight();
31079             this.baseScale = width / this.imageEl.OriginHeight;
31080             
31081             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31082                 height = this.thumbEl.getWidth();
31083                 this.baseScale = height / this.imageEl.OriginHeight;
31084             }
31085             
31086             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31087                 height = this.thumbEl.getWidth();
31088                 this.baseScale = height / this.imageEl.OriginHeight;
31089                 
31090                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31091                     width = this.thumbEl.getHeight();
31092                     this.baseScale = width / this.imageEl.OriginWidth;
31093                 }
31094             }
31095             
31096             return;
31097         }
31098         
31099         width = this.thumbEl.getWidth();
31100         this.baseScale = width / this.imageEl.OriginWidth;
31101         
31102         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31103             height = this.thumbEl.getHeight();
31104             this.baseScale = height / this.imageEl.OriginHeight;
31105         }
31106         
31107         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31108             
31109             height = this.thumbEl.getHeight();
31110             this.baseScale = height / this.imageEl.OriginHeight;
31111             
31112             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31113                 width = this.thumbEl.getWidth();
31114                 this.baseScale = width / this.imageEl.OriginWidth;
31115             }
31116             
31117         }
31118         
31119         return;
31120     },
31121     
31122     getScaleLevel : function()
31123     {
31124         return this.baseScale * Math.pow(1.1, this.scale);
31125     },
31126     
31127     onTouchStart : function(e)
31128     {
31129         if(!this.canvasLoaded){
31130             this.beforeSelectFile(e);
31131             return;
31132         }
31133         
31134         var touches = e.browserEvent.touches;
31135         
31136         if(!touches){
31137             return;
31138         }
31139         
31140         if(touches.length == 1){
31141             this.onMouseDown(e);
31142             return;
31143         }
31144         
31145         if(touches.length != 2){
31146             return;
31147         }
31148         
31149         var coords = [];
31150         
31151         for(var i = 0, finger; finger = touches[i]; i++){
31152             coords.push(finger.pageX, finger.pageY);
31153         }
31154         
31155         var x = Math.pow(coords[0] - coords[2], 2);
31156         var y = Math.pow(coords[1] - coords[3], 2);
31157         
31158         this.startDistance = Math.sqrt(x + y);
31159         
31160         this.startScale = this.scale;
31161         
31162         this.pinching = true;
31163         this.dragable = false;
31164         
31165     },
31166     
31167     onTouchMove : function(e)
31168     {
31169         if(!this.pinching && !this.dragable){
31170             return;
31171         }
31172         
31173         var touches = e.browserEvent.touches;
31174         
31175         if(!touches){
31176             return;
31177         }
31178         
31179         if(this.dragable){
31180             this.onMouseMove(e);
31181             return;
31182         }
31183         
31184         var coords = [];
31185         
31186         for(var i = 0, finger; finger = touches[i]; i++){
31187             coords.push(finger.pageX, finger.pageY);
31188         }
31189         
31190         var x = Math.pow(coords[0] - coords[2], 2);
31191         var y = Math.pow(coords[1] - coords[3], 2);
31192         
31193         this.endDistance = Math.sqrt(x + y);
31194         
31195         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31196         
31197         if(!this.zoomable()){
31198             this.scale = this.startScale;
31199             return;
31200         }
31201         
31202         this.draw();
31203         
31204     },
31205     
31206     onTouchEnd : function(e)
31207     {
31208         this.pinching = false;
31209         this.dragable = false;
31210         
31211     },
31212     
31213     process : function(file, crop)
31214     {
31215         if(this.loadMask){
31216             this.maskEl.mask(this.loadingText);
31217         }
31218         
31219         this.xhr = new XMLHttpRequest();
31220         
31221         file.xhr = this.xhr;
31222
31223         this.xhr.open(this.method, this.url, true);
31224         
31225         var headers = {
31226             "Accept": "application/json",
31227             "Cache-Control": "no-cache",
31228             "X-Requested-With": "XMLHttpRequest"
31229         };
31230         
31231         for (var headerName in headers) {
31232             var headerValue = headers[headerName];
31233             if (headerValue) {
31234                 this.xhr.setRequestHeader(headerName, headerValue);
31235             }
31236         }
31237         
31238         var _this = this;
31239         
31240         this.xhr.onload = function()
31241         {
31242             _this.xhrOnLoad(_this.xhr);
31243         }
31244         
31245         this.xhr.onerror = function()
31246         {
31247             _this.xhrOnError(_this.xhr);
31248         }
31249         
31250         var formData = new FormData();
31251
31252         formData.append('returnHTML', 'NO');
31253         
31254         if(crop){
31255             formData.append('crop', crop);
31256         }
31257         
31258         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31259             formData.append(this.paramName, file, file.name);
31260         }
31261         
31262         if(typeof(file.filename) != 'undefined'){
31263             formData.append('filename', file.filename);
31264         }
31265         
31266         if(typeof(file.mimetype) != 'undefined'){
31267             formData.append('mimetype', file.mimetype);
31268         }
31269         
31270         if(this.fireEvent('arrange', this, formData) != false){
31271             this.xhr.send(formData);
31272         };
31273     },
31274     
31275     xhrOnLoad : function(xhr)
31276     {
31277         if(this.loadMask){
31278             this.maskEl.unmask();
31279         }
31280         
31281         if (xhr.readyState !== 4) {
31282             this.fireEvent('exception', this, xhr);
31283             return;
31284         }
31285
31286         var response = Roo.decode(xhr.responseText);
31287         
31288         if(!response.success){
31289             this.fireEvent('exception', this, xhr);
31290             return;
31291         }
31292         
31293         var response = Roo.decode(xhr.responseText);
31294         
31295         this.fireEvent('upload', this, response);
31296         
31297     },
31298     
31299     xhrOnError : function()
31300     {
31301         if(this.loadMask){
31302             this.maskEl.unmask();
31303         }
31304         
31305         Roo.log('xhr on error');
31306         
31307         var response = Roo.decode(xhr.responseText);
31308           
31309         Roo.log(response);
31310         
31311     },
31312     
31313     prepare : function(file)
31314     {   
31315         if(this.loadMask){
31316             this.maskEl.mask(this.loadingText);
31317         }
31318         
31319         this.file = false;
31320         this.exif = {};
31321         
31322         if(typeof(file) === 'string'){
31323             this.loadCanvas(file);
31324             return;
31325         }
31326         
31327         if(!file || !this.urlAPI){
31328             return;
31329         }
31330         
31331         this.file = file;
31332         this.cropType = file.type;
31333         
31334         var _this = this;
31335         
31336         if(this.fireEvent('prepare', this, this.file) != false){
31337             
31338             var reader = new FileReader();
31339             
31340             reader.onload = function (e) {
31341                 if (e.target.error) {
31342                     Roo.log(e.target.error);
31343                     return;
31344                 }
31345                 
31346                 var buffer = e.target.result,
31347                     dataView = new DataView(buffer),
31348                     offset = 2,
31349                     maxOffset = dataView.byteLength - 4,
31350                     markerBytes,
31351                     markerLength;
31352                 
31353                 if (dataView.getUint16(0) === 0xffd8) {
31354                     while (offset < maxOffset) {
31355                         markerBytes = dataView.getUint16(offset);
31356                         
31357                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31358                             markerLength = dataView.getUint16(offset + 2) + 2;
31359                             if (offset + markerLength > dataView.byteLength) {
31360                                 Roo.log('Invalid meta data: Invalid segment size.');
31361                                 break;
31362                             }
31363                             
31364                             if(markerBytes == 0xffe1){
31365                                 _this.parseExifData(
31366                                     dataView,
31367                                     offset,
31368                                     markerLength
31369                                 );
31370                             }
31371                             
31372                             offset += markerLength;
31373                             
31374                             continue;
31375                         }
31376                         
31377                         break;
31378                     }
31379                     
31380                 }
31381                 
31382                 var url = _this.urlAPI.createObjectURL(_this.file);
31383                 
31384                 _this.loadCanvas(url);
31385                 
31386                 return;
31387             }
31388             
31389             reader.readAsArrayBuffer(this.file);
31390             
31391         }
31392         
31393     },
31394     
31395     parseExifData : function(dataView, offset, length)
31396     {
31397         var tiffOffset = offset + 10,
31398             littleEndian,
31399             dirOffset;
31400     
31401         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31402             // No Exif data, might be XMP data instead
31403             return;
31404         }
31405         
31406         // Check for the ASCII code for "Exif" (0x45786966):
31407         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31408             // No Exif data, might be XMP data instead
31409             return;
31410         }
31411         if (tiffOffset + 8 > dataView.byteLength) {
31412             Roo.log('Invalid Exif data: Invalid segment size.');
31413             return;
31414         }
31415         // Check for the two null bytes:
31416         if (dataView.getUint16(offset + 8) !== 0x0000) {
31417             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31418             return;
31419         }
31420         // Check the byte alignment:
31421         switch (dataView.getUint16(tiffOffset)) {
31422         case 0x4949:
31423             littleEndian = true;
31424             break;
31425         case 0x4D4D:
31426             littleEndian = false;
31427             break;
31428         default:
31429             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31430             return;
31431         }
31432         // Check for the TIFF tag marker (0x002A):
31433         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31434             Roo.log('Invalid Exif data: Missing TIFF marker.');
31435             return;
31436         }
31437         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31438         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31439         
31440         this.parseExifTags(
31441             dataView,
31442             tiffOffset,
31443             tiffOffset + dirOffset,
31444             littleEndian
31445         );
31446     },
31447     
31448     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31449     {
31450         var tagsNumber,
31451             dirEndOffset,
31452             i;
31453         if (dirOffset + 6 > dataView.byteLength) {
31454             Roo.log('Invalid Exif data: Invalid directory offset.');
31455             return;
31456         }
31457         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31458         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31459         if (dirEndOffset + 4 > dataView.byteLength) {
31460             Roo.log('Invalid Exif data: Invalid directory size.');
31461             return;
31462         }
31463         for (i = 0; i < tagsNumber; i += 1) {
31464             this.parseExifTag(
31465                 dataView,
31466                 tiffOffset,
31467                 dirOffset + 2 + 12 * i, // tag offset
31468                 littleEndian
31469             );
31470         }
31471         // Return the offset to the next directory:
31472         return dataView.getUint32(dirEndOffset, littleEndian);
31473     },
31474     
31475     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31476     {
31477         var tag = dataView.getUint16(offset, littleEndian);
31478         
31479         this.exif[tag] = this.getExifValue(
31480             dataView,
31481             tiffOffset,
31482             offset,
31483             dataView.getUint16(offset + 2, littleEndian), // tag type
31484             dataView.getUint32(offset + 4, littleEndian), // tag length
31485             littleEndian
31486         );
31487     },
31488     
31489     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31490     {
31491         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31492             tagSize,
31493             dataOffset,
31494             values,
31495             i,
31496             str,
31497             c;
31498     
31499         if (!tagType) {
31500             Roo.log('Invalid Exif data: Invalid tag type.');
31501             return;
31502         }
31503         
31504         tagSize = tagType.size * length;
31505         // Determine if the value is contained in the dataOffset bytes,
31506         // or if the value at the dataOffset is a pointer to the actual data:
31507         dataOffset = tagSize > 4 ?
31508                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31509         if (dataOffset + tagSize > dataView.byteLength) {
31510             Roo.log('Invalid Exif data: Invalid data offset.');
31511             return;
31512         }
31513         if (length === 1) {
31514             return tagType.getValue(dataView, dataOffset, littleEndian);
31515         }
31516         values = [];
31517         for (i = 0; i < length; i += 1) {
31518             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31519         }
31520         
31521         if (tagType.ascii) {
31522             str = '';
31523             // Concatenate the chars:
31524             for (i = 0; i < values.length; i += 1) {
31525                 c = values[i];
31526                 // Ignore the terminating NULL byte(s):
31527                 if (c === '\u0000') {
31528                     break;
31529                 }
31530                 str += c;
31531             }
31532             return str;
31533         }
31534         return values;
31535     }
31536     
31537 });
31538
31539 Roo.apply(Roo.bootstrap.UploadCropbox, {
31540     tags : {
31541         'Orientation': 0x0112
31542     },
31543     
31544     Orientation: {
31545             1: 0, //'top-left',
31546 //            2: 'top-right',
31547             3: 180, //'bottom-right',
31548 //            4: 'bottom-left',
31549 //            5: 'left-top',
31550             6: 90, //'right-top',
31551 //            7: 'right-bottom',
31552             8: 270 //'left-bottom'
31553     },
31554     
31555     exifTagTypes : {
31556         // byte, 8-bit unsigned int:
31557         1: {
31558             getValue: function (dataView, dataOffset) {
31559                 return dataView.getUint8(dataOffset);
31560             },
31561             size: 1
31562         },
31563         // ascii, 8-bit byte:
31564         2: {
31565             getValue: function (dataView, dataOffset) {
31566                 return String.fromCharCode(dataView.getUint8(dataOffset));
31567             },
31568             size: 1,
31569             ascii: true
31570         },
31571         // short, 16 bit int:
31572         3: {
31573             getValue: function (dataView, dataOffset, littleEndian) {
31574                 return dataView.getUint16(dataOffset, littleEndian);
31575             },
31576             size: 2
31577         },
31578         // long, 32 bit int:
31579         4: {
31580             getValue: function (dataView, dataOffset, littleEndian) {
31581                 return dataView.getUint32(dataOffset, littleEndian);
31582             },
31583             size: 4
31584         },
31585         // rational = two long values, first is numerator, second is denominator:
31586         5: {
31587             getValue: function (dataView, dataOffset, littleEndian) {
31588                 return dataView.getUint32(dataOffset, littleEndian) /
31589                     dataView.getUint32(dataOffset + 4, littleEndian);
31590             },
31591             size: 8
31592         },
31593         // slong, 32 bit signed int:
31594         9: {
31595             getValue: function (dataView, dataOffset, littleEndian) {
31596                 return dataView.getInt32(dataOffset, littleEndian);
31597             },
31598             size: 4
31599         },
31600         // srational, two slongs, first is numerator, second is denominator:
31601         10: {
31602             getValue: function (dataView, dataOffset, littleEndian) {
31603                 return dataView.getInt32(dataOffset, littleEndian) /
31604                     dataView.getInt32(dataOffset + 4, littleEndian);
31605             },
31606             size: 8
31607         }
31608     },
31609     
31610     footer : {
31611         STANDARD : [
31612             {
31613                 tag : 'div',
31614                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31615                 action : 'rotate-left',
31616                 cn : [
31617                     {
31618                         tag : 'button',
31619                         cls : 'btn btn-default',
31620                         html : '<i class="fa fa-undo"></i>'
31621                     }
31622                 ]
31623             },
31624             {
31625                 tag : 'div',
31626                 cls : 'btn-group roo-upload-cropbox-picture',
31627                 action : 'picture',
31628                 cn : [
31629                     {
31630                         tag : 'button',
31631                         cls : 'btn btn-default',
31632                         html : '<i class="fa fa-picture-o"></i>'
31633                     }
31634                 ]
31635             },
31636             {
31637                 tag : 'div',
31638                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31639                 action : 'rotate-right',
31640                 cn : [
31641                     {
31642                         tag : 'button',
31643                         cls : 'btn btn-default',
31644                         html : '<i class="fa fa-repeat"></i>'
31645                     }
31646                 ]
31647             }
31648         ],
31649         DOCUMENT : [
31650             {
31651                 tag : 'div',
31652                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31653                 action : 'rotate-left',
31654                 cn : [
31655                     {
31656                         tag : 'button',
31657                         cls : 'btn btn-default',
31658                         html : '<i class="fa fa-undo"></i>'
31659                     }
31660                 ]
31661             },
31662             {
31663                 tag : 'div',
31664                 cls : 'btn-group roo-upload-cropbox-download',
31665                 action : 'download',
31666                 cn : [
31667                     {
31668                         tag : 'button',
31669                         cls : 'btn btn-default',
31670                         html : '<i class="fa fa-download"></i>'
31671                     }
31672                 ]
31673             },
31674             {
31675                 tag : 'div',
31676                 cls : 'btn-group roo-upload-cropbox-crop',
31677                 action : 'crop',
31678                 cn : [
31679                     {
31680                         tag : 'button',
31681                         cls : 'btn btn-default',
31682                         html : '<i class="fa fa-crop"></i>'
31683                     }
31684                 ]
31685             },
31686             {
31687                 tag : 'div',
31688                 cls : 'btn-group roo-upload-cropbox-trash',
31689                 action : 'trash',
31690                 cn : [
31691                     {
31692                         tag : 'button',
31693                         cls : 'btn btn-default',
31694                         html : '<i class="fa fa-trash"></i>'
31695                     }
31696                 ]
31697             },
31698             {
31699                 tag : 'div',
31700                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31701                 action : 'rotate-right',
31702                 cn : [
31703                     {
31704                         tag : 'button',
31705                         cls : 'btn btn-default',
31706                         html : '<i class="fa fa-repeat"></i>'
31707                     }
31708                 ]
31709             }
31710         ],
31711         ROTATOR : [
31712             {
31713                 tag : 'div',
31714                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31715                 action : 'rotate-left',
31716                 cn : [
31717                     {
31718                         tag : 'button',
31719                         cls : 'btn btn-default',
31720                         html : '<i class="fa fa-undo"></i>'
31721                     }
31722                 ]
31723             },
31724             {
31725                 tag : 'div',
31726                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31727                 action : 'rotate-right',
31728                 cn : [
31729                     {
31730                         tag : 'button',
31731                         cls : 'btn btn-default',
31732                         html : '<i class="fa fa-repeat"></i>'
31733                     }
31734                 ]
31735             }
31736         ]
31737     }
31738 });
31739
31740 /*
31741 * Licence: LGPL
31742 */
31743
31744 /**
31745  * @class Roo.bootstrap.DocumentManager
31746  * @extends Roo.bootstrap.Component
31747  * Bootstrap DocumentManager class
31748  * @cfg {String} paramName default 'imageUpload'
31749  * @cfg {String} toolTipName default 'filename'
31750  * @cfg {String} method default POST
31751  * @cfg {String} url action url
31752  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31753  * @cfg {Boolean} multiple multiple upload default true
31754  * @cfg {Number} thumbSize default 300
31755  * @cfg {String} fieldLabel
31756  * @cfg {Number} labelWidth default 4
31757  * @cfg {String} labelAlign (left|top) default left
31758  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31759 * @cfg {Number} labellg set the width of label (1-12)
31760  * @cfg {Number} labelmd set the width of label (1-12)
31761  * @cfg {Number} labelsm set the width of label (1-12)
31762  * @cfg {Number} labelxs set the width of label (1-12)
31763  * 
31764  * @constructor
31765  * Create a new DocumentManager
31766  * @param {Object} config The config object
31767  */
31768
31769 Roo.bootstrap.DocumentManager = function(config){
31770     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31771     
31772     this.files = [];
31773     this.delegates = [];
31774     
31775     this.addEvents({
31776         /**
31777          * @event initial
31778          * Fire when initial the DocumentManager
31779          * @param {Roo.bootstrap.DocumentManager} this
31780          */
31781         "initial" : true,
31782         /**
31783          * @event inspect
31784          * inspect selected file
31785          * @param {Roo.bootstrap.DocumentManager} this
31786          * @param {File} file
31787          */
31788         "inspect" : true,
31789         /**
31790          * @event exception
31791          * Fire when xhr load exception
31792          * @param {Roo.bootstrap.DocumentManager} this
31793          * @param {XMLHttpRequest} xhr
31794          */
31795         "exception" : true,
31796         /**
31797          * @event afterupload
31798          * Fire when xhr load exception
31799          * @param {Roo.bootstrap.DocumentManager} this
31800          * @param {XMLHttpRequest} xhr
31801          */
31802         "afterupload" : true,
31803         /**
31804          * @event prepare
31805          * prepare the form data
31806          * @param {Roo.bootstrap.DocumentManager} this
31807          * @param {Object} formData
31808          */
31809         "prepare" : true,
31810         /**
31811          * @event remove
31812          * Fire when remove the file
31813          * @param {Roo.bootstrap.DocumentManager} this
31814          * @param {Object} file
31815          */
31816         "remove" : true,
31817         /**
31818          * @event refresh
31819          * Fire after refresh the file
31820          * @param {Roo.bootstrap.DocumentManager} this
31821          */
31822         "refresh" : true,
31823         /**
31824          * @event click
31825          * Fire after click the image
31826          * @param {Roo.bootstrap.DocumentManager} this
31827          * @param {Object} file
31828          */
31829         "click" : true,
31830         /**
31831          * @event edit
31832          * Fire when upload a image and editable set to true
31833          * @param {Roo.bootstrap.DocumentManager} this
31834          * @param {Object} file
31835          */
31836         "edit" : true,
31837         /**
31838          * @event beforeselectfile
31839          * Fire before select file
31840          * @param {Roo.bootstrap.DocumentManager} this
31841          */
31842         "beforeselectfile" : true,
31843         /**
31844          * @event process
31845          * Fire before process file
31846          * @param {Roo.bootstrap.DocumentManager} this
31847          * @param {Object} file
31848          */
31849         "process" : true,
31850         /**
31851          * @event previewrendered
31852          * Fire when preview rendered
31853          * @param {Roo.bootstrap.DocumentManager} this
31854          * @param {Object} file
31855          */
31856         "previewrendered" : true,
31857         /**
31858          */
31859         "previewResize" : true
31860         
31861     });
31862 };
31863
31864 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31865     
31866     boxes : 0,
31867     inputName : '',
31868     thumbSize : 300,
31869     multiple : true,
31870     files : false,
31871     method : 'POST',
31872     url : '',
31873     paramName : 'imageUpload',
31874     toolTipName : 'filename',
31875     fieldLabel : '',
31876     labelWidth : 4,
31877     labelAlign : 'left',
31878     editable : true,
31879     delegates : false,
31880     xhr : false, 
31881     
31882     labellg : 0,
31883     labelmd : 0,
31884     labelsm : 0,
31885     labelxs : 0,
31886     
31887     getAutoCreate : function()
31888     {   
31889         var managerWidget = {
31890             tag : 'div',
31891             cls : 'roo-document-manager',
31892             cn : [
31893                 {
31894                     tag : 'input',
31895                     cls : 'roo-document-manager-selector',
31896                     type : 'file'
31897                 },
31898                 {
31899                     tag : 'div',
31900                     cls : 'roo-document-manager-uploader',
31901                     cn : [
31902                         {
31903                             tag : 'div',
31904                             cls : 'roo-document-manager-upload-btn',
31905                             html : '<i class="fa fa-plus"></i>'
31906                         }
31907                     ]
31908                     
31909                 }
31910             ]
31911         };
31912         
31913         var content = [
31914             {
31915                 tag : 'div',
31916                 cls : 'column col-md-12',
31917                 cn : managerWidget
31918             }
31919         ];
31920         
31921         if(this.fieldLabel.length){
31922             
31923             content = [
31924                 {
31925                     tag : 'div',
31926                     cls : 'column col-md-12',
31927                     html : this.fieldLabel
31928                 },
31929                 {
31930                     tag : 'div',
31931                     cls : 'column col-md-12',
31932                     cn : managerWidget
31933                 }
31934             ];
31935
31936             if(this.labelAlign == 'left'){
31937                 content = [
31938                     {
31939                         tag : 'div',
31940                         cls : 'column',
31941                         html : this.fieldLabel
31942                     },
31943                     {
31944                         tag : 'div',
31945                         cls : 'column',
31946                         cn : managerWidget
31947                     }
31948                 ];
31949                 
31950                 if(this.labelWidth > 12){
31951                     content[0].style = "width: " + this.labelWidth + 'px';
31952                 }
31953
31954                 if(this.labelWidth < 13 && this.labelmd == 0){
31955                     this.labelmd = this.labelWidth;
31956                 }
31957
31958                 if(this.labellg > 0){
31959                     content[0].cls += ' col-lg-' + this.labellg;
31960                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31961                 }
31962
31963                 if(this.labelmd > 0){
31964                     content[0].cls += ' col-md-' + this.labelmd;
31965                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31966                 }
31967
31968                 if(this.labelsm > 0){
31969                     content[0].cls += ' col-sm-' + this.labelsm;
31970                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31971                 }
31972
31973                 if(this.labelxs > 0){
31974                     content[0].cls += ' col-xs-' + this.labelxs;
31975                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31976                 }
31977                 
31978             }
31979         }
31980         
31981         var cfg = {
31982             tag : 'div',
31983             cls : 'row clearfix',
31984             cn : content
31985         };
31986         
31987         return cfg;
31988         
31989     },
31990     
31991     initEvents : function()
31992     {
31993         this.managerEl = this.el.select('.roo-document-manager', true).first();
31994         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31995         
31996         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31997         this.selectorEl.hide();
31998         
31999         if(this.multiple){
32000             this.selectorEl.attr('multiple', 'multiple');
32001         }
32002         
32003         this.selectorEl.on('change', this.onFileSelected, this);
32004         
32005         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32006         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32007         
32008         this.uploader.on('click', this.onUploaderClick, this);
32009         
32010         this.renderProgressDialog();
32011         
32012         var _this = this;
32013         
32014         window.addEventListener("resize", function() { _this.refresh(); } );
32015         
32016         this.fireEvent('initial', this);
32017     },
32018     
32019     renderProgressDialog : function()
32020     {
32021         var _this = this;
32022         
32023         this.progressDialog = new Roo.bootstrap.Modal({
32024             cls : 'roo-document-manager-progress-dialog',
32025             allow_close : false,
32026             animate : false,
32027             title : '',
32028             buttons : [
32029                 {
32030                     name  :'cancel',
32031                     weight : 'danger',
32032                     html : 'Cancel'
32033                 }
32034             ], 
32035             listeners : { 
32036                 btnclick : function() {
32037                     _this.uploadCancel();
32038                     this.hide();
32039                 }
32040             }
32041         });
32042          
32043         this.progressDialog.render(Roo.get(document.body));
32044          
32045         this.progress = new Roo.bootstrap.Progress({
32046             cls : 'roo-document-manager-progress',
32047             active : true,
32048             striped : true
32049         });
32050         
32051         this.progress.render(this.progressDialog.getChildContainer());
32052         
32053         this.progressBar = new Roo.bootstrap.ProgressBar({
32054             cls : 'roo-document-manager-progress-bar',
32055             aria_valuenow : 0,
32056             aria_valuemin : 0,
32057             aria_valuemax : 12,
32058             panel : 'success'
32059         });
32060         
32061         this.progressBar.render(this.progress.getChildContainer());
32062     },
32063     
32064     onUploaderClick : function(e)
32065     {
32066         e.preventDefault();
32067      
32068         if(this.fireEvent('beforeselectfile', this) != false){
32069             this.selectorEl.dom.click();
32070         }
32071         
32072     },
32073     
32074     onFileSelected : function(e)
32075     {
32076         e.preventDefault();
32077         
32078         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32079             return;
32080         }
32081         
32082         Roo.each(this.selectorEl.dom.files, function(file){
32083             if(this.fireEvent('inspect', this, file) != false){
32084                 this.files.push(file);
32085             }
32086         }, this);
32087         
32088         this.queue();
32089         
32090     },
32091     
32092     queue : function()
32093     {
32094         this.selectorEl.dom.value = '';
32095         
32096         if(!this.files || !this.files.length){
32097             return;
32098         }
32099         
32100         if(this.boxes > 0 && this.files.length > this.boxes){
32101             this.files = this.files.slice(0, this.boxes);
32102         }
32103         
32104         this.uploader.show();
32105         
32106         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32107             this.uploader.hide();
32108         }
32109         
32110         var _this = this;
32111         
32112         var files = [];
32113         
32114         var docs = [];
32115         
32116         Roo.each(this.files, function(file){
32117             
32118             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32119                 var f = this.renderPreview(file);
32120                 files.push(f);
32121                 return;
32122             }
32123             
32124             if(file.type.indexOf('image') != -1){
32125                 this.delegates.push(
32126                     (function(){
32127                         _this.process(file);
32128                     }).createDelegate(this)
32129                 );
32130         
32131                 return;
32132             }
32133             
32134             docs.push(
32135                 (function(){
32136                     _this.process(file);
32137                 }).createDelegate(this)
32138             );
32139             
32140         }, this);
32141         
32142         this.files = files;
32143         
32144         this.delegates = this.delegates.concat(docs);
32145         
32146         if(!this.delegates.length){
32147             this.refresh();
32148             return;
32149         }
32150         
32151         this.progressBar.aria_valuemax = this.delegates.length;
32152         
32153         this.arrange();
32154         
32155         return;
32156     },
32157     
32158     arrange : function()
32159     {
32160         if(!this.delegates.length){
32161             this.progressDialog.hide();
32162             this.refresh();
32163             return;
32164         }
32165         
32166         var delegate = this.delegates.shift();
32167         
32168         this.progressDialog.show();
32169         
32170         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32171         
32172         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32173         
32174         delegate();
32175     },
32176     
32177     refresh : function()
32178     {
32179         this.uploader.show();
32180         
32181         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32182             this.uploader.hide();
32183         }
32184         
32185         Roo.isTouch ? this.closable(false) : this.closable(true);
32186         
32187         this.fireEvent('refresh', this);
32188     },
32189     
32190     onRemove : function(e, el, o)
32191     {
32192         e.preventDefault();
32193         
32194         this.fireEvent('remove', this, o);
32195         
32196     },
32197     
32198     remove : function(o)
32199     {
32200         var files = [];
32201         
32202         Roo.each(this.files, function(file){
32203             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32204                 files.push(file);
32205                 return;
32206             }
32207
32208             o.target.remove();
32209
32210         }, this);
32211         
32212         this.files = files;
32213         
32214         this.refresh();
32215     },
32216     
32217     clear : function()
32218     {
32219         Roo.each(this.files, function(file){
32220             if(!file.target){
32221                 return;
32222             }
32223             
32224             file.target.remove();
32225
32226         }, this);
32227         
32228         this.files = [];
32229         
32230         this.refresh();
32231     },
32232     
32233     onClick : function(e, el, o)
32234     {
32235         e.preventDefault();
32236         
32237         this.fireEvent('click', this, o);
32238         
32239     },
32240     
32241     closable : function(closable)
32242     {
32243         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32244             
32245             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32246             
32247             if(closable){
32248                 el.show();
32249                 return;
32250             }
32251             
32252             el.hide();
32253             
32254         }, this);
32255     },
32256     
32257     xhrOnLoad : function(xhr)
32258     {
32259         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32260             el.remove();
32261         }, this);
32262         
32263         if (xhr.readyState !== 4) {
32264             this.arrange();
32265             this.fireEvent('exception', this, xhr);
32266             return;
32267         }
32268
32269         var response = Roo.decode(xhr.responseText);
32270         
32271         if(!response.success){
32272             this.arrange();
32273             this.fireEvent('exception', this, xhr);
32274             return;
32275         }
32276         
32277         var file = this.renderPreview(response.data);
32278         
32279         this.files.push(file);
32280         
32281         this.arrange();
32282         
32283         this.fireEvent('afterupload', this, xhr);
32284         
32285     },
32286     
32287     xhrOnError : function(xhr)
32288     {
32289         Roo.log('xhr on error');
32290         
32291         var response = Roo.decode(xhr.responseText);
32292           
32293         Roo.log(response);
32294         
32295         this.arrange();
32296     },
32297     
32298     process : function(file)
32299     {
32300         if(this.fireEvent('process', this, file) !== false){
32301             if(this.editable && file.type.indexOf('image') != -1){
32302                 this.fireEvent('edit', this, file);
32303                 return;
32304             }
32305
32306             this.uploadStart(file, false);
32307
32308             return;
32309         }
32310         
32311     },
32312     
32313     uploadStart : function(file, crop)
32314     {
32315         this.xhr = new XMLHttpRequest();
32316         
32317         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32318             this.arrange();
32319             return;
32320         }
32321         
32322         file.xhr = this.xhr;
32323             
32324         this.managerEl.createChild({
32325             tag : 'div',
32326             cls : 'roo-document-manager-loading',
32327             cn : [
32328                 {
32329                     tag : 'div',
32330                     tooltip : file.name,
32331                     cls : 'roo-document-manager-thumb',
32332                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32333                 }
32334             ]
32335
32336         });
32337
32338         this.xhr.open(this.method, this.url, true);
32339         
32340         var headers = {
32341             "Accept": "application/json",
32342             "Cache-Control": "no-cache",
32343             "X-Requested-With": "XMLHttpRequest"
32344         };
32345         
32346         for (var headerName in headers) {
32347             var headerValue = headers[headerName];
32348             if (headerValue) {
32349                 this.xhr.setRequestHeader(headerName, headerValue);
32350             }
32351         }
32352         
32353         var _this = this;
32354         
32355         this.xhr.onload = function()
32356         {
32357             _this.xhrOnLoad(_this.xhr);
32358         }
32359         
32360         this.xhr.onerror = function()
32361         {
32362             _this.xhrOnError(_this.xhr);
32363         }
32364         
32365         var formData = new FormData();
32366
32367         formData.append('returnHTML', 'NO');
32368         
32369         if(crop){
32370             formData.append('crop', crop);
32371         }
32372         
32373         formData.append(this.paramName, file, file.name);
32374         
32375         var options = {
32376             file : file, 
32377             manually : false
32378         };
32379         
32380         if(this.fireEvent('prepare', this, formData, options) != false){
32381             
32382             if(options.manually){
32383                 return;
32384             }
32385             
32386             this.xhr.send(formData);
32387             return;
32388         };
32389         
32390         this.uploadCancel();
32391     },
32392     
32393     uploadCancel : function()
32394     {
32395         if (this.xhr) {
32396             this.xhr.abort();
32397         }
32398         
32399         this.delegates = [];
32400         
32401         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32402             el.remove();
32403         }, this);
32404         
32405         this.arrange();
32406     },
32407     
32408     renderPreview : function(file)
32409     {
32410         if(typeof(file.target) != 'undefined' && file.target){
32411             return file;
32412         }
32413         
32414         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32415         
32416         var previewEl = this.managerEl.createChild({
32417             tag : 'div',
32418             cls : 'roo-document-manager-preview',
32419             cn : [
32420                 {
32421                     tag : 'div',
32422                     tooltip : file[this.toolTipName],
32423                     cls : 'roo-document-manager-thumb',
32424                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32425                 },
32426                 {
32427                     tag : 'button',
32428                     cls : 'close',
32429                     html : '<i class="fa fa-times-circle"></i>'
32430                 }
32431             ]
32432         });
32433
32434         var close = previewEl.select('button.close', true).first();
32435
32436         close.on('click', this.onRemove, this, file);
32437
32438         file.target = previewEl;
32439
32440         var image = previewEl.select('img', true).first();
32441         
32442         var _this = this;
32443         
32444         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32445         
32446         image.on('click', this.onClick, this, file);
32447         
32448         this.fireEvent('previewrendered', this, file);
32449         
32450         return file;
32451         
32452     },
32453     
32454     onPreviewLoad : function(file, image)
32455     {
32456         if(typeof(file.target) == 'undefined' || !file.target){
32457             return;
32458         }
32459         
32460         var width = image.dom.naturalWidth || image.dom.width;
32461         var height = image.dom.naturalHeight || image.dom.height;
32462         
32463         if(!this.previewResize) {
32464             return;
32465         }
32466         
32467         if(width > height){
32468             file.target.addClass('wide');
32469             return;
32470         }
32471         
32472         file.target.addClass('tall');
32473         return;
32474         
32475     },
32476     
32477     uploadFromSource : function(file, crop)
32478     {
32479         this.xhr = new XMLHttpRequest();
32480         
32481         this.managerEl.createChild({
32482             tag : 'div',
32483             cls : 'roo-document-manager-loading',
32484             cn : [
32485                 {
32486                     tag : 'div',
32487                     tooltip : file.name,
32488                     cls : 'roo-document-manager-thumb',
32489                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32490                 }
32491             ]
32492
32493         });
32494
32495         this.xhr.open(this.method, this.url, true);
32496         
32497         var headers = {
32498             "Accept": "application/json",
32499             "Cache-Control": "no-cache",
32500             "X-Requested-With": "XMLHttpRequest"
32501         };
32502         
32503         for (var headerName in headers) {
32504             var headerValue = headers[headerName];
32505             if (headerValue) {
32506                 this.xhr.setRequestHeader(headerName, headerValue);
32507             }
32508         }
32509         
32510         var _this = this;
32511         
32512         this.xhr.onload = function()
32513         {
32514             _this.xhrOnLoad(_this.xhr);
32515         }
32516         
32517         this.xhr.onerror = function()
32518         {
32519             _this.xhrOnError(_this.xhr);
32520         }
32521         
32522         var formData = new FormData();
32523
32524         formData.append('returnHTML', 'NO');
32525         
32526         formData.append('crop', crop);
32527         
32528         if(typeof(file.filename) != 'undefined'){
32529             formData.append('filename', file.filename);
32530         }
32531         
32532         if(typeof(file.mimetype) != 'undefined'){
32533             formData.append('mimetype', file.mimetype);
32534         }
32535         
32536         Roo.log(formData);
32537         
32538         if(this.fireEvent('prepare', this, formData) != false){
32539             this.xhr.send(formData);
32540         };
32541     }
32542 });
32543
32544 /*
32545 * Licence: LGPL
32546 */
32547
32548 /**
32549  * @class Roo.bootstrap.DocumentViewer
32550  * @extends Roo.bootstrap.Component
32551  * Bootstrap DocumentViewer class
32552  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32553  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32554  * 
32555  * @constructor
32556  * Create a new DocumentViewer
32557  * @param {Object} config The config object
32558  */
32559
32560 Roo.bootstrap.DocumentViewer = function(config){
32561     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32562     
32563     this.addEvents({
32564         /**
32565          * @event initial
32566          * Fire after initEvent
32567          * @param {Roo.bootstrap.DocumentViewer} this
32568          */
32569         "initial" : true,
32570         /**
32571          * @event click
32572          * Fire after click
32573          * @param {Roo.bootstrap.DocumentViewer} this
32574          */
32575         "click" : true,
32576         /**
32577          * @event download
32578          * Fire after download button
32579          * @param {Roo.bootstrap.DocumentViewer} this
32580          */
32581         "download" : true,
32582         /**
32583          * @event trash
32584          * Fire after trash button
32585          * @param {Roo.bootstrap.DocumentViewer} this
32586          */
32587         "trash" : true
32588         
32589     });
32590 };
32591
32592 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32593     
32594     showDownload : true,
32595     
32596     showTrash : true,
32597     
32598     getAutoCreate : function()
32599     {
32600         var cfg = {
32601             tag : 'div',
32602             cls : 'roo-document-viewer',
32603             cn : [
32604                 {
32605                     tag : 'div',
32606                     cls : 'roo-document-viewer-body',
32607                     cn : [
32608                         {
32609                             tag : 'div',
32610                             cls : 'roo-document-viewer-thumb',
32611                             cn : [
32612                                 {
32613                                     tag : 'img',
32614                                     cls : 'roo-document-viewer-image'
32615                                 }
32616                             ]
32617                         }
32618                     ]
32619                 },
32620                 {
32621                     tag : 'div',
32622                     cls : 'roo-document-viewer-footer',
32623                     cn : {
32624                         tag : 'div',
32625                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32626                         cn : [
32627                             {
32628                                 tag : 'div',
32629                                 cls : 'btn-group roo-document-viewer-download',
32630                                 cn : [
32631                                     {
32632                                         tag : 'button',
32633                                         cls : 'btn btn-default',
32634                                         html : '<i class="fa fa-download"></i>'
32635                                     }
32636                                 ]
32637                             },
32638                             {
32639                                 tag : 'div',
32640                                 cls : 'btn-group roo-document-viewer-trash',
32641                                 cn : [
32642                                     {
32643                                         tag : 'button',
32644                                         cls : 'btn btn-default',
32645                                         html : '<i class="fa fa-trash"></i>'
32646                                     }
32647                                 ]
32648                             }
32649                         ]
32650                     }
32651                 }
32652             ]
32653         };
32654         
32655         return cfg;
32656     },
32657     
32658     initEvents : function()
32659     {
32660         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32661         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32662         
32663         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32664         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32665         
32666         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32667         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32668         
32669         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32670         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32671         
32672         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32673         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32674         
32675         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32676         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32677         
32678         this.bodyEl.on('click', this.onClick, this);
32679         this.downloadBtn.on('click', this.onDownload, this);
32680         this.trashBtn.on('click', this.onTrash, this);
32681         
32682         this.downloadBtn.hide();
32683         this.trashBtn.hide();
32684         
32685         if(this.showDownload){
32686             this.downloadBtn.show();
32687         }
32688         
32689         if(this.showTrash){
32690             this.trashBtn.show();
32691         }
32692         
32693         if(!this.showDownload && !this.showTrash) {
32694             this.footerEl.hide();
32695         }
32696         
32697     },
32698     
32699     initial : function()
32700     {
32701         this.fireEvent('initial', this);
32702         
32703     },
32704     
32705     onClick : function(e)
32706     {
32707         e.preventDefault();
32708         
32709         this.fireEvent('click', this);
32710     },
32711     
32712     onDownload : function(e)
32713     {
32714         e.preventDefault();
32715         
32716         this.fireEvent('download', this);
32717     },
32718     
32719     onTrash : function(e)
32720     {
32721         e.preventDefault();
32722         
32723         this.fireEvent('trash', this);
32724     }
32725     
32726 });
32727 /*
32728  * - LGPL
32729  *
32730  * nav progress bar
32731  * 
32732  */
32733
32734 /**
32735  * @class Roo.bootstrap.NavProgressBar
32736  * @extends Roo.bootstrap.Component
32737  * Bootstrap NavProgressBar class
32738  * 
32739  * @constructor
32740  * Create a new nav progress bar
32741  * @param {Object} config The config object
32742  */
32743
32744 Roo.bootstrap.NavProgressBar = function(config){
32745     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32746
32747     this.bullets = this.bullets || [];
32748    
32749 //    Roo.bootstrap.NavProgressBar.register(this);
32750      this.addEvents({
32751         /**
32752              * @event changed
32753              * Fires when the active item changes
32754              * @param {Roo.bootstrap.NavProgressBar} this
32755              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32756              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32757          */
32758         'changed': true
32759      });
32760     
32761 };
32762
32763 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32764     
32765     bullets : [],
32766     barItems : [],
32767     
32768     getAutoCreate : function()
32769     {
32770         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32771         
32772         cfg = {
32773             tag : 'div',
32774             cls : 'roo-navigation-bar-group',
32775             cn : [
32776                 {
32777                     tag : 'div',
32778                     cls : 'roo-navigation-top-bar'
32779                 },
32780                 {
32781                     tag : 'div',
32782                     cls : 'roo-navigation-bullets-bar',
32783                     cn : [
32784                         {
32785                             tag : 'ul',
32786                             cls : 'roo-navigation-bar'
32787                         }
32788                     ]
32789                 },
32790                 
32791                 {
32792                     tag : 'div',
32793                     cls : 'roo-navigation-bottom-bar'
32794                 }
32795             ]
32796             
32797         };
32798         
32799         return cfg;
32800         
32801     },
32802     
32803     initEvents: function() 
32804     {
32805         
32806     },
32807     
32808     onRender : function(ct, position) 
32809     {
32810         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32811         
32812         if(this.bullets.length){
32813             Roo.each(this.bullets, function(b){
32814                this.addItem(b);
32815             }, this);
32816         }
32817         
32818         this.format();
32819         
32820     },
32821     
32822     addItem : function(cfg)
32823     {
32824         var item = new Roo.bootstrap.NavProgressItem(cfg);
32825         
32826         item.parentId = this.id;
32827         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32828         
32829         if(cfg.html){
32830             var top = new Roo.bootstrap.Element({
32831                 tag : 'div',
32832                 cls : 'roo-navigation-bar-text'
32833             });
32834             
32835             var bottom = new Roo.bootstrap.Element({
32836                 tag : 'div',
32837                 cls : 'roo-navigation-bar-text'
32838             });
32839             
32840             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32841             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32842             
32843             var topText = new Roo.bootstrap.Element({
32844                 tag : 'span',
32845                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32846             });
32847             
32848             var bottomText = new Roo.bootstrap.Element({
32849                 tag : 'span',
32850                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32851             });
32852             
32853             topText.onRender(top.el, null);
32854             bottomText.onRender(bottom.el, null);
32855             
32856             item.topEl = top;
32857             item.bottomEl = bottom;
32858         }
32859         
32860         this.barItems.push(item);
32861         
32862         return item;
32863     },
32864     
32865     getActive : function()
32866     {
32867         var active = false;
32868         
32869         Roo.each(this.barItems, function(v){
32870             
32871             if (!v.isActive()) {
32872                 return;
32873             }
32874             
32875             active = v;
32876             return false;
32877             
32878         });
32879         
32880         return active;
32881     },
32882     
32883     setActiveItem : function(item)
32884     {
32885         var prev = false;
32886         
32887         Roo.each(this.barItems, function(v){
32888             if (v.rid == item.rid) {
32889                 return ;
32890             }
32891             
32892             if (v.isActive()) {
32893                 v.setActive(false);
32894                 prev = v;
32895             }
32896         });
32897
32898         item.setActive(true);
32899         
32900         this.fireEvent('changed', this, item, prev);
32901     },
32902     
32903     getBarItem: function(rid)
32904     {
32905         var ret = false;
32906         
32907         Roo.each(this.barItems, function(e) {
32908             if (e.rid != rid) {
32909                 return;
32910             }
32911             
32912             ret =  e;
32913             return false;
32914         });
32915         
32916         return ret;
32917     },
32918     
32919     indexOfItem : function(item)
32920     {
32921         var index = false;
32922         
32923         Roo.each(this.barItems, function(v, i){
32924             
32925             if (v.rid != item.rid) {
32926                 return;
32927             }
32928             
32929             index = i;
32930             return false
32931         });
32932         
32933         return index;
32934     },
32935     
32936     setActiveNext : function()
32937     {
32938         var i = this.indexOfItem(this.getActive());
32939         
32940         if (i > this.barItems.length) {
32941             return;
32942         }
32943         
32944         this.setActiveItem(this.barItems[i+1]);
32945     },
32946     
32947     setActivePrev : function()
32948     {
32949         var i = this.indexOfItem(this.getActive());
32950         
32951         if (i  < 1) {
32952             return;
32953         }
32954         
32955         this.setActiveItem(this.barItems[i-1]);
32956     },
32957     
32958     format : function()
32959     {
32960         if(!this.barItems.length){
32961             return;
32962         }
32963      
32964         var width = 100 / this.barItems.length;
32965         
32966         Roo.each(this.barItems, function(i){
32967             i.el.setStyle('width', width + '%');
32968             i.topEl.el.setStyle('width', width + '%');
32969             i.bottomEl.el.setStyle('width', width + '%');
32970         }, this);
32971         
32972     }
32973     
32974 });
32975 /*
32976  * - LGPL
32977  *
32978  * Nav Progress Item
32979  * 
32980  */
32981
32982 /**
32983  * @class Roo.bootstrap.NavProgressItem
32984  * @extends Roo.bootstrap.Component
32985  * Bootstrap NavProgressItem class
32986  * @cfg {String} rid the reference id
32987  * @cfg {Boolean} active (true|false) Is item active default false
32988  * @cfg {Boolean} disabled (true|false) Is item active default false
32989  * @cfg {String} html
32990  * @cfg {String} position (top|bottom) text position default bottom
32991  * @cfg {String} icon show icon instead of number
32992  * 
32993  * @constructor
32994  * Create a new NavProgressItem
32995  * @param {Object} config The config object
32996  */
32997 Roo.bootstrap.NavProgressItem = function(config){
32998     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32999     this.addEvents({
33000         // raw events
33001         /**
33002          * @event click
33003          * The raw click event for the entire grid.
33004          * @param {Roo.bootstrap.NavProgressItem} this
33005          * @param {Roo.EventObject} e
33006          */
33007         "click" : true
33008     });
33009    
33010 };
33011
33012 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33013     
33014     rid : '',
33015     active : false,
33016     disabled : false,
33017     html : '',
33018     position : 'bottom',
33019     icon : false,
33020     
33021     getAutoCreate : function()
33022     {
33023         var iconCls = 'roo-navigation-bar-item-icon';
33024         
33025         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33026         
33027         var cfg = {
33028             tag: 'li',
33029             cls: 'roo-navigation-bar-item',
33030             cn : [
33031                 {
33032                     tag : 'i',
33033                     cls : iconCls
33034                 }
33035             ]
33036         };
33037         
33038         if(this.active){
33039             cfg.cls += ' active';
33040         }
33041         if(this.disabled){
33042             cfg.cls += ' disabled';
33043         }
33044         
33045         return cfg;
33046     },
33047     
33048     disable : function()
33049     {
33050         this.setDisabled(true);
33051     },
33052     
33053     enable : function()
33054     {
33055         this.setDisabled(false);
33056     },
33057     
33058     initEvents: function() 
33059     {
33060         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33061         
33062         this.iconEl.on('click', this.onClick, this);
33063     },
33064     
33065     onClick : function(e)
33066     {
33067         e.preventDefault();
33068         
33069         if(this.disabled){
33070             return;
33071         }
33072         
33073         if(this.fireEvent('click', this, e) === false){
33074             return;
33075         };
33076         
33077         this.parent().setActiveItem(this);
33078     },
33079     
33080     isActive: function () 
33081     {
33082         return this.active;
33083     },
33084     
33085     setActive : function(state)
33086     {
33087         if(this.active == state){
33088             return;
33089         }
33090         
33091         this.active = state;
33092         
33093         if (state) {
33094             this.el.addClass('active');
33095             return;
33096         }
33097         
33098         this.el.removeClass('active');
33099         
33100         return;
33101     },
33102     
33103     setDisabled : function(state)
33104     {
33105         if(this.disabled == state){
33106             return;
33107         }
33108         
33109         this.disabled = state;
33110         
33111         if (state) {
33112             this.el.addClass('disabled');
33113             return;
33114         }
33115         
33116         this.el.removeClass('disabled');
33117     },
33118     
33119     tooltipEl : function()
33120     {
33121         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33122     }
33123 });
33124  
33125
33126  /*
33127  * - LGPL
33128  *
33129  * FieldLabel
33130  * 
33131  */
33132
33133 /**
33134  * @class Roo.bootstrap.FieldLabel
33135  * @extends Roo.bootstrap.Component
33136  * Bootstrap FieldLabel class
33137  * @cfg {String} html contents of the element
33138  * @cfg {String} tag tag of the element default label
33139  * @cfg {String} cls class of the element
33140  * @cfg {String} target label target 
33141  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33142  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33143  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33144  * @cfg {String} iconTooltip default "This field is required"
33145  * @cfg {String} indicatorpos (left|right) default left
33146  * 
33147  * @constructor
33148  * Create a new FieldLabel
33149  * @param {Object} config The config object
33150  */
33151
33152 Roo.bootstrap.FieldLabel = function(config){
33153     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33154     
33155     this.addEvents({
33156             /**
33157              * @event invalid
33158              * Fires after the field has been marked as invalid.
33159              * @param {Roo.form.FieldLabel} this
33160              * @param {String} msg The validation message
33161              */
33162             invalid : true,
33163             /**
33164              * @event valid
33165              * Fires after the field has been validated with no errors.
33166              * @param {Roo.form.FieldLabel} this
33167              */
33168             valid : true
33169         });
33170 };
33171
33172 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33173     
33174     tag: 'label',
33175     cls: '',
33176     html: '',
33177     target: '',
33178     allowBlank : true,
33179     invalidClass : 'has-warning',
33180     validClass : 'has-success',
33181     iconTooltip : 'This field is required',
33182     indicatorpos : 'left',
33183     
33184     getAutoCreate : function(){
33185         
33186         var cls = "";
33187         if (!this.allowBlank) {
33188             cls  = "visible";
33189         }
33190         
33191         var cfg = {
33192             tag : this.tag,
33193             cls : 'roo-bootstrap-field-label ' + this.cls,
33194             for : this.target,
33195             cn : [
33196                 {
33197                     tag : 'i',
33198                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33199                     tooltip : this.iconTooltip
33200                 },
33201                 {
33202                     tag : 'span',
33203                     html : this.html
33204                 }
33205             ] 
33206         };
33207         
33208         if(this.indicatorpos == 'right'){
33209             var cfg = {
33210                 tag : this.tag,
33211                 cls : 'roo-bootstrap-field-label ' + this.cls,
33212                 for : this.target,
33213                 cn : [
33214                     {
33215                         tag : 'span',
33216                         html : this.html
33217                     },
33218                     {
33219                         tag : 'i',
33220                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33221                         tooltip : this.iconTooltip
33222                     }
33223                 ] 
33224             };
33225         }
33226         
33227         return cfg;
33228     },
33229     
33230     initEvents: function() 
33231     {
33232         Roo.bootstrap.Element.superclass.initEvents.call(this);
33233         
33234         this.indicator = this.indicatorEl();
33235         
33236         if(this.indicator){
33237             this.indicator.removeClass('visible');
33238             this.indicator.addClass('invisible');
33239         }
33240         
33241         Roo.bootstrap.FieldLabel.register(this);
33242     },
33243     
33244     indicatorEl : function()
33245     {
33246         var indicator = this.el.select('i.roo-required-indicator',true).first();
33247         
33248         if(!indicator){
33249             return false;
33250         }
33251         
33252         return indicator;
33253         
33254     },
33255     
33256     /**
33257      * Mark this field as valid
33258      */
33259     markValid : function()
33260     {
33261         if(this.indicator){
33262             this.indicator.removeClass('visible');
33263             this.indicator.addClass('invisible');
33264         }
33265         if (Roo.bootstrap.version == 3) {
33266             this.el.removeClass(this.invalidClass);
33267             this.el.addClass(this.validClass);
33268         } else {
33269             this.el.removeClass('is-invalid');
33270             this.el.addClass('is-valid');
33271         }
33272         
33273         
33274         this.fireEvent('valid', this);
33275     },
33276     
33277     /**
33278      * Mark this field as invalid
33279      * @param {String} msg The validation message
33280      */
33281     markInvalid : function(msg)
33282     {
33283         if(this.indicator){
33284             this.indicator.removeClass('invisible');
33285             this.indicator.addClass('visible');
33286         }
33287           if (Roo.bootstrap.version == 3) {
33288             this.el.removeClass(this.validClass);
33289             this.el.addClass(this.invalidClass);
33290         } else {
33291             this.el.removeClass('is-valid');
33292             this.el.addClass('is-invalid');
33293         }
33294         
33295         
33296         this.fireEvent('invalid', this, msg);
33297     }
33298     
33299    
33300 });
33301
33302 Roo.apply(Roo.bootstrap.FieldLabel, {
33303     
33304     groups: {},
33305     
33306      /**
33307     * register a FieldLabel Group
33308     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33309     */
33310     register : function(label)
33311     {
33312         if(this.groups.hasOwnProperty(label.target)){
33313             return;
33314         }
33315      
33316         this.groups[label.target] = label;
33317         
33318     },
33319     /**
33320     * fetch a FieldLabel Group based on the target
33321     * @param {string} target
33322     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33323     */
33324     get: function(target) {
33325         if (typeof(this.groups[target]) == 'undefined') {
33326             return false;
33327         }
33328         
33329         return this.groups[target] ;
33330     }
33331 });
33332
33333  
33334
33335  /*
33336  * - LGPL
33337  *
33338  * page DateSplitField.
33339  * 
33340  */
33341
33342
33343 /**
33344  * @class Roo.bootstrap.DateSplitField
33345  * @extends Roo.bootstrap.Component
33346  * Bootstrap DateSplitField class
33347  * @cfg {string} fieldLabel - the label associated
33348  * @cfg {Number} labelWidth set the width of label (0-12)
33349  * @cfg {String} labelAlign (top|left)
33350  * @cfg {Boolean} dayAllowBlank (true|false) default false
33351  * @cfg {Boolean} monthAllowBlank (true|false) default false
33352  * @cfg {Boolean} yearAllowBlank (true|false) default false
33353  * @cfg {string} dayPlaceholder 
33354  * @cfg {string} monthPlaceholder
33355  * @cfg {string} yearPlaceholder
33356  * @cfg {string} dayFormat default 'd'
33357  * @cfg {string} monthFormat default 'm'
33358  * @cfg {string} yearFormat default 'Y'
33359  * @cfg {Number} labellg set the width of label (1-12)
33360  * @cfg {Number} labelmd set the width of label (1-12)
33361  * @cfg {Number} labelsm set the width of label (1-12)
33362  * @cfg {Number} labelxs set the width of label (1-12)
33363
33364  *     
33365  * @constructor
33366  * Create a new DateSplitField
33367  * @param {Object} config The config object
33368  */
33369
33370 Roo.bootstrap.DateSplitField = function(config){
33371     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33372     
33373     this.addEvents({
33374         // raw events
33375          /**
33376          * @event years
33377          * getting the data of years
33378          * @param {Roo.bootstrap.DateSplitField} this
33379          * @param {Object} years
33380          */
33381         "years" : true,
33382         /**
33383          * @event days
33384          * getting the data of days
33385          * @param {Roo.bootstrap.DateSplitField} this
33386          * @param {Object} days
33387          */
33388         "days" : true,
33389         /**
33390          * @event invalid
33391          * Fires after the field has been marked as invalid.
33392          * @param {Roo.form.Field} this
33393          * @param {String} msg The validation message
33394          */
33395         invalid : true,
33396        /**
33397          * @event valid
33398          * Fires after the field has been validated with no errors.
33399          * @param {Roo.form.Field} this
33400          */
33401         valid : true
33402     });
33403 };
33404
33405 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33406     
33407     fieldLabel : '',
33408     labelAlign : 'top',
33409     labelWidth : 3,
33410     dayAllowBlank : false,
33411     monthAllowBlank : false,
33412     yearAllowBlank : false,
33413     dayPlaceholder : '',
33414     monthPlaceholder : '',
33415     yearPlaceholder : '',
33416     dayFormat : 'd',
33417     monthFormat : 'm',
33418     yearFormat : 'Y',
33419     isFormField : true,
33420     labellg : 0,
33421     labelmd : 0,
33422     labelsm : 0,
33423     labelxs : 0,
33424     
33425     getAutoCreate : function()
33426     {
33427         var cfg = {
33428             tag : 'div',
33429             cls : 'row roo-date-split-field-group',
33430             cn : [
33431                 {
33432                     tag : 'input',
33433                     type : 'hidden',
33434                     cls : 'form-hidden-field roo-date-split-field-group-value',
33435                     name : this.name
33436                 }
33437             ]
33438         };
33439         
33440         var labelCls = 'col-md-12';
33441         var contentCls = 'col-md-4';
33442         
33443         if(this.fieldLabel){
33444             
33445             var label = {
33446                 tag : 'div',
33447                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33448                 cn : [
33449                     {
33450                         tag : 'label',
33451                         html : this.fieldLabel
33452                     }
33453                 ]
33454             };
33455             
33456             if(this.labelAlign == 'left'){
33457             
33458                 if(this.labelWidth > 12){
33459                     label.style = "width: " + this.labelWidth + 'px';
33460                 }
33461
33462                 if(this.labelWidth < 13 && this.labelmd == 0){
33463                     this.labelmd = this.labelWidth;
33464                 }
33465
33466                 if(this.labellg > 0){
33467                     labelCls = ' col-lg-' + this.labellg;
33468                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33469                 }
33470
33471                 if(this.labelmd > 0){
33472                     labelCls = ' col-md-' + this.labelmd;
33473                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33474                 }
33475
33476                 if(this.labelsm > 0){
33477                     labelCls = ' col-sm-' + this.labelsm;
33478                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33479                 }
33480
33481                 if(this.labelxs > 0){
33482                     labelCls = ' col-xs-' + this.labelxs;
33483                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33484                 }
33485             }
33486             
33487             label.cls += ' ' + labelCls;
33488             
33489             cfg.cn.push(label);
33490         }
33491         
33492         Roo.each(['day', 'month', 'year'], function(t){
33493             cfg.cn.push({
33494                 tag : 'div',
33495                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33496             });
33497         }, this);
33498         
33499         return cfg;
33500     },
33501     
33502     inputEl: function ()
33503     {
33504         return this.el.select('.roo-date-split-field-group-value', true).first();
33505     },
33506     
33507     onRender : function(ct, position) 
33508     {
33509         var _this = this;
33510         
33511         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33512         
33513         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33514         
33515         this.dayField = new Roo.bootstrap.ComboBox({
33516             allowBlank : this.dayAllowBlank,
33517             alwaysQuery : true,
33518             displayField : 'value',
33519             editable : false,
33520             fieldLabel : '',
33521             forceSelection : true,
33522             mode : 'local',
33523             placeholder : this.dayPlaceholder,
33524             selectOnFocus : true,
33525             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33526             triggerAction : 'all',
33527             typeAhead : true,
33528             valueField : 'value',
33529             store : new Roo.data.SimpleStore({
33530                 data : (function() {    
33531                     var days = [];
33532                     _this.fireEvent('days', _this, days);
33533                     return days;
33534                 })(),
33535                 fields : [ 'value' ]
33536             }),
33537             listeners : {
33538                 select : function (_self, record, index)
33539                 {
33540                     _this.setValue(_this.getValue());
33541                 }
33542             }
33543         });
33544
33545         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33546         
33547         this.monthField = new Roo.bootstrap.MonthField({
33548             after : '<i class=\"fa fa-calendar\"></i>',
33549             allowBlank : this.monthAllowBlank,
33550             placeholder : this.monthPlaceholder,
33551             readOnly : true,
33552             listeners : {
33553                 render : function (_self)
33554                 {
33555                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33556                         e.preventDefault();
33557                         _self.focus();
33558                     });
33559                 },
33560                 select : function (_self, oldvalue, newvalue)
33561                 {
33562                     _this.setValue(_this.getValue());
33563                 }
33564             }
33565         });
33566         
33567         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33568         
33569         this.yearField = new Roo.bootstrap.ComboBox({
33570             allowBlank : this.yearAllowBlank,
33571             alwaysQuery : true,
33572             displayField : 'value',
33573             editable : false,
33574             fieldLabel : '',
33575             forceSelection : true,
33576             mode : 'local',
33577             placeholder : this.yearPlaceholder,
33578             selectOnFocus : true,
33579             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33580             triggerAction : 'all',
33581             typeAhead : true,
33582             valueField : 'value',
33583             store : new Roo.data.SimpleStore({
33584                 data : (function() {
33585                     var years = [];
33586                     _this.fireEvent('years', _this, years);
33587                     return years;
33588                 })(),
33589                 fields : [ 'value' ]
33590             }),
33591             listeners : {
33592                 select : function (_self, record, index)
33593                 {
33594                     _this.setValue(_this.getValue());
33595                 }
33596             }
33597         });
33598
33599         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33600     },
33601     
33602     setValue : function(v, format)
33603     {
33604         this.inputEl.dom.value = v;
33605         
33606         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33607         
33608         var d = Date.parseDate(v, f);
33609         
33610         if(!d){
33611             this.validate();
33612             return;
33613         }
33614         
33615         this.setDay(d.format(this.dayFormat));
33616         this.setMonth(d.format(this.monthFormat));
33617         this.setYear(d.format(this.yearFormat));
33618         
33619         this.validate();
33620         
33621         return;
33622     },
33623     
33624     setDay : function(v)
33625     {
33626         this.dayField.setValue(v);
33627         this.inputEl.dom.value = this.getValue();
33628         this.validate();
33629         return;
33630     },
33631     
33632     setMonth : function(v)
33633     {
33634         this.monthField.setValue(v, true);
33635         this.inputEl.dom.value = this.getValue();
33636         this.validate();
33637         return;
33638     },
33639     
33640     setYear : function(v)
33641     {
33642         this.yearField.setValue(v);
33643         this.inputEl.dom.value = this.getValue();
33644         this.validate();
33645         return;
33646     },
33647     
33648     getDay : function()
33649     {
33650         return this.dayField.getValue();
33651     },
33652     
33653     getMonth : function()
33654     {
33655         return this.monthField.getValue();
33656     },
33657     
33658     getYear : function()
33659     {
33660         return this.yearField.getValue();
33661     },
33662     
33663     getValue : function()
33664     {
33665         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33666         
33667         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33668         
33669         return date;
33670     },
33671     
33672     reset : function()
33673     {
33674         this.setDay('');
33675         this.setMonth('');
33676         this.setYear('');
33677         this.inputEl.dom.value = '';
33678         this.validate();
33679         return;
33680     },
33681     
33682     validate : function()
33683     {
33684         var d = this.dayField.validate();
33685         var m = this.monthField.validate();
33686         var y = this.yearField.validate();
33687         
33688         var valid = true;
33689         
33690         if(
33691                 (!this.dayAllowBlank && !d) ||
33692                 (!this.monthAllowBlank && !m) ||
33693                 (!this.yearAllowBlank && !y)
33694         ){
33695             valid = false;
33696         }
33697         
33698         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33699             return valid;
33700         }
33701         
33702         if(valid){
33703             this.markValid();
33704             return valid;
33705         }
33706         
33707         this.markInvalid();
33708         
33709         return valid;
33710     },
33711     
33712     markValid : function()
33713     {
33714         
33715         var label = this.el.select('label', true).first();
33716         var icon = this.el.select('i.fa-star', true).first();
33717
33718         if(label && icon){
33719             icon.remove();
33720         }
33721         
33722         this.fireEvent('valid', this);
33723     },
33724     
33725      /**
33726      * Mark this field as invalid
33727      * @param {String} msg The validation message
33728      */
33729     markInvalid : function(msg)
33730     {
33731         
33732         var label = this.el.select('label', true).first();
33733         var icon = this.el.select('i.fa-star', true).first();
33734
33735         if(label && !icon){
33736             this.el.select('.roo-date-split-field-label', true).createChild({
33737                 tag : 'i',
33738                 cls : 'text-danger fa fa-lg fa-star',
33739                 tooltip : 'This field is required',
33740                 style : 'margin-right:5px;'
33741             }, label, true);
33742         }
33743         
33744         this.fireEvent('invalid', this, msg);
33745     },
33746     
33747     clearInvalid : function()
33748     {
33749         var label = this.el.select('label', true).first();
33750         var icon = this.el.select('i.fa-star', true).first();
33751
33752         if(label && icon){
33753             icon.remove();
33754         }
33755         
33756         this.fireEvent('valid', this);
33757     },
33758     
33759     getName: function()
33760     {
33761         return this.name;
33762     }
33763     
33764 });
33765
33766  /**
33767  *
33768  * This is based on 
33769  * http://masonry.desandro.com
33770  *
33771  * The idea is to render all the bricks based on vertical width...
33772  *
33773  * The original code extends 'outlayer' - we might need to use that....
33774  * 
33775  */
33776
33777
33778 /**
33779  * @class Roo.bootstrap.LayoutMasonry
33780  * @extends Roo.bootstrap.Component
33781  * Bootstrap Layout Masonry class
33782  * 
33783  * @constructor
33784  * Create a new Element
33785  * @param {Object} config The config object
33786  */
33787
33788 Roo.bootstrap.LayoutMasonry = function(config){
33789     
33790     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33791     
33792     this.bricks = [];
33793     
33794     Roo.bootstrap.LayoutMasonry.register(this);
33795     
33796     this.addEvents({
33797         // raw events
33798         /**
33799          * @event layout
33800          * Fire after layout the items
33801          * @param {Roo.bootstrap.LayoutMasonry} this
33802          * @param {Roo.EventObject} e
33803          */
33804         "layout" : true
33805     });
33806     
33807 };
33808
33809 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33810     
33811     /**
33812      * @cfg {Boolean} isLayoutInstant = no animation?
33813      */   
33814     isLayoutInstant : false, // needed?
33815    
33816     /**
33817      * @cfg {Number} boxWidth  width of the columns
33818      */   
33819     boxWidth : 450,
33820     
33821       /**
33822      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33823      */   
33824     boxHeight : 0,
33825     
33826     /**
33827      * @cfg {Number} padWidth padding below box..
33828      */   
33829     padWidth : 10, 
33830     
33831     /**
33832      * @cfg {Number} gutter gutter width..
33833      */   
33834     gutter : 10,
33835     
33836      /**
33837      * @cfg {Number} maxCols maximum number of columns
33838      */   
33839     
33840     maxCols: 0,
33841     
33842     /**
33843      * @cfg {Boolean} isAutoInitial defalut true
33844      */   
33845     isAutoInitial : true, 
33846     
33847     containerWidth: 0,
33848     
33849     /**
33850      * @cfg {Boolean} isHorizontal defalut false
33851      */   
33852     isHorizontal : false, 
33853
33854     currentSize : null,
33855     
33856     tag: 'div',
33857     
33858     cls: '',
33859     
33860     bricks: null, //CompositeElement
33861     
33862     cols : 1,
33863     
33864     _isLayoutInited : false,
33865     
33866 //    isAlternative : false, // only use for vertical layout...
33867     
33868     /**
33869      * @cfg {Number} alternativePadWidth padding below box..
33870      */   
33871     alternativePadWidth : 50,
33872     
33873     selectedBrick : [],
33874     
33875     getAutoCreate : function(){
33876         
33877         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33878         
33879         var cfg = {
33880             tag: this.tag,
33881             cls: 'blog-masonary-wrapper ' + this.cls,
33882             cn : {
33883                 cls : 'mas-boxes masonary'
33884             }
33885         };
33886         
33887         return cfg;
33888     },
33889     
33890     getChildContainer: function( )
33891     {
33892         if (this.boxesEl) {
33893             return this.boxesEl;
33894         }
33895         
33896         this.boxesEl = this.el.select('.mas-boxes').first();
33897         
33898         return this.boxesEl;
33899     },
33900     
33901     
33902     initEvents : function()
33903     {
33904         var _this = this;
33905         
33906         if(this.isAutoInitial){
33907             Roo.log('hook children rendered');
33908             this.on('childrenrendered', function() {
33909                 Roo.log('children rendered');
33910                 _this.initial();
33911             } ,this);
33912         }
33913     },
33914     
33915     initial : function()
33916     {
33917         this.selectedBrick = [];
33918         
33919         this.currentSize = this.el.getBox(true);
33920         
33921         Roo.EventManager.onWindowResize(this.resize, this); 
33922
33923         if(!this.isAutoInitial){
33924             this.layout();
33925             return;
33926         }
33927         
33928         this.layout();
33929         
33930         return;
33931         //this.layout.defer(500,this);
33932         
33933     },
33934     
33935     resize : function()
33936     {
33937         var cs = this.el.getBox(true);
33938         
33939         if (
33940                 this.currentSize.width == cs.width && 
33941                 this.currentSize.x == cs.x && 
33942                 this.currentSize.height == cs.height && 
33943                 this.currentSize.y == cs.y 
33944         ) {
33945             Roo.log("no change in with or X or Y");
33946             return;
33947         }
33948         
33949         this.currentSize = cs;
33950         
33951         this.layout();
33952         
33953     },
33954     
33955     layout : function()
33956     {   
33957         this._resetLayout();
33958         
33959         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33960         
33961         this.layoutItems( isInstant );
33962       
33963         this._isLayoutInited = true;
33964         
33965         this.fireEvent('layout', this);
33966         
33967     },
33968     
33969     _resetLayout : function()
33970     {
33971         if(this.isHorizontal){
33972             this.horizontalMeasureColumns();
33973             return;
33974         }
33975         
33976         this.verticalMeasureColumns();
33977         
33978     },
33979     
33980     verticalMeasureColumns : function()
33981     {
33982         this.getContainerWidth();
33983         
33984 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33985 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33986 //            return;
33987 //        }
33988         
33989         var boxWidth = this.boxWidth + this.padWidth;
33990         
33991         if(this.containerWidth < this.boxWidth){
33992             boxWidth = this.containerWidth
33993         }
33994         
33995         var containerWidth = this.containerWidth;
33996         
33997         var cols = Math.floor(containerWidth / boxWidth);
33998         
33999         this.cols = Math.max( cols, 1 );
34000         
34001         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34002         
34003         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34004         
34005         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34006         
34007         this.colWidth = boxWidth + avail - this.padWidth;
34008         
34009         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34010         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34011     },
34012     
34013     horizontalMeasureColumns : function()
34014     {
34015         this.getContainerWidth();
34016         
34017         var boxWidth = this.boxWidth;
34018         
34019         if(this.containerWidth < boxWidth){
34020             boxWidth = this.containerWidth;
34021         }
34022         
34023         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34024         
34025         this.el.setHeight(boxWidth);
34026         
34027     },
34028     
34029     getContainerWidth : function()
34030     {
34031         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34032     },
34033     
34034     layoutItems : function( isInstant )
34035     {
34036         Roo.log(this.bricks);
34037         
34038         var items = Roo.apply([], this.bricks);
34039         
34040         if(this.isHorizontal){
34041             this._horizontalLayoutItems( items , isInstant );
34042             return;
34043         }
34044         
34045 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34046 //            this._verticalAlternativeLayoutItems( items , isInstant );
34047 //            return;
34048 //        }
34049         
34050         this._verticalLayoutItems( items , isInstant );
34051         
34052     },
34053     
34054     _verticalLayoutItems : function ( items , isInstant)
34055     {
34056         if ( !items || !items.length ) {
34057             return;
34058         }
34059         
34060         var standard = [
34061             ['xs', 'xs', 'xs', 'tall'],
34062             ['xs', 'xs', 'tall'],
34063             ['xs', 'xs', 'sm'],
34064             ['xs', 'xs', 'xs'],
34065             ['xs', 'tall'],
34066             ['xs', 'sm'],
34067             ['xs', 'xs'],
34068             ['xs'],
34069             
34070             ['sm', 'xs', 'xs'],
34071             ['sm', 'xs'],
34072             ['sm'],
34073             
34074             ['tall', 'xs', 'xs', 'xs'],
34075             ['tall', 'xs', 'xs'],
34076             ['tall', 'xs'],
34077             ['tall']
34078             
34079         ];
34080         
34081         var queue = [];
34082         
34083         var boxes = [];
34084         
34085         var box = [];
34086         
34087         Roo.each(items, function(item, k){
34088             
34089             switch (item.size) {
34090                 // these layouts take up a full box,
34091                 case 'md' :
34092                 case 'md-left' :
34093                 case 'md-right' :
34094                 case 'wide' :
34095                     
34096                     if(box.length){
34097                         boxes.push(box);
34098                         box = [];
34099                     }
34100                     
34101                     boxes.push([item]);
34102                     
34103                     break;
34104                     
34105                 case 'xs' :
34106                 case 'sm' :
34107                 case 'tall' :
34108                     
34109                     box.push(item);
34110                     
34111                     break;
34112                 default :
34113                     break;
34114                     
34115             }
34116             
34117         }, this);
34118         
34119         if(box.length){
34120             boxes.push(box);
34121             box = [];
34122         }
34123         
34124         var filterPattern = function(box, length)
34125         {
34126             if(!box.length){
34127                 return;
34128             }
34129             
34130             var match = false;
34131             
34132             var pattern = box.slice(0, length);
34133             
34134             var format = [];
34135             
34136             Roo.each(pattern, function(i){
34137                 format.push(i.size);
34138             }, this);
34139             
34140             Roo.each(standard, function(s){
34141                 
34142                 if(String(s) != String(format)){
34143                     return;
34144                 }
34145                 
34146                 match = true;
34147                 return false;
34148                 
34149             }, this);
34150             
34151             if(!match && length == 1){
34152                 return;
34153             }
34154             
34155             if(!match){
34156                 filterPattern(box, length - 1);
34157                 return;
34158             }
34159                 
34160             queue.push(pattern);
34161
34162             box = box.slice(length, box.length);
34163
34164             filterPattern(box, 4);
34165
34166             return;
34167             
34168         }
34169         
34170         Roo.each(boxes, function(box, k){
34171             
34172             if(!box.length){
34173                 return;
34174             }
34175             
34176             if(box.length == 1){
34177                 queue.push(box);
34178                 return;
34179             }
34180             
34181             filterPattern(box, 4);
34182             
34183         }, this);
34184         
34185         this._processVerticalLayoutQueue( queue, isInstant );
34186         
34187     },
34188     
34189 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34190 //    {
34191 //        if ( !items || !items.length ) {
34192 //            return;
34193 //        }
34194 //
34195 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34196 //        
34197 //    },
34198     
34199     _horizontalLayoutItems : function ( items , isInstant)
34200     {
34201         if ( !items || !items.length || items.length < 3) {
34202             return;
34203         }
34204         
34205         items.reverse();
34206         
34207         var eItems = items.slice(0, 3);
34208         
34209         items = items.slice(3, items.length);
34210         
34211         var standard = [
34212             ['xs', 'xs', 'xs', 'wide'],
34213             ['xs', 'xs', 'wide'],
34214             ['xs', 'xs', 'sm'],
34215             ['xs', 'xs', 'xs'],
34216             ['xs', 'wide'],
34217             ['xs', 'sm'],
34218             ['xs', 'xs'],
34219             ['xs'],
34220             
34221             ['sm', 'xs', 'xs'],
34222             ['sm', 'xs'],
34223             ['sm'],
34224             
34225             ['wide', 'xs', 'xs', 'xs'],
34226             ['wide', 'xs', 'xs'],
34227             ['wide', 'xs'],
34228             ['wide'],
34229             
34230             ['wide-thin']
34231         ];
34232         
34233         var queue = [];
34234         
34235         var boxes = [];
34236         
34237         var box = [];
34238         
34239         Roo.each(items, function(item, k){
34240             
34241             switch (item.size) {
34242                 case 'md' :
34243                 case 'md-left' :
34244                 case 'md-right' :
34245                 case 'tall' :
34246                     
34247                     if(box.length){
34248                         boxes.push(box);
34249                         box = [];
34250                     }
34251                     
34252                     boxes.push([item]);
34253                     
34254                     break;
34255                     
34256                 case 'xs' :
34257                 case 'sm' :
34258                 case 'wide' :
34259                 case 'wide-thin' :
34260                     
34261                     box.push(item);
34262                     
34263                     break;
34264                 default :
34265                     break;
34266                     
34267             }
34268             
34269         }, this);
34270         
34271         if(box.length){
34272             boxes.push(box);
34273             box = [];
34274         }
34275         
34276         var filterPattern = function(box, length)
34277         {
34278             if(!box.length){
34279                 return;
34280             }
34281             
34282             var match = false;
34283             
34284             var pattern = box.slice(0, length);
34285             
34286             var format = [];
34287             
34288             Roo.each(pattern, function(i){
34289                 format.push(i.size);
34290             }, this);
34291             
34292             Roo.each(standard, function(s){
34293                 
34294                 if(String(s) != String(format)){
34295                     return;
34296                 }
34297                 
34298                 match = true;
34299                 return false;
34300                 
34301             }, this);
34302             
34303             if(!match && length == 1){
34304                 return;
34305             }
34306             
34307             if(!match){
34308                 filterPattern(box, length - 1);
34309                 return;
34310             }
34311                 
34312             queue.push(pattern);
34313
34314             box = box.slice(length, box.length);
34315
34316             filterPattern(box, 4);
34317
34318             return;
34319             
34320         }
34321         
34322         Roo.each(boxes, function(box, k){
34323             
34324             if(!box.length){
34325                 return;
34326             }
34327             
34328             if(box.length == 1){
34329                 queue.push(box);
34330                 return;
34331             }
34332             
34333             filterPattern(box, 4);
34334             
34335         }, this);
34336         
34337         
34338         var prune = [];
34339         
34340         var pos = this.el.getBox(true);
34341         
34342         var minX = pos.x;
34343         
34344         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34345         
34346         var hit_end = false;
34347         
34348         Roo.each(queue, function(box){
34349             
34350             if(hit_end){
34351                 
34352                 Roo.each(box, function(b){
34353                 
34354                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34355                     b.el.hide();
34356
34357                 }, this);
34358
34359                 return;
34360             }
34361             
34362             var mx = 0;
34363             
34364             Roo.each(box, function(b){
34365                 
34366                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34367                 b.el.show();
34368
34369                 mx = Math.max(mx, b.x);
34370                 
34371             }, this);
34372             
34373             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34374             
34375             if(maxX < minX){
34376                 
34377                 Roo.each(box, function(b){
34378                 
34379                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34380                     b.el.hide();
34381                     
34382                 }, this);
34383                 
34384                 hit_end = true;
34385                 
34386                 return;
34387             }
34388             
34389             prune.push(box);
34390             
34391         }, this);
34392         
34393         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34394     },
34395     
34396     /** Sets position of item in DOM
34397     * @param {Element} item
34398     * @param {Number} x - horizontal position
34399     * @param {Number} y - vertical position
34400     * @param {Boolean} isInstant - disables transitions
34401     */
34402     _processVerticalLayoutQueue : function( queue, isInstant )
34403     {
34404         var pos = this.el.getBox(true);
34405         var x = pos.x;
34406         var y = pos.y;
34407         var maxY = [];
34408         
34409         for (var i = 0; i < this.cols; i++){
34410             maxY[i] = pos.y;
34411         }
34412         
34413         Roo.each(queue, function(box, k){
34414             
34415             var col = k % this.cols;
34416             
34417             Roo.each(box, function(b,kk){
34418                 
34419                 b.el.position('absolute');
34420                 
34421                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34422                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34423                 
34424                 if(b.size == 'md-left' || b.size == 'md-right'){
34425                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34426                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34427                 }
34428                 
34429                 b.el.setWidth(width);
34430                 b.el.setHeight(height);
34431                 // iframe?
34432                 b.el.select('iframe',true).setSize(width,height);
34433                 
34434             }, this);
34435             
34436             for (var i = 0; i < this.cols; i++){
34437                 
34438                 if(maxY[i] < maxY[col]){
34439                     col = i;
34440                     continue;
34441                 }
34442                 
34443                 col = Math.min(col, i);
34444                 
34445             }
34446             
34447             x = pos.x + col * (this.colWidth + this.padWidth);
34448             
34449             y = maxY[col];
34450             
34451             var positions = [];
34452             
34453             switch (box.length){
34454                 case 1 :
34455                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34456                     break;
34457                 case 2 :
34458                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34459                     break;
34460                 case 3 :
34461                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34462                     break;
34463                 case 4 :
34464                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34465                     break;
34466                 default :
34467                     break;
34468             }
34469             
34470             Roo.each(box, function(b,kk){
34471                 
34472                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34473                 
34474                 var sz = b.el.getSize();
34475                 
34476                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34477                 
34478             }, this);
34479             
34480         }, this);
34481         
34482         var mY = 0;
34483         
34484         for (var i = 0; i < this.cols; i++){
34485             mY = Math.max(mY, maxY[i]);
34486         }
34487         
34488         this.el.setHeight(mY - pos.y);
34489         
34490     },
34491     
34492 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34493 //    {
34494 //        var pos = this.el.getBox(true);
34495 //        var x = pos.x;
34496 //        var y = pos.y;
34497 //        var maxX = pos.right;
34498 //        
34499 //        var maxHeight = 0;
34500 //        
34501 //        Roo.each(items, function(item, k){
34502 //            
34503 //            var c = k % 2;
34504 //            
34505 //            item.el.position('absolute');
34506 //                
34507 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34508 //
34509 //            item.el.setWidth(width);
34510 //
34511 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34512 //
34513 //            item.el.setHeight(height);
34514 //            
34515 //            if(c == 0){
34516 //                item.el.setXY([x, y], isInstant ? false : true);
34517 //            } else {
34518 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34519 //            }
34520 //            
34521 //            y = y + height + this.alternativePadWidth;
34522 //            
34523 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34524 //            
34525 //        }, this);
34526 //        
34527 //        this.el.setHeight(maxHeight);
34528 //        
34529 //    },
34530     
34531     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34532     {
34533         var pos = this.el.getBox(true);
34534         
34535         var minX = pos.x;
34536         var minY = pos.y;
34537         
34538         var maxX = pos.right;
34539         
34540         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34541         
34542         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34543         
34544         Roo.each(queue, function(box, k){
34545             
34546             Roo.each(box, function(b, kk){
34547                 
34548                 b.el.position('absolute');
34549                 
34550                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34551                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34552                 
34553                 if(b.size == 'md-left' || b.size == 'md-right'){
34554                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34555                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34556                 }
34557                 
34558                 b.el.setWidth(width);
34559                 b.el.setHeight(height);
34560                 
34561             }, this);
34562             
34563             if(!box.length){
34564                 return;
34565             }
34566             
34567             var positions = [];
34568             
34569             switch (box.length){
34570                 case 1 :
34571                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34572                     break;
34573                 case 2 :
34574                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34575                     break;
34576                 case 3 :
34577                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34578                     break;
34579                 case 4 :
34580                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34581                     break;
34582                 default :
34583                     break;
34584             }
34585             
34586             Roo.each(box, function(b,kk){
34587                 
34588                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34589                 
34590                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34591                 
34592             }, this);
34593             
34594         }, this);
34595         
34596     },
34597     
34598     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34599     {
34600         Roo.each(eItems, function(b,k){
34601             
34602             b.size = (k == 0) ? 'sm' : 'xs';
34603             b.x = (k == 0) ? 2 : 1;
34604             b.y = (k == 0) ? 2 : 1;
34605             
34606             b.el.position('absolute');
34607             
34608             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34609                 
34610             b.el.setWidth(width);
34611             
34612             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34613             
34614             b.el.setHeight(height);
34615             
34616         }, this);
34617
34618         var positions = [];
34619         
34620         positions.push({
34621             x : maxX - this.unitWidth * 2 - this.gutter,
34622             y : minY
34623         });
34624         
34625         positions.push({
34626             x : maxX - this.unitWidth,
34627             y : minY + (this.unitWidth + this.gutter) * 2
34628         });
34629         
34630         positions.push({
34631             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34632             y : minY
34633         });
34634         
34635         Roo.each(eItems, function(b,k){
34636             
34637             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34638
34639         }, this);
34640         
34641     },
34642     
34643     getVerticalOneBoxColPositions : function(x, y, box)
34644     {
34645         var pos = [];
34646         
34647         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34648         
34649         if(box[0].size == 'md-left'){
34650             rand = 0;
34651         }
34652         
34653         if(box[0].size == 'md-right'){
34654             rand = 1;
34655         }
34656         
34657         pos.push({
34658             x : x + (this.unitWidth + this.gutter) * rand,
34659             y : y
34660         });
34661         
34662         return pos;
34663     },
34664     
34665     getVerticalTwoBoxColPositions : function(x, y, box)
34666     {
34667         var pos = [];
34668         
34669         if(box[0].size == 'xs'){
34670             
34671             pos.push({
34672                 x : x,
34673                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34674             });
34675
34676             pos.push({
34677                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34678                 y : y
34679             });
34680             
34681             return pos;
34682             
34683         }
34684         
34685         pos.push({
34686             x : x,
34687             y : y
34688         });
34689
34690         pos.push({
34691             x : x + (this.unitWidth + this.gutter) * 2,
34692             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34693         });
34694         
34695         return pos;
34696         
34697     },
34698     
34699     getVerticalThreeBoxColPositions : function(x, y, box)
34700     {
34701         var pos = [];
34702         
34703         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34704             
34705             pos.push({
34706                 x : x,
34707                 y : y
34708             });
34709
34710             pos.push({
34711                 x : x + (this.unitWidth + this.gutter) * 1,
34712                 y : y
34713             });
34714             
34715             pos.push({
34716                 x : x + (this.unitWidth + this.gutter) * 2,
34717                 y : y
34718             });
34719             
34720             return pos;
34721             
34722         }
34723         
34724         if(box[0].size == 'xs' && box[1].size == 'xs'){
34725             
34726             pos.push({
34727                 x : x,
34728                 y : y
34729             });
34730
34731             pos.push({
34732                 x : x,
34733                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34734             });
34735             
34736             pos.push({
34737                 x : x + (this.unitWidth + this.gutter) * 1,
34738                 y : y
34739             });
34740             
34741             return pos;
34742             
34743         }
34744         
34745         pos.push({
34746             x : x,
34747             y : y
34748         });
34749
34750         pos.push({
34751             x : x + (this.unitWidth + this.gutter) * 2,
34752             y : y
34753         });
34754
34755         pos.push({
34756             x : x + (this.unitWidth + this.gutter) * 2,
34757             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34758         });
34759             
34760         return pos;
34761         
34762     },
34763     
34764     getVerticalFourBoxColPositions : function(x, y, box)
34765     {
34766         var pos = [];
34767         
34768         if(box[0].size == 'xs'){
34769             
34770             pos.push({
34771                 x : x,
34772                 y : y
34773             });
34774
34775             pos.push({
34776                 x : x,
34777                 y : y + (this.unitHeight + this.gutter) * 1
34778             });
34779             
34780             pos.push({
34781                 x : x,
34782                 y : y + (this.unitHeight + this.gutter) * 2
34783             });
34784             
34785             pos.push({
34786                 x : x + (this.unitWidth + this.gutter) * 1,
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
34802         });
34803
34804         pos.push({
34805             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34806             y : y + (this.unitHeight + this.gutter) * 1
34807         });
34808
34809         pos.push({
34810             x : x + (this.unitWidth + this.gutter) * 2,
34811             y : y + (this.unitWidth + this.gutter) * 2
34812         });
34813
34814         return pos;
34815         
34816     },
34817     
34818     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34819     {
34820         var pos = [];
34821         
34822         if(box[0].size == 'md-left'){
34823             pos.push({
34824                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34825                 y : minY
34826             });
34827             
34828             return pos;
34829         }
34830         
34831         if(box[0].size == 'md-right'){
34832             pos.push({
34833                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34834                 y : minY + (this.unitWidth + this.gutter) * 1
34835             });
34836             
34837             return pos;
34838         }
34839         
34840         var rand = Math.floor(Math.random() * (4 - box[0].y));
34841         
34842         pos.push({
34843             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34844             y : minY + (this.unitWidth + this.gutter) * rand
34845         });
34846         
34847         return pos;
34848         
34849     },
34850     
34851     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34852     {
34853         var pos = [];
34854         
34855         if(box[0].size == 'xs'){
34856             
34857             pos.push({
34858                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34859                 y : minY
34860             });
34861
34862             pos.push({
34863                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34864                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34865             });
34866             
34867             return pos;
34868             
34869         }
34870         
34871         pos.push({
34872             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34873             y : minY
34874         });
34875
34876         pos.push({
34877             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34878             y : minY + (this.unitWidth + this.gutter) * 2
34879         });
34880         
34881         return pos;
34882         
34883     },
34884     
34885     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34886     {
34887         var pos = [];
34888         
34889         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34890             
34891             pos.push({
34892                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34893                 y : minY
34894             });
34895
34896             pos.push({
34897                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34898                 y : minY + (this.unitWidth + this.gutter) * 1
34899             });
34900             
34901             pos.push({
34902                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34903                 y : minY + (this.unitWidth + this.gutter) * 2
34904             });
34905             
34906             return pos;
34907             
34908         }
34909         
34910         if(box[0].size == 'xs' && box[1].size == 'xs'){
34911             
34912             pos.push({
34913                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34914                 y : minY
34915             });
34916
34917             pos.push({
34918                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34919                 y : minY
34920             });
34921             
34922             pos.push({
34923                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34924                 y : minY + (this.unitWidth + this.gutter) * 1
34925             });
34926             
34927             return pos;
34928             
34929         }
34930         
34931         pos.push({
34932             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34933             y : minY
34934         });
34935
34936         pos.push({
34937             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34938             y : minY + (this.unitWidth + this.gutter) * 2
34939         });
34940
34941         pos.push({
34942             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34943             y : minY + (this.unitWidth + this.gutter) * 2
34944         });
34945             
34946         return pos;
34947         
34948     },
34949     
34950     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34951     {
34952         var pos = [];
34953         
34954         if(box[0].size == 'xs'){
34955             
34956             pos.push({
34957                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34958                 y : minY
34959             });
34960
34961             pos.push({
34962                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34963                 y : minY
34964             });
34965             
34966             pos.push({
34967                 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),
34968                 y : minY
34969             });
34970             
34971             pos.push({
34972                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34973                 y : minY + (this.unitWidth + this.gutter) * 1
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         pos.push({
34991             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34992             y : minY + (this.unitWidth + this.gutter) * 2
34993         });
34994         
34995         pos.push({
34996             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),
34997             y : minY + (this.unitWidth + this.gutter) * 2
34998         });
34999
35000         return pos;
35001         
35002     },
35003     
35004     /**
35005     * remove a Masonry Brick
35006     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35007     */
35008     removeBrick : function(brick_id)
35009     {
35010         if (!brick_id) {
35011             return;
35012         }
35013         
35014         for (var i = 0; i<this.bricks.length; i++) {
35015             if (this.bricks[i].id == brick_id) {
35016                 this.bricks.splice(i,1);
35017                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35018                 this.initial();
35019             }
35020         }
35021     },
35022     
35023     /**
35024     * adds a Masonry Brick
35025     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35026     */
35027     addBrick : function(cfg)
35028     {
35029         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35030         //this.register(cn);
35031         cn.parentId = this.id;
35032         cn.render(this.el);
35033         return cn;
35034     },
35035     
35036     /**
35037     * register a Masonry Brick
35038     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35039     */
35040     
35041     register : function(brick)
35042     {
35043         this.bricks.push(brick);
35044         brick.masonryId = this.id;
35045     },
35046     
35047     /**
35048     * clear all the Masonry Brick
35049     */
35050     clearAll : function()
35051     {
35052         this.bricks = [];
35053         //this.getChildContainer().dom.innerHTML = "";
35054         this.el.dom.innerHTML = '';
35055     },
35056     
35057     getSelected : function()
35058     {
35059         if (!this.selectedBrick) {
35060             return false;
35061         }
35062         
35063         return this.selectedBrick;
35064     }
35065 });
35066
35067 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35068     
35069     groups: {},
35070      /**
35071     * register a Masonry Layout
35072     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35073     */
35074     
35075     register : function(layout)
35076     {
35077         this.groups[layout.id] = layout;
35078     },
35079     /**
35080     * fetch a  Masonry Layout based on the masonry layout ID
35081     * @param {string} the masonry layout to add
35082     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35083     */
35084     
35085     get: function(layout_id) {
35086         if (typeof(this.groups[layout_id]) == 'undefined') {
35087             return false;
35088         }
35089         return this.groups[layout_id] ;
35090     }
35091     
35092     
35093     
35094 });
35095
35096  
35097
35098  /**
35099  *
35100  * This is based on 
35101  * http://masonry.desandro.com
35102  *
35103  * The idea is to render all the bricks based on vertical width...
35104  *
35105  * The original code extends 'outlayer' - we might need to use that....
35106  * 
35107  */
35108
35109
35110 /**
35111  * @class Roo.bootstrap.LayoutMasonryAuto
35112  * @extends Roo.bootstrap.Component
35113  * Bootstrap Layout Masonry class
35114  * 
35115  * @constructor
35116  * Create a new Element
35117  * @param {Object} config The config object
35118  */
35119
35120 Roo.bootstrap.LayoutMasonryAuto = function(config){
35121     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35122 };
35123
35124 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35125     
35126       /**
35127      * @cfg {Boolean} isFitWidth  - resize the width..
35128      */   
35129     isFitWidth : false,  // options..
35130     /**
35131      * @cfg {Boolean} isOriginLeft = left align?
35132      */   
35133     isOriginLeft : true,
35134     /**
35135      * @cfg {Boolean} isOriginTop = top align?
35136      */   
35137     isOriginTop : false,
35138     /**
35139      * @cfg {Boolean} isLayoutInstant = no animation?
35140      */   
35141     isLayoutInstant : false, // needed?
35142     /**
35143      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35144      */   
35145     isResizingContainer : true,
35146     /**
35147      * @cfg {Number} columnWidth  width of the columns 
35148      */   
35149     
35150     columnWidth : 0,
35151     
35152     /**
35153      * @cfg {Number} maxCols maximum number of columns
35154      */   
35155     
35156     maxCols: 0,
35157     /**
35158      * @cfg {Number} padHeight padding below box..
35159      */   
35160     
35161     padHeight : 10, 
35162     
35163     /**
35164      * @cfg {Boolean} isAutoInitial defalut true
35165      */   
35166     
35167     isAutoInitial : true, 
35168     
35169     // private?
35170     gutter : 0,
35171     
35172     containerWidth: 0,
35173     initialColumnWidth : 0,
35174     currentSize : null,
35175     
35176     colYs : null, // array.
35177     maxY : 0,
35178     padWidth: 10,
35179     
35180     
35181     tag: 'div',
35182     cls: '',
35183     bricks: null, //CompositeElement
35184     cols : 0, // array?
35185     // element : null, // wrapped now this.el
35186     _isLayoutInited : null, 
35187     
35188     
35189     getAutoCreate : function(){
35190         
35191         var cfg = {
35192             tag: this.tag,
35193             cls: 'blog-masonary-wrapper ' + this.cls,
35194             cn : {
35195                 cls : 'mas-boxes masonary'
35196             }
35197         };
35198         
35199         return cfg;
35200     },
35201     
35202     getChildContainer: function( )
35203     {
35204         if (this.boxesEl) {
35205             return this.boxesEl;
35206         }
35207         
35208         this.boxesEl = this.el.select('.mas-boxes').first();
35209         
35210         return this.boxesEl;
35211     },
35212     
35213     
35214     initEvents : function()
35215     {
35216         var _this = this;
35217         
35218         if(this.isAutoInitial){
35219             Roo.log('hook children rendered');
35220             this.on('childrenrendered', function() {
35221                 Roo.log('children rendered');
35222                 _this.initial();
35223             } ,this);
35224         }
35225         
35226     },
35227     
35228     initial : function()
35229     {
35230         this.reloadItems();
35231
35232         this.currentSize = this.el.getBox(true);
35233
35234         /// was window resize... - let's see if this works..
35235         Roo.EventManager.onWindowResize(this.resize, this); 
35236
35237         if(!this.isAutoInitial){
35238             this.layout();
35239             return;
35240         }
35241         
35242         this.layout.defer(500,this);
35243     },
35244     
35245     reloadItems: function()
35246     {
35247         this.bricks = this.el.select('.masonry-brick', true);
35248         
35249         this.bricks.each(function(b) {
35250             //Roo.log(b.getSize());
35251             if (!b.attr('originalwidth')) {
35252                 b.attr('originalwidth',  b.getSize().width);
35253             }
35254             
35255         });
35256         
35257         Roo.log(this.bricks.elements.length);
35258     },
35259     
35260     resize : function()
35261     {
35262         Roo.log('resize');
35263         var cs = this.el.getBox(true);
35264         
35265         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35266             Roo.log("no change in with or X");
35267             return;
35268         }
35269         this.currentSize = cs;
35270         this.layout();
35271     },
35272     
35273     layout : function()
35274     {
35275          Roo.log('layout');
35276         this._resetLayout();
35277         //this._manageStamps();
35278       
35279         // don't animate first layout
35280         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35281         this.layoutItems( isInstant );
35282       
35283         // flag for initalized
35284         this._isLayoutInited = true;
35285     },
35286     
35287     layoutItems : function( isInstant )
35288     {
35289         //var items = this._getItemsForLayout( this.items );
35290         // original code supports filtering layout items.. we just ignore it..
35291         
35292         this._layoutItems( this.bricks , isInstant );
35293       
35294         this._postLayout();
35295     },
35296     _layoutItems : function ( items , isInstant)
35297     {
35298        //this.fireEvent( 'layout', this, items );
35299     
35300
35301         if ( !items || !items.elements.length ) {
35302           // no items, emit event with empty array
35303             return;
35304         }
35305
35306         var queue = [];
35307         items.each(function(item) {
35308             Roo.log("layout item");
35309             Roo.log(item);
35310             // get x/y object from method
35311             var position = this._getItemLayoutPosition( item );
35312             // enqueue
35313             position.item = item;
35314             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35315             queue.push( position );
35316         }, this);
35317       
35318         this._processLayoutQueue( queue );
35319     },
35320     /** Sets position of item in DOM
35321     * @param {Element} item
35322     * @param {Number} x - horizontal position
35323     * @param {Number} y - vertical position
35324     * @param {Boolean} isInstant - disables transitions
35325     */
35326     _processLayoutQueue : function( queue )
35327     {
35328         for ( var i=0, len = queue.length; i < len; i++ ) {
35329             var obj = queue[i];
35330             obj.item.position('absolute');
35331             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35332         }
35333     },
35334       
35335     
35336     /**
35337     * Any logic you want to do after each layout,
35338     * i.e. size the container
35339     */
35340     _postLayout : function()
35341     {
35342         this.resizeContainer();
35343     },
35344     
35345     resizeContainer : function()
35346     {
35347         if ( !this.isResizingContainer ) {
35348             return;
35349         }
35350         var size = this._getContainerSize();
35351         if ( size ) {
35352             this.el.setSize(size.width,size.height);
35353             this.boxesEl.setSize(size.width,size.height);
35354         }
35355     },
35356     
35357     
35358     
35359     _resetLayout : function()
35360     {
35361         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35362         this.colWidth = this.el.getWidth();
35363         //this.gutter = this.el.getWidth(); 
35364         
35365         this.measureColumns();
35366
35367         // reset column Y
35368         var i = this.cols;
35369         this.colYs = [];
35370         while (i--) {
35371             this.colYs.push( 0 );
35372         }
35373     
35374         this.maxY = 0;
35375     },
35376
35377     measureColumns : function()
35378     {
35379         this.getContainerWidth();
35380       // if columnWidth is 0, default to outerWidth of first item
35381         if ( !this.columnWidth ) {
35382             var firstItem = this.bricks.first();
35383             Roo.log(firstItem);
35384             this.columnWidth  = this.containerWidth;
35385             if (firstItem && firstItem.attr('originalwidth') ) {
35386                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35387             }
35388             // columnWidth fall back to item of first element
35389             Roo.log("set column width?");
35390                         this.initialColumnWidth = this.columnWidth  ;
35391
35392             // if first elem has no width, default to size of container
35393             
35394         }
35395         
35396         
35397         if (this.initialColumnWidth) {
35398             this.columnWidth = this.initialColumnWidth;
35399         }
35400         
35401         
35402             
35403         // column width is fixed at the top - however if container width get's smaller we should
35404         // reduce it...
35405         
35406         // this bit calcs how man columns..
35407             
35408         var columnWidth = this.columnWidth += this.gutter;
35409       
35410         // calculate columns
35411         var containerWidth = this.containerWidth + this.gutter;
35412         
35413         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35414         // fix rounding errors, typically with gutters
35415         var excess = columnWidth - containerWidth % columnWidth;
35416         
35417         
35418         // if overshoot is less than a pixel, round up, otherwise floor it
35419         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35420         cols = Math[ mathMethod ]( cols );
35421         this.cols = Math.max( cols, 1 );
35422         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35423         
35424          // padding positioning..
35425         var totalColWidth = this.cols * this.columnWidth;
35426         var padavail = this.containerWidth - totalColWidth;
35427         // so for 2 columns - we need 3 'pads'
35428         
35429         var padNeeded = (1+this.cols) * this.padWidth;
35430         
35431         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35432         
35433         this.columnWidth += padExtra
35434         //this.padWidth = Math.floor(padavail /  ( this.cols));
35435         
35436         // adjust colum width so that padding is fixed??
35437         
35438         // we have 3 columns ... total = width * 3
35439         // we have X left over... that should be used by 
35440         
35441         //if (this.expandC) {
35442             
35443         //}
35444         
35445         
35446         
35447     },
35448     
35449     getContainerWidth : function()
35450     {
35451        /* // container is parent if fit width
35452         var container = this.isFitWidth ? this.element.parentNode : this.element;
35453         // check that this.size and size are there
35454         // IE8 triggers resize on body size change, so they might not be
35455         
35456         var size = getSize( container );  //FIXME
35457         this.containerWidth = size && size.innerWidth; //FIXME
35458         */
35459          
35460         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35461         
35462     },
35463     
35464     _getItemLayoutPosition : function( item )  // what is item?
35465     {
35466         // we resize the item to our columnWidth..
35467       
35468         item.setWidth(this.columnWidth);
35469         item.autoBoxAdjust  = false;
35470         
35471         var sz = item.getSize();
35472  
35473         // how many columns does this brick span
35474         var remainder = this.containerWidth % this.columnWidth;
35475         
35476         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35477         // round if off by 1 pixel, otherwise use ceil
35478         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35479         colSpan = Math.min( colSpan, this.cols );
35480         
35481         // normally this should be '1' as we dont' currently allow multi width columns..
35482         
35483         var colGroup = this._getColGroup( colSpan );
35484         // get the minimum Y value from the columns
35485         var minimumY = Math.min.apply( Math, colGroup );
35486         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35487         
35488         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35489          
35490         // position the brick
35491         var position = {
35492             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35493             y: this.currentSize.y + minimumY + this.padHeight
35494         };
35495         
35496         Roo.log(position);
35497         // apply setHeight to necessary columns
35498         var setHeight = minimumY + sz.height + this.padHeight;
35499         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35500         
35501         var setSpan = this.cols + 1 - colGroup.length;
35502         for ( var i = 0; i < setSpan; i++ ) {
35503           this.colYs[ shortColIndex + i ] = setHeight ;
35504         }
35505       
35506         return position;
35507     },
35508     
35509     /**
35510      * @param {Number} colSpan - number of columns the element spans
35511      * @returns {Array} colGroup
35512      */
35513     _getColGroup : function( colSpan )
35514     {
35515         if ( colSpan < 2 ) {
35516           // if brick spans only one column, use all the column Ys
35517           return this.colYs;
35518         }
35519       
35520         var colGroup = [];
35521         // how many different places could this brick fit horizontally
35522         var groupCount = this.cols + 1 - colSpan;
35523         // for each group potential horizontal position
35524         for ( var i = 0; i < groupCount; i++ ) {
35525           // make an array of colY values for that one group
35526           var groupColYs = this.colYs.slice( i, i + colSpan );
35527           // and get the max value of the array
35528           colGroup[i] = Math.max.apply( Math, groupColYs );
35529         }
35530         return colGroup;
35531     },
35532     /*
35533     _manageStamp : function( stamp )
35534     {
35535         var stampSize =  stamp.getSize();
35536         var offset = stamp.getBox();
35537         // get the columns that this stamp affects
35538         var firstX = this.isOriginLeft ? offset.x : offset.right;
35539         var lastX = firstX + stampSize.width;
35540         var firstCol = Math.floor( firstX / this.columnWidth );
35541         firstCol = Math.max( 0, firstCol );
35542         
35543         var lastCol = Math.floor( lastX / this.columnWidth );
35544         // lastCol should not go over if multiple of columnWidth #425
35545         lastCol -= lastX % this.columnWidth ? 0 : 1;
35546         lastCol = Math.min( this.cols - 1, lastCol );
35547         
35548         // set colYs to bottom of the stamp
35549         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35550             stampSize.height;
35551             
35552         for ( var i = firstCol; i <= lastCol; i++ ) {
35553           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35554         }
35555     },
35556     */
35557     
35558     _getContainerSize : function()
35559     {
35560         this.maxY = Math.max.apply( Math, this.colYs );
35561         var size = {
35562             height: this.maxY
35563         };
35564       
35565         if ( this.isFitWidth ) {
35566             size.width = this._getContainerFitWidth();
35567         }
35568       
35569         return size;
35570     },
35571     
35572     _getContainerFitWidth : function()
35573     {
35574         var unusedCols = 0;
35575         // count unused columns
35576         var i = this.cols;
35577         while ( --i ) {
35578           if ( this.colYs[i] !== 0 ) {
35579             break;
35580           }
35581           unusedCols++;
35582         }
35583         // fit container to columns that have been used
35584         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35585     },
35586     
35587     needsResizeLayout : function()
35588     {
35589         var previousWidth = this.containerWidth;
35590         this.getContainerWidth();
35591         return previousWidth !== this.containerWidth;
35592     }
35593  
35594 });
35595
35596  
35597
35598  /*
35599  * - LGPL
35600  *
35601  * element
35602  * 
35603  */
35604
35605 /**
35606  * @class Roo.bootstrap.MasonryBrick
35607  * @extends Roo.bootstrap.Component
35608  * Bootstrap MasonryBrick class
35609  * 
35610  * @constructor
35611  * Create a new MasonryBrick
35612  * @param {Object} config The config object
35613  */
35614
35615 Roo.bootstrap.MasonryBrick = function(config){
35616     
35617     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35618     
35619     Roo.bootstrap.MasonryBrick.register(this);
35620     
35621     this.addEvents({
35622         // raw events
35623         /**
35624          * @event click
35625          * When a MasonryBrick is clcik
35626          * @param {Roo.bootstrap.MasonryBrick} this
35627          * @param {Roo.EventObject} e
35628          */
35629         "click" : true
35630     });
35631 };
35632
35633 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35634     
35635     /**
35636      * @cfg {String} title
35637      */   
35638     title : '',
35639     /**
35640      * @cfg {String} html
35641      */   
35642     html : '',
35643     /**
35644      * @cfg {String} bgimage
35645      */   
35646     bgimage : '',
35647     /**
35648      * @cfg {String} videourl
35649      */   
35650     videourl : '',
35651     /**
35652      * @cfg {String} cls
35653      */   
35654     cls : '',
35655     /**
35656      * @cfg {String} href
35657      */   
35658     href : '',
35659     /**
35660      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35661      */   
35662     size : 'xs',
35663     
35664     /**
35665      * @cfg {String} placetitle (center|bottom)
35666      */   
35667     placetitle : '',
35668     
35669     /**
35670      * @cfg {Boolean} isFitContainer defalut true
35671      */   
35672     isFitContainer : true, 
35673     
35674     /**
35675      * @cfg {Boolean} preventDefault defalut false
35676      */   
35677     preventDefault : false, 
35678     
35679     /**
35680      * @cfg {Boolean} inverse defalut false
35681      */   
35682     maskInverse : false, 
35683     
35684     getAutoCreate : function()
35685     {
35686         if(!this.isFitContainer){
35687             return this.getSplitAutoCreate();
35688         }
35689         
35690         var cls = 'masonry-brick masonry-brick-full';
35691         
35692         if(this.href.length){
35693             cls += ' masonry-brick-link';
35694         }
35695         
35696         if(this.bgimage.length){
35697             cls += ' masonry-brick-image';
35698         }
35699         
35700         if(this.maskInverse){
35701             cls += ' mask-inverse';
35702         }
35703         
35704         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35705             cls += ' enable-mask';
35706         }
35707         
35708         if(this.size){
35709             cls += ' masonry-' + this.size + '-brick';
35710         }
35711         
35712         if(this.placetitle.length){
35713             
35714             switch (this.placetitle) {
35715                 case 'center' :
35716                     cls += ' masonry-center-title';
35717                     break;
35718                 case 'bottom' :
35719                     cls += ' masonry-bottom-title';
35720                     break;
35721                 default:
35722                     break;
35723             }
35724             
35725         } else {
35726             if(!this.html.length && !this.bgimage.length){
35727                 cls += ' masonry-center-title';
35728             }
35729
35730             if(!this.html.length && this.bgimage.length){
35731                 cls += ' masonry-bottom-title';
35732             }
35733         }
35734         
35735         if(this.cls){
35736             cls += ' ' + this.cls;
35737         }
35738         
35739         var cfg = {
35740             tag: (this.href.length) ? 'a' : 'div',
35741             cls: cls,
35742             cn: [
35743                 {
35744                     tag: 'div',
35745                     cls: 'masonry-brick-mask'
35746                 },
35747                 {
35748                     tag: 'div',
35749                     cls: 'masonry-brick-paragraph',
35750                     cn: []
35751                 }
35752             ]
35753         };
35754         
35755         if(this.href.length){
35756             cfg.href = this.href;
35757         }
35758         
35759         var cn = cfg.cn[1].cn;
35760         
35761         if(this.title.length){
35762             cn.push({
35763                 tag: 'h4',
35764                 cls: 'masonry-brick-title',
35765                 html: this.title
35766             });
35767         }
35768         
35769         if(this.html.length){
35770             cn.push({
35771                 tag: 'p',
35772                 cls: 'masonry-brick-text',
35773                 html: this.html
35774             });
35775         }
35776         
35777         if (!this.title.length && !this.html.length) {
35778             cfg.cn[1].cls += ' hide';
35779         }
35780         
35781         if(this.bgimage.length){
35782             cfg.cn.push({
35783                 tag: 'img',
35784                 cls: 'masonry-brick-image-view',
35785                 src: this.bgimage
35786             });
35787         }
35788         
35789         if(this.videourl.length){
35790             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35791             // youtube support only?
35792             cfg.cn.push({
35793                 tag: 'iframe',
35794                 cls: 'masonry-brick-image-view',
35795                 src: vurl,
35796                 frameborder : 0,
35797                 allowfullscreen : true
35798             });
35799         }
35800         
35801         return cfg;
35802         
35803     },
35804     
35805     getSplitAutoCreate : function()
35806     {
35807         var cls = 'masonry-brick masonry-brick-split';
35808         
35809         if(this.href.length){
35810             cls += ' masonry-brick-link';
35811         }
35812         
35813         if(this.bgimage.length){
35814             cls += ' masonry-brick-image';
35815         }
35816         
35817         if(this.size){
35818             cls += ' masonry-' + this.size + '-brick';
35819         }
35820         
35821         switch (this.placetitle) {
35822             case 'center' :
35823                 cls += ' masonry-center-title';
35824                 break;
35825             case 'bottom' :
35826                 cls += ' masonry-bottom-title';
35827                 break;
35828             default:
35829                 if(!this.bgimage.length){
35830                     cls += ' masonry-center-title';
35831                 }
35832
35833                 if(this.bgimage.length){
35834                     cls += ' masonry-bottom-title';
35835                 }
35836                 break;
35837         }
35838         
35839         if(this.cls){
35840             cls += ' ' + this.cls;
35841         }
35842         
35843         var cfg = {
35844             tag: (this.href.length) ? 'a' : 'div',
35845             cls: cls,
35846             cn: [
35847                 {
35848                     tag: 'div',
35849                     cls: 'masonry-brick-split-head',
35850                     cn: [
35851                         {
35852                             tag: 'div',
35853                             cls: 'masonry-brick-paragraph',
35854                             cn: []
35855                         }
35856                     ]
35857                 },
35858                 {
35859                     tag: 'div',
35860                     cls: 'masonry-brick-split-body',
35861                     cn: []
35862                 }
35863             ]
35864         };
35865         
35866         if(this.href.length){
35867             cfg.href = this.href;
35868         }
35869         
35870         if(this.title.length){
35871             cfg.cn[0].cn[0].cn.push({
35872                 tag: 'h4',
35873                 cls: 'masonry-brick-title',
35874                 html: this.title
35875             });
35876         }
35877         
35878         if(this.html.length){
35879             cfg.cn[1].cn.push({
35880                 tag: 'p',
35881                 cls: 'masonry-brick-text',
35882                 html: this.html
35883             });
35884         }
35885
35886         if(this.bgimage.length){
35887             cfg.cn[0].cn.push({
35888                 tag: 'img',
35889                 cls: 'masonry-brick-image-view',
35890                 src: this.bgimage
35891             });
35892         }
35893         
35894         if(this.videourl.length){
35895             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35896             // youtube support only?
35897             cfg.cn[0].cn.cn.push({
35898                 tag: 'iframe',
35899                 cls: 'masonry-brick-image-view',
35900                 src: vurl,
35901                 frameborder : 0,
35902                 allowfullscreen : true
35903             });
35904         }
35905         
35906         return cfg;
35907     },
35908     
35909     initEvents: function() 
35910     {
35911         switch (this.size) {
35912             case 'xs' :
35913                 this.x = 1;
35914                 this.y = 1;
35915                 break;
35916             case 'sm' :
35917                 this.x = 2;
35918                 this.y = 2;
35919                 break;
35920             case 'md' :
35921             case 'md-left' :
35922             case 'md-right' :
35923                 this.x = 3;
35924                 this.y = 3;
35925                 break;
35926             case 'tall' :
35927                 this.x = 2;
35928                 this.y = 3;
35929                 break;
35930             case 'wide' :
35931                 this.x = 3;
35932                 this.y = 2;
35933                 break;
35934             case 'wide-thin' :
35935                 this.x = 3;
35936                 this.y = 1;
35937                 break;
35938                         
35939             default :
35940                 break;
35941         }
35942         
35943         if(Roo.isTouch){
35944             this.el.on('touchstart', this.onTouchStart, this);
35945             this.el.on('touchmove', this.onTouchMove, this);
35946             this.el.on('touchend', this.onTouchEnd, this);
35947             this.el.on('contextmenu', this.onContextMenu, this);
35948         } else {
35949             this.el.on('mouseenter'  ,this.enter, this);
35950             this.el.on('mouseleave', this.leave, this);
35951             this.el.on('click', this.onClick, this);
35952         }
35953         
35954         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35955             this.parent().bricks.push(this);   
35956         }
35957         
35958     },
35959     
35960     onClick: function(e, el)
35961     {
35962         var time = this.endTimer - this.startTimer;
35963         // Roo.log(e.preventDefault());
35964         if(Roo.isTouch){
35965             if(time > 1000){
35966                 e.preventDefault();
35967                 return;
35968             }
35969         }
35970         
35971         if(!this.preventDefault){
35972             return;
35973         }
35974         
35975         e.preventDefault();
35976         
35977         if (this.activeClass != '') {
35978             this.selectBrick();
35979         }
35980         
35981         this.fireEvent('click', this, e);
35982     },
35983     
35984     enter: function(e, el)
35985     {
35986         e.preventDefault();
35987         
35988         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35989             return;
35990         }
35991         
35992         if(this.bgimage.length && this.html.length){
35993             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35994         }
35995     },
35996     
35997     leave: function(e, el)
35998     {
35999         e.preventDefault();
36000         
36001         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36002             return;
36003         }
36004         
36005         if(this.bgimage.length && this.html.length){
36006             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36007         }
36008     },
36009     
36010     onTouchStart: function(e, el)
36011     {
36012 //        e.preventDefault();
36013         
36014         this.touchmoved = false;
36015         
36016         if(!this.isFitContainer){
36017             return;
36018         }
36019         
36020         if(!this.bgimage.length || !this.html.length){
36021             return;
36022         }
36023         
36024         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36025         
36026         this.timer = new Date().getTime();
36027         
36028     },
36029     
36030     onTouchMove: function(e, el)
36031     {
36032         this.touchmoved = true;
36033     },
36034     
36035     onContextMenu : function(e,el)
36036     {
36037         e.preventDefault();
36038         e.stopPropagation();
36039         return false;
36040     },
36041     
36042     onTouchEnd: function(e, el)
36043     {
36044 //        e.preventDefault();
36045         
36046         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36047         
36048             this.leave(e,el);
36049             
36050             return;
36051         }
36052         
36053         if(!this.bgimage.length || !this.html.length){
36054             
36055             if(this.href.length){
36056                 window.location.href = this.href;
36057             }
36058             
36059             return;
36060         }
36061         
36062         if(!this.isFitContainer){
36063             return;
36064         }
36065         
36066         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36067         
36068         window.location.href = this.href;
36069     },
36070     
36071     //selection on single brick only
36072     selectBrick : function() {
36073         
36074         if (!this.parentId) {
36075             return;
36076         }
36077         
36078         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36079         var index = m.selectedBrick.indexOf(this.id);
36080         
36081         if ( index > -1) {
36082             m.selectedBrick.splice(index,1);
36083             this.el.removeClass(this.activeClass);
36084             return;
36085         }
36086         
36087         for(var i = 0; i < m.selectedBrick.length; i++) {
36088             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36089             b.el.removeClass(b.activeClass);
36090         }
36091         
36092         m.selectedBrick = [];
36093         
36094         m.selectedBrick.push(this.id);
36095         this.el.addClass(this.activeClass);
36096         return;
36097     },
36098     
36099     isSelected : function(){
36100         return this.el.hasClass(this.activeClass);
36101         
36102     }
36103 });
36104
36105 Roo.apply(Roo.bootstrap.MasonryBrick, {
36106     
36107     //groups: {},
36108     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36109      /**
36110     * register a Masonry Brick
36111     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36112     */
36113     
36114     register : function(brick)
36115     {
36116         //this.groups[brick.id] = brick;
36117         this.groups.add(brick.id, brick);
36118     },
36119     /**
36120     * fetch a  masonry brick based on the masonry brick ID
36121     * @param {string} the masonry brick to add
36122     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36123     */
36124     
36125     get: function(brick_id) 
36126     {
36127         // if (typeof(this.groups[brick_id]) == 'undefined') {
36128         //     return false;
36129         // }
36130         // return this.groups[brick_id] ;
36131         
36132         if(this.groups.key(brick_id)) {
36133             return this.groups.key(brick_id);
36134         }
36135         
36136         return false;
36137     }
36138     
36139     
36140     
36141 });
36142
36143  /*
36144  * - LGPL
36145  *
36146  * element
36147  * 
36148  */
36149
36150 /**
36151  * @class Roo.bootstrap.Brick
36152  * @extends Roo.bootstrap.Component
36153  * Bootstrap Brick class
36154  * 
36155  * @constructor
36156  * Create a new Brick
36157  * @param {Object} config The config object
36158  */
36159
36160 Roo.bootstrap.Brick = function(config){
36161     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36162     
36163     this.addEvents({
36164         // raw events
36165         /**
36166          * @event click
36167          * When a Brick is click
36168          * @param {Roo.bootstrap.Brick} this
36169          * @param {Roo.EventObject} e
36170          */
36171         "click" : true
36172     });
36173 };
36174
36175 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36176     
36177     /**
36178      * @cfg {String} title
36179      */   
36180     title : '',
36181     /**
36182      * @cfg {String} html
36183      */   
36184     html : '',
36185     /**
36186      * @cfg {String} bgimage
36187      */   
36188     bgimage : '',
36189     /**
36190      * @cfg {String} cls
36191      */   
36192     cls : '',
36193     /**
36194      * @cfg {String} href
36195      */   
36196     href : '',
36197     /**
36198      * @cfg {String} video
36199      */   
36200     video : '',
36201     /**
36202      * @cfg {Boolean} square
36203      */   
36204     square : true,
36205     
36206     getAutoCreate : function()
36207     {
36208         var cls = 'roo-brick';
36209         
36210         if(this.href.length){
36211             cls += ' roo-brick-link';
36212         }
36213         
36214         if(this.bgimage.length){
36215             cls += ' roo-brick-image';
36216         }
36217         
36218         if(!this.html.length && !this.bgimage.length){
36219             cls += ' roo-brick-center-title';
36220         }
36221         
36222         if(!this.html.length && this.bgimage.length){
36223             cls += ' roo-brick-bottom-title';
36224         }
36225         
36226         if(this.cls){
36227             cls += ' ' + this.cls;
36228         }
36229         
36230         var cfg = {
36231             tag: (this.href.length) ? 'a' : 'div',
36232             cls: cls,
36233             cn: [
36234                 {
36235                     tag: 'div',
36236                     cls: 'roo-brick-paragraph',
36237                     cn: []
36238                 }
36239             ]
36240         };
36241         
36242         if(this.href.length){
36243             cfg.href = this.href;
36244         }
36245         
36246         var cn = cfg.cn[0].cn;
36247         
36248         if(this.title.length){
36249             cn.push({
36250                 tag: 'h4',
36251                 cls: 'roo-brick-title',
36252                 html: this.title
36253             });
36254         }
36255         
36256         if(this.html.length){
36257             cn.push({
36258                 tag: 'p',
36259                 cls: 'roo-brick-text',
36260                 html: this.html
36261             });
36262         } else {
36263             cn.cls += ' hide';
36264         }
36265         
36266         if(this.bgimage.length){
36267             cfg.cn.push({
36268                 tag: 'img',
36269                 cls: 'roo-brick-image-view',
36270                 src: this.bgimage
36271             });
36272         }
36273         
36274         return cfg;
36275     },
36276     
36277     initEvents: function() 
36278     {
36279         if(this.title.length || this.html.length){
36280             this.el.on('mouseenter'  ,this.enter, this);
36281             this.el.on('mouseleave', this.leave, this);
36282         }
36283         
36284         Roo.EventManager.onWindowResize(this.resize, this); 
36285         
36286         if(this.bgimage.length){
36287             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36288             this.imageEl.on('load', this.onImageLoad, this);
36289             return;
36290         }
36291         
36292         this.resize();
36293     },
36294     
36295     onImageLoad : function()
36296     {
36297         this.resize();
36298     },
36299     
36300     resize : function()
36301     {
36302         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36303         
36304         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36305         
36306         if(this.bgimage.length){
36307             var image = this.el.select('.roo-brick-image-view', true).first();
36308             
36309             image.setWidth(paragraph.getWidth());
36310             
36311             if(this.square){
36312                 image.setHeight(paragraph.getWidth());
36313             }
36314             
36315             this.el.setHeight(image.getHeight());
36316             paragraph.setHeight(image.getHeight());
36317             
36318         }
36319         
36320     },
36321     
36322     enter: function(e, el)
36323     {
36324         e.preventDefault();
36325         
36326         if(this.bgimage.length){
36327             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36328             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36329         }
36330     },
36331     
36332     leave: function(e, el)
36333     {
36334         e.preventDefault();
36335         
36336         if(this.bgimage.length){
36337             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36338             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36339         }
36340     }
36341     
36342 });
36343
36344  
36345
36346  /*
36347  * - LGPL
36348  *
36349  * Number field 
36350  */
36351
36352 /**
36353  * @class Roo.bootstrap.NumberField
36354  * @extends Roo.bootstrap.Input
36355  * Bootstrap NumberField class
36356  * 
36357  * 
36358  * 
36359  * 
36360  * @constructor
36361  * Create a new NumberField
36362  * @param {Object} config The config object
36363  */
36364
36365 Roo.bootstrap.NumberField = function(config){
36366     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36367 };
36368
36369 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36370     
36371     /**
36372      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36373      */
36374     allowDecimals : true,
36375     /**
36376      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36377      */
36378     decimalSeparator : ".",
36379     /**
36380      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36381      */
36382     decimalPrecision : 2,
36383     /**
36384      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36385      */
36386     allowNegative : true,
36387     
36388     /**
36389      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36390      */
36391     allowZero: true,
36392     /**
36393      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36394      */
36395     minValue : Number.NEGATIVE_INFINITY,
36396     /**
36397      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36398      */
36399     maxValue : Number.MAX_VALUE,
36400     /**
36401      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36402      */
36403     minText : "The minimum value for this field is {0}",
36404     /**
36405      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36406      */
36407     maxText : "The maximum value for this field is {0}",
36408     /**
36409      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36410      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36411      */
36412     nanText : "{0} is not a valid number",
36413     /**
36414      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36415      */
36416     thousandsDelimiter : false,
36417     /**
36418      * @cfg {String} valueAlign alignment of value
36419      */
36420     valueAlign : "left",
36421
36422     getAutoCreate : function()
36423     {
36424         var hiddenInput = {
36425             tag: 'input',
36426             type: 'hidden',
36427             id: Roo.id(),
36428             cls: 'hidden-number-input'
36429         };
36430         
36431         if (this.name) {
36432             hiddenInput.name = this.name;
36433         }
36434         
36435         this.name = '';
36436         
36437         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36438         
36439         this.name = hiddenInput.name;
36440         
36441         if(cfg.cn.length > 0) {
36442             cfg.cn.push(hiddenInput);
36443         }
36444         
36445         return cfg;
36446     },
36447
36448     // private
36449     initEvents : function()
36450     {   
36451         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36452         
36453         var allowed = "0123456789";
36454         
36455         if(this.allowDecimals){
36456             allowed += this.decimalSeparator;
36457         }
36458         
36459         if(this.allowNegative){
36460             allowed += "-";
36461         }
36462         
36463         if(this.thousandsDelimiter) {
36464             allowed += ",";
36465         }
36466         
36467         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36468         
36469         var keyPress = function(e){
36470             
36471             var k = e.getKey();
36472             
36473             var c = e.getCharCode();
36474             
36475             if(
36476                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36477                     allowed.indexOf(String.fromCharCode(c)) === -1
36478             ){
36479                 e.stopEvent();
36480                 return;
36481             }
36482             
36483             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36484                 return;
36485             }
36486             
36487             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36488                 e.stopEvent();
36489             }
36490         };
36491         
36492         this.el.on("keypress", keyPress, this);
36493     },
36494     
36495     validateValue : function(value)
36496     {
36497         
36498         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36499             return false;
36500         }
36501         
36502         var num = this.parseValue(value);
36503         
36504         if(isNaN(num)){
36505             this.markInvalid(String.format(this.nanText, value));
36506             return false;
36507         }
36508         
36509         if(num < this.minValue){
36510             this.markInvalid(String.format(this.minText, this.minValue));
36511             return false;
36512         }
36513         
36514         if(num > this.maxValue){
36515             this.markInvalid(String.format(this.maxText, this.maxValue));
36516             return false;
36517         }
36518         
36519         return true;
36520     },
36521
36522     getValue : function()
36523     {
36524         var v = this.hiddenEl().getValue();
36525         
36526         return this.fixPrecision(this.parseValue(v));
36527     },
36528
36529     parseValue : function(value)
36530     {
36531         if(this.thousandsDelimiter) {
36532             value += "";
36533             r = new RegExp(",", "g");
36534             value = value.replace(r, "");
36535         }
36536         
36537         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36538         return isNaN(value) ? '' : value;
36539     },
36540
36541     fixPrecision : function(value)
36542     {
36543         if(this.thousandsDelimiter) {
36544             value += "";
36545             r = new RegExp(",", "g");
36546             value = value.replace(r, "");
36547         }
36548         
36549         var nan = isNaN(value);
36550         
36551         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36552             return nan ? '' : value;
36553         }
36554         return parseFloat(value).toFixed(this.decimalPrecision);
36555     },
36556
36557     setValue : function(v)
36558     {
36559         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36560         
36561         this.value = v;
36562         
36563         if(this.rendered){
36564             
36565             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36566             
36567             this.inputEl().dom.value = (v == '') ? '' :
36568                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36569             
36570             if(!this.allowZero && v === '0') {
36571                 this.hiddenEl().dom.value = '';
36572                 this.inputEl().dom.value = '';
36573             }
36574             
36575             this.validate();
36576         }
36577     },
36578
36579     decimalPrecisionFcn : function(v)
36580     {
36581         return Math.floor(v);
36582     },
36583
36584     beforeBlur : function()
36585     {
36586         var v = this.parseValue(this.getRawValue());
36587         
36588         if(v || v === 0 || v === ''){
36589             this.setValue(v);
36590         }
36591     },
36592     
36593     hiddenEl : function()
36594     {
36595         return this.el.select('input.hidden-number-input',true).first();
36596     }
36597     
36598 });
36599
36600  
36601
36602 /*
36603 * Licence: LGPL
36604 */
36605
36606 /**
36607  * @class Roo.bootstrap.DocumentSlider
36608  * @extends Roo.bootstrap.Component
36609  * Bootstrap DocumentSlider class
36610  * 
36611  * @constructor
36612  * Create a new DocumentViewer
36613  * @param {Object} config The config object
36614  */
36615
36616 Roo.bootstrap.DocumentSlider = function(config){
36617     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36618     
36619     this.files = [];
36620     
36621     this.addEvents({
36622         /**
36623          * @event initial
36624          * Fire after initEvent
36625          * @param {Roo.bootstrap.DocumentSlider} this
36626          */
36627         "initial" : true,
36628         /**
36629          * @event update
36630          * Fire after update
36631          * @param {Roo.bootstrap.DocumentSlider} this
36632          */
36633         "update" : true,
36634         /**
36635          * @event click
36636          * Fire after click
36637          * @param {Roo.bootstrap.DocumentSlider} this
36638          */
36639         "click" : true
36640     });
36641 };
36642
36643 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36644     
36645     files : false,
36646     
36647     indicator : 0,
36648     
36649     getAutoCreate : function()
36650     {
36651         var cfg = {
36652             tag : 'div',
36653             cls : 'roo-document-slider',
36654             cn : [
36655                 {
36656                     tag : 'div',
36657                     cls : 'roo-document-slider-header',
36658                     cn : [
36659                         {
36660                             tag : 'div',
36661                             cls : 'roo-document-slider-header-title'
36662                         }
36663                     ]
36664                 },
36665                 {
36666                     tag : 'div',
36667                     cls : 'roo-document-slider-body',
36668                     cn : [
36669                         {
36670                             tag : 'div',
36671                             cls : 'roo-document-slider-prev',
36672                             cn : [
36673                                 {
36674                                     tag : 'i',
36675                                     cls : 'fa fa-chevron-left'
36676                                 }
36677                             ]
36678                         },
36679                         {
36680                             tag : 'div',
36681                             cls : 'roo-document-slider-thumb',
36682                             cn : [
36683                                 {
36684                                     tag : 'img',
36685                                     cls : 'roo-document-slider-image'
36686                                 }
36687                             ]
36688                         },
36689                         {
36690                             tag : 'div',
36691                             cls : 'roo-document-slider-next',
36692                             cn : [
36693                                 {
36694                                     tag : 'i',
36695                                     cls : 'fa fa-chevron-right'
36696                                 }
36697                             ]
36698                         }
36699                     ]
36700                 }
36701             ]
36702         };
36703         
36704         return cfg;
36705     },
36706     
36707     initEvents : function()
36708     {
36709         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36710         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36711         
36712         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36713         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36714         
36715         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36716         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36717         
36718         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36719         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36720         
36721         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36722         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36723         
36724         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36725         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36726         
36727         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36728         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36729         
36730         this.thumbEl.on('click', this.onClick, this);
36731         
36732         this.prevIndicator.on('click', this.prev, this);
36733         
36734         this.nextIndicator.on('click', this.next, this);
36735         
36736     },
36737     
36738     initial : function()
36739     {
36740         if(this.files.length){
36741             this.indicator = 1;
36742             this.update()
36743         }
36744         
36745         this.fireEvent('initial', this);
36746     },
36747     
36748     update : function()
36749     {
36750         this.imageEl.attr('src', this.files[this.indicator - 1]);
36751         
36752         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36753         
36754         this.prevIndicator.show();
36755         
36756         if(this.indicator == 1){
36757             this.prevIndicator.hide();
36758         }
36759         
36760         this.nextIndicator.show();
36761         
36762         if(this.indicator == this.files.length){
36763             this.nextIndicator.hide();
36764         }
36765         
36766         this.thumbEl.scrollTo('top');
36767         
36768         this.fireEvent('update', this);
36769     },
36770     
36771     onClick : function(e)
36772     {
36773         e.preventDefault();
36774         
36775         this.fireEvent('click', this);
36776     },
36777     
36778     prev : function(e)
36779     {
36780         e.preventDefault();
36781         
36782         this.indicator = Math.max(1, this.indicator - 1);
36783         
36784         this.update();
36785     },
36786     
36787     next : function(e)
36788     {
36789         e.preventDefault();
36790         
36791         this.indicator = Math.min(this.files.length, this.indicator + 1);
36792         
36793         this.update();
36794     }
36795 });
36796 /*
36797  * - LGPL
36798  *
36799  * RadioSet
36800  *
36801  *
36802  */
36803
36804 /**
36805  * @class Roo.bootstrap.RadioSet
36806  * @extends Roo.bootstrap.Input
36807  * Bootstrap RadioSet class
36808  * @cfg {String} indicatorpos (left|right) default left
36809  * @cfg {Boolean} inline (true|false) inline the element (default true)
36810  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36811  * @constructor
36812  * Create a new RadioSet
36813  * @param {Object} config The config object
36814  */
36815
36816 Roo.bootstrap.RadioSet = function(config){
36817     
36818     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36819     
36820     this.radioes = [];
36821     
36822     Roo.bootstrap.RadioSet.register(this);
36823     
36824     this.addEvents({
36825         /**
36826         * @event check
36827         * Fires when the element is checked or unchecked.
36828         * @param {Roo.bootstrap.RadioSet} this This radio
36829         * @param {Roo.bootstrap.Radio} item The checked item
36830         */
36831        check : true,
36832        /**
36833         * @event click
36834         * Fires when the element is click.
36835         * @param {Roo.bootstrap.RadioSet} this This radio set
36836         * @param {Roo.bootstrap.Radio} item The checked item
36837         * @param {Roo.EventObject} e The event object
36838         */
36839        click : true
36840     });
36841     
36842 };
36843
36844 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36845
36846     radioes : false,
36847     
36848     inline : true,
36849     
36850     weight : '',
36851     
36852     indicatorpos : 'left',
36853     
36854     getAutoCreate : function()
36855     {
36856         var label = {
36857             tag : 'label',
36858             cls : 'roo-radio-set-label',
36859             cn : [
36860                 {
36861                     tag : 'span',
36862                     html : this.fieldLabel
36863                 }
36864             ]
36865         };
36866         if (Roo.bootstrap.version == 3) {
36867             
36868             
36869             if(this.indicatorpos == 'left'){
36870                 label.cn.unshift({
36871                     tag : 'i',
36872                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36873                     tooltip : 'This field is required'
36874                 });
36875             } else {
36876                 label.cn.push({
36877                     tag : 'i',
36878                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36879                     tooltip : 'This field is required'
36880                 });
36881             }
36882         }
36883         var items = {
36884             tag : 'div',
36885             cls : 'roo-radio-set-items'
36886         };
36887         
36888         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36889         
36890         if (align === 'left' && this.fieldLabel.length) {
36891             
36892             items = {
36893                 cls : "roo-radio-set-right", 
36894                 cn: [
36895                     items
36896                 ]
36897             };
36898             
36899             if(this.labelWidth > 12){
36900                 label.style = "width: " + this.labelWidth + 'px';
36901             }
36902             
36903             if(this.labelWidth < 13 && this.labelmd == 0){
36904                 this.labelmd = this.labelWidth;
36905             }
36906             
36907             if(this.labellg > 0){
36908                 label.cls += ' col-lg-' + this.labellg;
36909                 items.cls += ' col-lg-' + (12 - this.labellg);
36910             }
36911             
36912             if(this.labelmd > 0){
36913                 label.cls += ' col-md-' + this.labelmd;
36914                 items.cls += ' col-md-' + (12 - this.labelmd);
36915             }
36916             
36917             if(this.labelsm > 0){
36918                 label.cls += ' col-sm-' + this.labelsm;
36919                 items.cls += ' col-sm-' + (12 - this.labelsm);
36920             }
36921             
36922             if(this.labelxs > 0){
36923                 label.cls += ' col-xs-' + this.labelxs;
36924                 items.cls += ' col-xs-' + (12 - this.labelxs);
36925             }
36926         }
36927         
36928         var cfg = {
36929             tag : 'div',
36930             cls : 'roo-radio-set',
36931             cn : [
36932                 {
36933                     tag : 'input',
36934                     cls : 'roo-radio-set-input',
36935                     type : 'hidden',
36936                     name : this.name,
36937                     value : this.value ? this.value :  ''
36938                 },
36939                 label,
36940                 items
36941             ]
36942         };
36943         
36944         if(this.weight.length){
36945             cfg.cls += ' roo-radio-' + this.weight;
36946         }
36947         
36948         if(this.inline) {
36949             cfg.cls += ' roo-radio-set-inline';
36950         }
36951         
36952         var settings=this;
36953         ['xs','sm','md','lg'].map(function(size){
36954             if (settings[size]) {
36955                 cfg.cls += ' col-' + size + '-' + settings[size];
36956             }
36957         });
36958         
36959         return cfg;
36960         
36961     },
36962
36963     initEvents : function()
36964     {
36965         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36966         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36967         
36968         if(!this.fieldLabel.length){
36969             this.labelEl.hide();
36970         }
36971         
36972         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36973         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36974         
36975         this.indicator = this.indicatorEl();
36976         
36977         if(this.indicator){
36978             this.indicator.addClass('invisible');
36979         }
36980         
36981         this.originalValue = this.getValue();
36982         
36983     },
36984     
36985     inputEl: function ()
36986     {
36987         return this.el.select('.roo-radio-set-input', true).first();
36988     },
36989     
36990     getChildContainer : function()
36991     {
36992         return this.itemsEl;
36993     },
36994     
36995     register : function(item)
36996     {
36997         this.radioes.push(item);
36998         
36999     },
37000     
37001     validate : function()
37002     {   
37003         if(this.getVisibilityEl().hasClass('hidden')){
37004             return true;
37005         }
37006         
37007         var valid = false;
37008         
37009         Roo.each(this.radioes, function(i){
37010             if(!i.checked){
37011                 return;
37012             }
37013             
37014             valid = true;
37015             return false;
37016         });
37017         
37018         if(this.allowBlank) {
37019             return true;
37020         }
37021         
37022         if(this.disabled || valid){
37023             this.markValid();
37024             return true;
37025         }
37026         
37027         this.markInvalid();
37028         return false;
37029         
37030     },
37031     
37032     markValid : function()
37033     {
37034         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37035             this.indicatorEl().removeClass('visible');
37036             this.indicatorEl().addClass('invisible');
37037         }
37038         
37039         
37040         if (Roo.bootstrap.version == 3) {
37041             this.el.removeClass([this.invalidClass, this.validClass]);
37042             this.el.addClass(this.validClass);
37043         } else {
37044             this.el.removeClass(['is-invalid','is-valid']);
37045             this.el.addClass(['is-valid']);
37046         }
37047         this.fireEvent('valid', this);
37048     },
37049     
37050     markInvalid : function(msg)
37051     {
37052         if(this.allowBlank || this.disabled){
37053             return;
37054         }
37055         
37056         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37057             this.indicatorEl().removeClass('invisible');
37058             this.indicatorEl().addClass('visible');
37059         }
37060         if (Roo.bootstrap.version == 3) {
37061             this.el.removeClass([this.invalidClass, this.validClass]);
37062             this.el.addClass(this.invalidClass);
37063         } else {
37064             this.el.removeClass(['is-invalid','is-valid']);
37065             this.el.addClass(['is-invalid']);
37066         }
37067         
37068         this.fireEvent('invalid', this, msg);
37069         
37070     },
37071     
37072     setValue : function(v, suppressEvent)
37073     {   
37074         if(this.value === v){
37075             return;
37076         }
37077         
37078         this.value = v;
37079         
37080         if(this.rendered){
37081             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37082         }
37083         
37084         Roo.each(this.radioes, function(i){
37085             i.checked = false;
37086             i.el.removeClass('checked');
37087         });
37088         
37089         Roo.each(this.radioes, function(i){
37090             
37091             if(i.value === v || i.value.toString() === v.toString()){
37092                 i.checked = true;
37093                 i.el.addClass('checked');
37094                 
37095                 if(suppressEvent !== true){
37096                     this.fireEvent('check', this, i);
37097                 }
37098                 
37099                 return false;
37100             }
37101             
37102         }, this);
37103         
37104         this.validate();
37105     },
37106     
37107     clearInvalid : function(){
37108         
37109         if(!this.el || this.preventMark){
37110             return;
37111         }
37112         
37113         this.el.removeClass([this.invalidClass]);
37114         
37115         this.fireEvent('valid', this);
37116     }
37117     
37118 });
37119
37120 Roo.apply(Roo.bootstrap.RadioSet, {
37121     
37122     groups: {},
37123     
37124     register : function(set)
37125     {
37126         this.groups[set.name] = set;
37127     },
37128     
37129     get: function(name) 
37130     {
37131         if (typeof(this.groups[name]) == 'undefined') {
37132             return false;
37133         }
37134         
37135         return this.groups[name] ;
37136     }
37137     
37138 });
37139 /*
37140  * Based on:
37141  * Ext JS Library 1.1.1
37142  * Copyright(c) 2006-2007, Ext JS, LLC.
37143  *
37144  * Originally Released Under LGPL - original licence link has changed is not relivant.
37145  *
37146  * Fork - LGPL
37147  * <script type="text/javascript">
37148  */
37149
37150
37151 /**
37152  * @class Roo.bootstrap.SplitBar
37153  * @extends Roo.util.Observable
37154  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37155  * <br><br>
37156  * Usage:
37157  * <pre><code>
37158 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37159                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37160 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37161 split.minSize = 100;
37162 split.maxSize = 600;
37163 split.animate = true;
37164 split.on('moved', splitterMoved);
37165 </code></pre>
37166  * @constructor
37167  * Create a new SplitBar
37168  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37169  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37170  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37171  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37172                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37173                         position of the SplitBar).
37174  */
37175 Roo.bootstrap.SplitBar = function(cfg){
37176     
37177     /** @private */
37178     
37179     //{
37180     //  dragElement : elm
37181     //  resizingElement: el,
37182         // optional..
37183     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37184     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37185         // existingProxy ???
37186     //}
37187     
37188     this.el = Roo.get(cfg.dragElement, true);
37189     this.el.dom.unselectable = "on";
37190     /** @private */
37191     this.resizingEl = Roo.get(cfg.resizingElement, true);
37192
37193     /**
37194      * @private
37195      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37196      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37197      * @type Number
37198      */
37199     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37200     
37201     /**
37202      * The minimum size of the resizing element. (Defaults to 0)
37203      * @type Number
37204      */
37205     this.minSize = 0;
37206     
37207     /**
37208      * The maximum size of the resizing element. (Defaults to 2000)
37209      * @type Number
37210      */
37211     this.maxSize = 2000;
37212     
37213     /**
37214      * Whether to animate the transition to the new size
37215      * @type Boolean
37216      */
37217     this.animate = false;
37218     
37219     /**
37220      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37221      * @type Boolean
37222      */
37223     this.useShim = false;
37224     
37225     /** @private */
37226     this.shim = null;
37227     
37228     if(!cfg.existingProxy){
37229         /** @private */
37230         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37231     }else{
37232         this.proxy = Roo.get(cfg.existingProxy).dom;
37233     }
37234     /** @private */
37235     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37236     
37237     /** @private */
37238     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37239     
37240     /** @private */
37241     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37242     
37243     /** @private */
37244     this.dragSpecs = {};
37245     
37246     /**
37247      * @private The adapter to use to positon and resize elements
37248      */
37249     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37250     this.adapter.init(this);
37251     
37252     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37253         /** @private */
37254         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37255         this.el.addClass("roo-splitbar-h");
37256     }else{
37257         /** @private */
37258         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37259         this.el.addClass("roo-splitbar-v");
37260     }
37261     
37262     this.addEvents({
37263         /**
37264          * @event resize
37265          * Fires when the splitter is moved (alias for {@link #event-moved})
37266          * @param {Roo.bootstrap.SplitBar} this
37267          * @param {Number} newSize the new width or height
37268          */
37269         "resize" : true,
37270         /**
37271          * @event moved
37272          * Fires when the splitter is moved
37273          * @param {Roo.bootstrap.SplitBar} this
37274          * @param {Number} newSize the new width or height
37275          */
37276         "moved" : true,
37277         /**
37278          * @event beforeresize
37279          * Fires before the splitter is dragged
37280          * @param {Roo.bootstrap.SplitBar} this
37281          */
37282         "beforeresize" : true,
37283
37284         "beforeapply" : true
37285     });
37286
37287     Roo.util.Observable.call(this);
37288 };
37289
37290 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37291     onStartProxyDrag : function(x, y){
37292         this.fireEvent("beforeresize", this);
37293         if(!this.overlay){
37294             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37295             o.unselectable();
37296             o.enableDisplayMode("block");
37297             // all splitbars share the same overlay
37298             Roo.bootstrap.SplitBar.prototype.overlay = o;
37299         }
37300         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37301         this.overlay.show();
37302         Roo.get(this.proxy).setDisplayed("block");
37303         var size = this.adapter.getElementSize(this);
37304         this.activeMinSize = this.getMinimumSize();;
37305         this.activeMaxSize = this.getMaximumSize();;
37306         var c1 = size - this.activeMinSize;
37307         var c2 = Math.max(this.activeMaxSize - size, 0);
37308         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37309             this.dd.resetConstraints();
37310             this.dd.setXConstraint(
37311                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37312                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37313             );
37314             this.dd.setYConstraint(0, 0);
37315         }else{
37316             this.dd.resetConstraints();
37317             this.dd.setXConstraint(0, 0);
37318             this.dd.setYConstraint(
37319                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37320                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37321             );
37322          }
37323         this.dragSpecs.startSize = size;
37324         this.dragSpecs.startPoint = [x, y];
37325         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37326     },
37327     
37328     /** 
37329      * @private Called after the drag operation by the DDProxy
37330      */
37331     onEndProxyDrag : function(e){
37332         Roo.get(this.proxy).setDisplayed(false);
37333         var endPoint = Roo.lib.Event.getXY(e);
37334         if(this.overlay){
37335             this.overlay.hide();
37336         }
37337         var newSize;
37338         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37339             newSize = this.dragSpecs.startSize + 
37340                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37341                     endPoint[0] - this.dragSpecs.startPoint[0] :
37342                     this.dragSpecs.startPoint[0] - endPoint[0]
37343                 );
37344         }else{
37345             newSize = this.dragSpecs.startSize + 
37346                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37347                     endPoint[1] - this.dragSpecs.startPoint[1] :
37348                     this.dragSpecs.startPoint[1] - endPoint[1]
37349                 );
37350         }
37351         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37352         if(newSize != this.dragSpecs.startSize){
37353             if(this.fireEvent('beforeapply', this, newSize) !== false){
37354                 this.adapter.setElementSize(this, newSize);
37355                 this.fireEvent("moved", this, newSize);
37356                 this.fireEvent("resize", this, newSize);
37357             }
37358         }
37359     },
37360     
37361     /**
37362      * Get the adapter this SplitBar uses
37363      * @return The adapter object
37364      */
37365     getAdapter : function(){
37366         return this.adapter;
37367     },
37368     
37369     /**
37370      * Set the adapter this SplitBar uses
37371      * @param {Object} adapter A SplitBar adapter object
37372      */
37373     setAdapter : function(adapter){
37374         this.adapter = adapter;
37375         this.adapter.init(this);
37376     },
37377     
37378     /**
37379      * Gets the minimum size for the resizing element
37380      * @return {Number} The minimum size
37381      */
37382     getMinimumSize : function(){
37383         return this.minSize;
37384     },
37385     
37386     /**
37387      * Sets the minimum size for the resizing element
37388      * @param {Number} minSize The minimum size
37389      */
37390     setMinimumSize : function(minSize){
37391         this.minSize = minSize;
37392     },
37393     
37394     /**
37395      * Gets the maximum size for the resizing element
37396      * @return {Number} The maximum size
37397      */
37398     getMaximumSize : function(){
37399         return this.maxSize;
37400     },
37401     
37402     /**
37403      * Sets the maximum size for the resizing element
37404      * @param {Number} maxSize The maximum size
37405      */
37406     setMaximumSize : function(maxSize){
37407         this.maxSize = maxSize;
37408     },
37409     
37410     /**
37411      * Sets the initialize size for the resizing element
37412      * @param {Number} size The initial size
37413      */
37414     setCurrentSize : function(size){
37415         var oldAnimate = this.animate;
37416         this.animate = false;
37417         this.adapter.setElementSize(this, size);
37418         this.animate = oldAnimate;
37419     },
37420     
37421     /**
37422      * Destroy this splitbar. 
37423      * @param {Boolean} removeEl True to remove the element
37424      */
37425     destroy : function(removeEl){
37426         if(this.shim){
37427             this.shim.remove();
37428         }
37429         this.dd.unreg();
37430         this.proxy.parentNode.removeChild(this.proxy);
37431         if(removeEl){
37432             this.el.remove();
37433         }
37434     }
37435 });
37436
37437 /**
37438  * @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.
37439  */
37440 Roo.bootstrap.SplitBar.createProxy = function(dir){
37441     var proxy = new Roo.Element(document.createElement("div"));
37442     proxy.unselectable();
37443     var cls = 'roo-splitbar-proxy';
37444     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37445     document.body.appendChild(proxy.dom);
37446     return proxy.dom;
37447 };
37448
37449 /** 
37450  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37451  * Default Adapter. It assumes the splitter and resizing element are not positioned
37452  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37453  */
37454 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37455 };
37456
37457 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37458     // do nothing for now
37459     init : function(s){
37460     
37461     },
37462     /**
37463      * Called before drag operations to get the current size of the resizing element. 
37464      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37465      */
37466      getElementSize : function(s){
37467         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37468             return s.resizingEl.getWidth();
37469         }else{
37470             return s.resizingEl.getHeight();
37471         }
37472     },
37473     
37474     /**
37475      * Called after drag operations to set the size of the resizing element.
37476      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37477      * @param {Number} newSize The new size to set
37478      * @param {Function} onComplete A function to be invoked when resizing is complete
37479      */
37480     setElementSize : function(s, newSize, onComplete){
37481         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37482             if(!s.animate){
37483                 s.resizingEl.setWidth(newSize);
37484                 if(onComplete){
37485                     onComplete(s, newSize);
37486                 }
37487             }else{
37488                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37489             }
37490         }else{
37491             
37492             if(!s.animate){
37493                 s.resizingEl.setHeight(newSize);
37494                 if(onComplete){
37495                     onComplete(s, newSize);
37496                 }
37497             }else{
37498                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37499             }
37500         }
37501     }
37502 };
37503
37504 /** 
37505  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37506  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37507  * Adapter that  moves the splitter element to align with the resized sizing element. 
37508  * Used with an absolute positioned SplitBar.
37509  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37510  * document.body, make sure you assign an id to the body element.
37511  */
37512 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37513     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37514     this.container = Roo.get(container);
37515 };
37516
37517 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37518     init : function(s){
37519         this.basic.init(s);
37520     },
37521     
37522     getElementSize : function(s){
37523         return this.basic.getElementSize(s);
37524     },
37525     
37526     setElementSize : function(s, newSize, onComplete){
37527         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37528     },
37529     
37530     moveSplitter : function(s){
37531         var yes = Roo.bootstrap.SplitBar;
37532         switch(s.placement){
37533             case yes.LEFT:
37534                 s.el.setX(s.resizingEl.getRight());
37535                 break;
37536             case yes.RIGHT:
37537                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37538                 break;
37539             case yes.TOP:
37540                 s.el.setY(s.resizingEl.getBottom());
37541                 break;
37542             case yes.BOTTOM:
37543                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37544                 break;
37545         }
37546     }
37547 };
37548
37549 /**
37550  * Orientation constant - Create a vertical SplitBar
37551  * @static
37552  * @type Number
37553  */
37554 Roo.bootstrap.SplitBar.VERTICAL = 1;
37555
37556 /**
37557  * Orientation constant - Create a horizontal SplitBar
37558  * @static
37559  * @type Number
37560  */
37561 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37562
37563 /**
37564  * Placement constant - The resizing element is to the left of the splitter element
37565  * @static
37566  * @type Number
37567  */
37568 Roo.bootstrap.SplitBar.LEFT = 1;
37569
37570 /**
37571  * Placement constant - The resizing element is to the right of the splitter element
37572  * @static
37573  * @type Number
37574  */
37575 Roo.bootstrap.SplitBar.RIGHT = 2;
37576
37577 /**
37578  * Placement constant - The resizing element is positioned above the splitter element
37579  * @static
37580  * @type Number
37581  */
37582 Roo.bootstrap.SplitBar.TOP = 3;
37583
37584 /**
37585  * Placement constant - The resizing element is positioned under splitter element
37586  * @static
37587  * @type Number
37588  */
37589 Roo.bootstrap.SplitBar.BOTTOM = 4;
37590 Roo.namespace("Roo.bootstrap.layout");/*
37591  * Based on:
37592  * Ext JS Library 1.1.1
37593  * Copyright(c) 2006-2007, Ext JS, LLC.
37594  *
37595  * Originally Released Under LGPL - original licence link has changed is not relivant.
37596  *
37597  * Fork - LGPL
37598  * <script type="text/javascript">
37599  */
37600
37601 /**
37602  * @class Roo.bootstrap.layout.Manager
37603  * @extends Roo.bootstrap.Component
37604  * Base class for layout managers.
37605  */
37606 Roo.bootstrap.layout.Manager = function(config)
37607 {
37608     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37609
37610
37611
37612
37613
37614     /** false to disable window resize monitoring @type Boolean */
37615     this.monitorWindowResize = true;
37616     this.regions = {};
37617     this.addEvents({
37618         /**
37619          * @event layout
37620          * Fires when a layout is performed.
37621          * @param {Roo.LayoutManager} this
37622          */
37623         "layout" : true,
37624         /**
37625          * @event regionresized
37626          * Fires when the user resizes a region.
37627          * @param {Roo.LayoutRegion} region The resized region
37628          * @param {Number} newSize The new size (width for east/west, height for north/south)
37629          */
37630         "regionresized" : true,
37631         /**
37632          * @event regioncollapsed
37633          * Fires when a region is collapsed.
37634          * @param {Roo.LayoutRegion} region The collapsed region
37635          */
37636         "regioncollapsed" : true,
37637         /**
37638          * @event regionexpanded
37639          * Fires when a region is expanded.
37640          * @param {Roo.LayoutRegion} region The expanded region
37641          */
37642         "regionexpanded" : true
37643     });
37644     this.updating = false;
37645
37646     if (config.el) {
37647         this.el = Roo.get(config.el);
37648         this.initEvents();
37649     }
37650
37651 };
37652
37653 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37654
37655
37656     regions : null,
37657
37658     monitorWindowResize : true,
37659
37660
37661     updating : false,
37662
37663
37664     onRender : function(ct, position)
37665     {
37666         if(!this.el){
37667             this.el = Roo.get(ct);
37668             this.initEvents();
37669         }
37670         //this.fireEvent('render',this);
37671     },
37672
37673
37674     initEvents: function()
37675     {
37676
37677
37678         // ie scrollbar fix
37679         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37680             document.body.scroll = "no";
37681         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37682             this.el.position('relative');
37683         }
37684         this.id = this.el.id;
37685         this.el.addClass("roo-layout-container");
37686         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37687         if(this.el.dom != document.body ) {
37688             this.el.on('resize', this.layout,this);
37689             this.el.on('show', this.layout,this);
37690         }
37691
37692     },
37693
37694     /**
37695      * Returns true if this layout is currently being updated
37696      * @return {Boolean}
37697      */
37698     isUpdating : function(){
37699         return this.updating;
37700     },
37701
37702     /**
37703      * Suspend the LayoutManager from doing auto-layouts while
37704      * making multiple add or remove calls
37705      */
37706     beginUpdate : function(){
37707         this.updating = true;
37708     },
37709
37710     /**
37711      * Restore auto-layouts and optionally disable the manager from performing a layout
37712      * @param {Boolean} noLayout true to disable a layout update
37713      */
37714     endUpdate : function(noLayout){
37715         this.updating = false;
37716         if(!noLayout){
37717             this.layout();
37718         }
37719     },
37720
37721     layout: function(){
37722         // abstract...
37723     },
37724
37725     onRegionResized : function(region, newSize){
37726         this.fireEvent("regionresized", region, newSize);
37727         this.layout();
37728     },
37729
37730     onRegionCollapsed : function(region){
37731         this.fireEvent("regioncollapsed", region);
37732     },
37733
37734     onRegionExpanded : function(region){
37735         this.fireEvent("regionexpanded", region);
37736     },
37737
37738     /**
37739      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37740      * performs box-model adjustments.
37741      * @return {Object} The size as an object {width: (the width), height: (the height)}
37742      */
37743     getViewSize : function()
37744     {
37745         var size;
37746         if(this.el.dom != document.body){
37747             size = this.el.getSize();
37748         }else{
37749             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37750         }
37751         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37752         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37753         return size;
37754     },
37755
37756     /**
37757      * Returns the Element this layout is bound to.
37758      * @return {Roo.Element}
37759      */
37760     getEl : function(){
37761         return this.el;
37762     },
37763
37764     /**
37765      * Returns the specified region.
37766      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37767      * @return {Roo.LayoutRegion}
37768      */
37769     getRegion : function(target){
37770         return this.regions[target.toLowerCase()];
37771     },
37772
37773     onWindowResize : function(){
37774         if(this.monitorWindowResize){
37775             this.layout();
37776         }
37777     }
37778 });
37779 /*
37780  * Based on:
37781  * Ext JS Library 1.1.1
37782  * Copyright(c) 2006-2007, Ext JS, LLC.
37783  *
37784  * Originally Released Under LGPL - original licence link has changed is not relivant.
37785  *
37786  * Fork - LGPL
37787  * <script type="text/javascript">
37788  */
37789 /**
37790  * @class Roo.bootstrap.layout.Border
37791  * @extends Roo.bootstrap.layout.Manager
37792  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37793  * please see: examples/bootstrap/nested.html<br><br>
37794  
37795 <b>The container the layout is rendered into can be either the body element or any other element.
37796 If it is not the body element, the container needs to either be an absolute positioned element,
37797 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37798 the container size if it is not the body element.</b>
37799
37800 * @constructor
37801 * Create a new Border
37802 * @param {Object} config Configuration options
37803  */
37804 Roo.bootstrap.layout.Border = function(config){
37805     config = config || {};
37806     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37807     
37808     
37809     
37810     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37811         if(config[region]){
37812             config[region].region = region;
37813             this.addRegion(config[region]);
37814         }
37815     },this);
37816     
37817 };
37818
37819 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37820
37821 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37822     
37823     parent : false, // this might point to a 'nest' or a ???
37824     
37825     /**
37826      * Creates and adds a new region if it doesn't already exist.
37827      * @param {String} target The target region key (north, south, east, west or center).
37828      * @param {Object} config The regions config object
37829      * @return {BorderLayoutRegion} The new region
37830      */
37831     addRegion : function(config)
37832     {
37833         if(!this.regions[config.region]){
37834             var r = this.factory(config);
37835             this.bindRegion(r);
37836         }
37837         return this.regions[config.region];
37838     },
37839
37840     // private (kinda)
37841     bindRegion : function(r){
37842         this.regions[r.config.region] = r;
37843         
37844         r.on("visibilitychange",    this.layout, this);
37845         r.on("paneladded",          this.layout, this);
37846         r.on("panelremoved",        this.layout, this);
37847         r.on("invalidated",         this.layout, this);
37848         r.on("resized",             this.onRegionResized, this);
37849         r.on("collapsed",           this.onRegionCollapsed, this);
37850         r.on("expanded",            this.onRegionExpanded, this);
37851     },
37852
37853     /**
37854      * Performs a layout update.
37855      */
37856     layout : function()
37857     {
37858         if(this.updating) {
37859             return;
37860         }
37861         
37862         // render all the rebions if they have not been done alreayd?
37863         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37864             if(this.regions[region] && !this.regions[region].bodyEl){
37865                 this.regions[region].onRender(this.el)
37866             }
37867         },this);
37868         
37869         var size = this.getViewSize();
37870         var w = size.width;
37871         var h = size.height;
37872         var centerW = w;
37873         var centerH = h;
37874         var centerY = 0;
37875         var centerX = 0;
37876         //var x = 0, y = 0;
37877
37878         var rs = this.regions;
37879         var north = rs["north"];
37880         var south = rs["south"]; 
37881         var west = rs["west"];
37882         var east = rs["east"];
37883         var center = rs["center"];
37884         //if(this.hideOnLayout){ // not supported anymore
37885             //c.el.setStyle("display", "none");
37886         //}
37887         if(north && north.isVisible()){
37888             var b = north.getBox();
37889             var m = north.getMargins();
37890             b.width = w - (m.left+m.right);
37891             b.x = m.left;
37892             b.y = m.top;
37893             centerY = b.height + b.y + m.bottom;
37894             centerH -= centerY;
37895             north.updateBox(this.safeBox(b));
37896         }
37897         if(south && south.isVisible()){
37898             var b = south.getBox();
37899             var m = south.getMargins();
37900             b.width = w - (m.left+m.right);
37901             b.x = m.left;
37902             var totalHeight = (b.height + m.top + m.bottom);
37903             b.y = h - totalHeight + m.top;
37904             centerH -= totalHeight;
37905             south.updateBox(this.safeBox(b));
37906         }
37907         if(west && west.isVisible()){
37908             var b = west.getBox();
37909             var m = west.getMargins();
37910             b.height = centerH - (m.top+m.bottom);
37911             b.x = m.left;
37912             b.y = centerY + m.top;
37913             var totalWidth = (b.width + m.left + m.right);
37914             centerX += totalWidth;
37915             centerW -= totalWidth;
37916             west.updateBox(this.safeBox(b));
37917         }
37918         if(east && east.isVisible()){
37919             var b = east.getBox();
37920             var m = east.getMargins();
37921             b.height = centerH - (m.top+m.bottom);
37922             var totalWidth = (b.width + m.left + m.right);
37923             b.x = w - totalWidth + m.left;
37924             b.y = centerY + m.top;
37925             centerW -= totalWidth;
37926             east.updateBox(this.safeBox(b));
37927         }
37928         if(center){
37929             var m = center.getMargins();
37930             var centerBox = {
37931                 x: centerX + m.left,
37932                 y: centerY + m.top,
37933                 width: centerW - (m.left+m.right),
37934                 height: centerH - (m.top+m.bottom)
37935             };
37936             //if(this.hideOnLayout){
37937                 //center.el.setStyle("display", "block");
37938             //}
37939             center.updateBox(this.safeBox(centerBox));
37940         }
37941         this.el.repaint();
37942         this.fireEvent("layout", this);
37943     },
37944
37945     // private
37946     safeBox : function(box){
37947         box.width = Math.max(0, box.width);
37948         box.height = Math.max(0, box.height);
37949         return box;
37950     },
37951
37952     /**
37953      * Adds a ContentPanel (or subclass) to this layout.
37954      * @param {String} target The target region key (north, south, east, west or center).
37955      * @param {Roo.ContentPanel} panel The panel to add
37956      * @return {Roo.ContentPanel} The added panel
37957      */
37958     add : function(target, panel){
37959          
37960         target = target.toLowerCase();
37961         return this.regions[target].add(panel);
37962     },
37963
37964     /**
37965      * Remove a ContentPanel (or subclass) to this layout.
37966      * @param {String} target The target region key (north, south, east, west or center).
37967      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37968      * @return {Roo.ContentPanel} The removed panel
37969      */
37970     remove : function(target, panel){
37971         target = target.toLowerCase();
37972         return this.regions[target].remove(panel);
37973     },
37974
37975     /**
37976      * Searches all regions for a panel with the specified id
37977      * @param {String} panelId
37978      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37979      */
37980     findPanel : function(panelId){
37981         var rs = this.regions;
37982         for(var target in rs){
37983             if(typeof rs[target] != "function"){
37984                 var p = rs[target].getPanel(panelId);
37985                 if(p){
37986                     return p;
37987                 }
37988             }
37989         }
37990         return null;
37991     },
37992
37993     /**
37994      * Searches all regions for a panel with the specified id and activates (shows) it.
37995      * @param {String/ContentPanel} panelId The panels id or the panel itself
37996      * @return {Roo.ContentPanel} The shown panel or null
37997      */
37998     showPanel : function(panelId) {
37999       var rs = this.regions;
38000       for(var target in rs){
38001          var r = rs[target];
38002          if(typeof r != "function"){
38003             if(r.hasPanel(panelId)){
38004                return r.showPanel(panelId);
38005             }
38006          }
38007       }
38008       return null;
38009    },
38010
38011    /**
38012      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38013      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38014      */
38015    /*
38016     restoreState : function(provider){
38017         if(!provider){
38018             provider = Roo.state.Manager;
38019         }
38020         var sm = new Roo.LayoutStateManager();
38021         sm.init(this, provider);
38022     },
38023 */
38024  
38025  
38026     /**
38027      * Adds a xtype elements to the layout.
38028      * <pre><code>
38029
38030 layout.addxtype({
38031        xtype : 'ContentPanel',
38032        region: 'west',
38033        items: [ .... ]
38034    }
38035 );
38036
38037 layout.addxtype({
38038         xtype : 'NestedLayoutPanel',
38039         region: 'west',
38040         layout: {
38041            center: { },
38042            west: { }   
38043         },
38044         items : [ ... list of content panels or nested layout panels.. ]
38045    }
38046 );
38047 </code></pre>
38048      * @param {Object} cfg Xtype definition of item to add.
38049      */
38050     addxtype : function(cfg)
38051     {
38052         // basically accepts a pannel...
38053         // can accept a layout region..!?!?
38054         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38055         
38056         
38057         // theory?  children can only be panels??
38058         
38059         //if (!cfg.xtype.match(/Panel$/)) {
38060         //    return false;
38061         //}
38062         var ret = false;
38063         
38064         if (typeof(cfg.region) == 'undefined') {
38065             Roo.log("Failed to add Panel, region was not set");
38066             Roo.log(cfg);
38067             return false;
38068         }
38069         var region = cfg.region;
38070         delete cfg.region;
38071         
38072           
38073         var xitems = [];
38074         if (cfg.items) {
38075             xitems = cfg.items;
38076             delete cfg.items;
38077         }
38078         var nb = false;
38079         
38080         if ( region == 'center') {
38081             Roo.log("Center: " + cfg.title);
38082         }
38083         
38084         
38085         switch(cfg.xtype) 
38086         {
38087             case 'Content':  // ContentPanel (el, cfg)
38088             case 'Scroll':  // ContentPanel (el, cfg)
38089             case 'View': 
38090                 cfg.autoCreate = cfg.autoCreate || true;
38091                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38092                 //} else {
38093                 //    var el = this.el.createChild();
38094                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38095                 //}
38096                 
38097                 this.add(region, ret);
38098                 break;
38099             
38100             /*
38101             case 'TreePanel': // our new panel!
38102                 cfg.el = this.el.createChild();
38103                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38104                 this.add(region, ret);
38105                 break;
38106             */
38107             
38108             case 'Nest': 
38109                 // create a new Layout (which is  a Border Layout...
38110                 
38111                 var clayout = cfg.layout;
38112                 clayout.el  = this.el.createChild();
38113                 clayout.items   = clayout.items  || [];
38114                 
38115                 delete cfg.layout;
38116                 
38117                 // replace this exitems with the clayout ones..
38118                 xitems = clayout.items;
38119                  
38120                 // force background off if it's in center...
38121                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38122                     cfg.background = false;
38123                 }
38124                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38125                 
38126                 
38127                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38128                 //console.log('adding nested layout panel '  + cfg.toSource());
38129                 this.add(region, ret);
38130                 nb = {}; /// find first...
38131                 break;
38132             
38133             case 'Grid':
38134                 
38135                 // needs grid and region
38136                 
38137                 //var el = this.getRegion(region).el.createChild();
38138                 /*
38139                  *var el = this.el.createChild();
38140                 // create the grid first...
38141                 cfg.grid.container = el;
38142                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38143                 */
38144                 
38145                 if (region == 'center' && this.active ) {
38146                     cfg.background = false;
38147                 }
38148                 
38149                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38150                 
38151                 this.add(region, ret);
38152                 /*
38153                 if (cfg.background) {
38154                     // render grid on panel activation (if panel background)
38155                     ret.on('activate', function(gp) {
38156                         if (!gp.grid.rendered) {
38157                     //        gp.grid.render(el);
38158                         }
38159                     });
38160                 } else {
38161                   //  cfg.grid.render(el);
38162                 }
38163                 */
38164                 break;
38165            
38166            
38167             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38168                 // it was the old xcomponent building that caused this before.
38169                 // espeically if border is the top element in the tree.
38170                 ret = this;
38171                 break; 
38172                 
38173                     
38174                 
38175                 
38176                 
38177             default:
38178                 /*
38179                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38180                     
38181                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38182                     this.add(region, ret);
38183                 } else {
38184                 */
38185                     Roo.log(cfg);
38186                     throw "Can not add '" + cfg.xtype + "' to Border";
38187                     return null;
38188              
38189                                 
38190              
38191         }
38192         this.beginUpdate();
38193         // add children..
38194         var region = '';
38195         var abn = {};
38196         Roo.each(xitems, function(i)  {
38197             region = nb && i.region ? i.region : false;
38198             
38199             var add = ret.addxtype(i);
38200            
38201             if (region) {
38202                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38203                 if (!i.background) {
38204                     abn[region] = nb[region] ;
38205                 }
38206             }
38207             
38208         });
38209         this.endUpdate();
38210
38211         // make the last non-background panel active..
38212         //if (nb) { Roo.log(abn); }
38213         if (nb) {
38214             
38215             for(var r in abn) {
38216                 region = this.getRegion(r);
38217                 if (region) {
38218                     // tried using nb[r], but it does not work..
38219                      
38220                     region.showPanel(abn[r]);
38221                    
38222                 }
38223             }
38224         }
38225         return ret;
38226         
38227     },
38228     
38229     
38230 // private
38231     factory : function(cfg)
38232     {
38233         
38234         var validRegions = Roo.bootstrap.layout.Border.regions;
38235
38236         var target = cfg.region;
38237         cfg.mgr = this;
38238         
38239         var r = Roo.bootstrap.layout;
38240         Roo.log(target);
38241         switch(target){
38242             case "north":
38243                 return new r.North(cfg);
38244             case "south":
38245                 return new r.South(cfg);
38246             case "east":
38247                 return new r.East(cfg);
38248             case "west":
38249                 return new r.West(cfg);
38250             case "center":
38251                 return new r.Center(cfg);
38252         }
38253         throw 'Layout region "'+target+'" not supported.';
38254     }
38255     
38256     
38257 });
38258  /*
38259  * Based on:
38260  * Ext JS Library 1.1.1
38261  * Copyright(c) 2006-2007, Ext JS, LLC.
38262  *
38263  * Originally Released Under LGPL - original licence link has changed is not relivant.
38264  *
38265  * Fork - LGPL
38266  * <script type="text/javascript">
38267  */
38268  
38269 /**
38270  * @class Roo.bootstrap.layout.Basic
38271  * @extends Roo.util.Observable
38272  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38273  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38274  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38275  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38276  * @cfg {string}   region  the region that it inhabits..
38277  * @cfg {bool}   skipConfig skip config?
38278  * 
38279
38280  */
38281 Roo.bootstrap.layout.Basic = function(config){
38282     
38283     this.mgr = config.mgr;
38284     
38285     this.position = config.region;
38286     
38287     var skipConfig = config.skipConfig;
38288     
38289     this.events = {
38290         /**
38291          * @scope Roo.BasicLayoutRegion
38292          */
38293         
38294         /**
38295          * @event beforeremove
38296          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38297          * @param {Roo.LayoutRegion} this
38298          * @param {Roo.ContentPanel} panel The panel
38299          * @param {Object} e The cancel event object
38300          */
38301         "beforeremove" : true,
38302         /**
38303          * @event invalidated
38304          * Fires when the layout for this region is changed.
38305          * @param {Roo.LayoutRegion} this
38306          */
38307         "invalidated" : true,
38308         /**
38309          * @event visibilitychange
38310          * Fires when this region is shown or hidden 
38311          * @param {Roo.LayoutRegion} this
38312          * @param {Boolean} visibility true or false
38313          */
38314         "visibilitychange" : true,
38315         /**
38316          * @event paneladded
38317          * Fires when a panel is added. 
38318          * @param {Roo.LayoutRegion} this
38319          * @param {Roo.ContentPanel} panel The panel
38320          */
38321         "paneladded" : true,
38322         /**
38323          * @event panelremoved
38324          * Fires when a panel is removed. 
38325          * @param {Roo.LayoutRegion} this
38326          * @param {Roo.ContentPanel} panel The panel
38327          */
38328         "panelremoved" : true,
38329         /**
38330          * @event beforecollapse
38331          * Fires when this region before collapse.
38332          * @param {Roo.LayoutRegion} this
38333          */
38334         "beforecollapse" : true,
38335         /**
38336          * @event collapsed
38337          * Fires when this region is collapsed.
38338          * @param {Roo.LayoutRegion} this
38339          */
38340         "collapsed" : true,
38341         /**
38342          * @event expanded
38343          * Fires when this region is expanded.
38344          * @param {Roo.LayoutRegion} this
38345          */
38346         "expanded" : true,
38347         /**
38348          * @event slideshow
38349          * Fires when this region is slid into view.
38350          * @param {Roo.LayoutRegion} this
38351          */
38352         "slideshow" : true,
38353         /**
38354          * @event slidehide
38355          * Fires when this region slides out of view. 
38356          * @param {Roo.LayoutRegion} this
38357          */
38358         "slidehide" : true,
38359         /**
38360          * @event panelactivated
38361          * Fires when a panel is activated. 
38362          * @param {Roo.LayoutRegion} this
38363          * @param {Roo.ContentPanel} panel The activated panel
38364          */
38365         "panelactivated" : true,
38366         /**
38367          * @event resized
38368          * Fires when the user resizes this region. 
38369          * @param {Roo.LayoutRegion} this
38370          * @param {Number} newSize The new size (width for east/west, height for north/south)
38371          */
38372         "resized" : true
38373     };
38374     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38375     this.panels = new Roo.util.MixedCollection();
38376     this.panels.getKey = this.getPanelId.createDelegate(this);
38377     this.box = null;
38378     this.activePanel = null;
38379     // ensure listeners are added...
38380     
38381     if (config.listeners || config.events) {
38382         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38383             listeners : config.listeners || {},
38384             events : config.events || {}
38385         });
38386     }
38387     
38388     if(skipConfig !== true){
38389         this.applyConfig(config);
38390     }
38391 };
38392
38393 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38394 {
38395     getPanelId : function(p){
38396         return p.getId();
38397     },
38398     
38399     applyConfig : function(config){
38400         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38401         this.config = config;
38402         
38403     },
38404     
38405     /**
38406      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38407      * the width, for horizontal (north, south) the height.
38408      * @param {Number} newSize The new width or height
38409      */
38410     resizeTo : function(newSize){
38411         var el = this.el ? this.el :
38412                  (this.activePanel ? this.activePanel.getEl() : null);
38413         if(el){
38414             switch(this.position){
38415                 case "east":
38416                 case "west":
38417                     el.setWidth(newSize);
38418                     this.fireEvent("resized", this, newSize);
38419                 break;
38420                 case "north":
38421                 case "south":
38422                     el.setHeight(newSize);
38423                     this.fireEvent("resized", this, newSize);
38424                 break;                
38425             }
38426         }
38427     },
38428     
38429     getBox : function(){
38430         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38431     },
38432     
38433     getMargins : function(){
38434         return this.margins;
38435     },
38436     
38437     updateBox : function(box){
38438         this.box = box;
38439         var el = this.activePanel.getEl();
38440         el.dom.style.left = box.x + "px";
38441         el.dom.style.top = box.y + "px";
38442         this.activePanel.setSize(box.width, box.height);
38443     },
38444     
38445     /**
38446      * Returns the container element for this region.
38447      * @return {Roo.Element}
38448      */
38449     getEl : function(){
38450         return this.activePanel;
38451     },
38452     
38453     /**
38454      * Returns true if this region is currently visible.
38455      * @return {Boolean}
38456      */
38457     isVisible : function(){
38458         return this.activePanel ? true : false;
38459     },
38460     
38461     setActivePanel : function(panel){
38462         panel = this.getPanel(panel);
38463         if(this.activePanel && this.activePanel != panel){
38464             this.activePanel.setActiveState(false);
38465             this.activePanel.getEl().setLeftTop(-10000,-10000);
38466         }
38467         this.activePanel = panel;
38468         panel.setActiveState(true);
38469         if(this.box){
38470             panel.setSize(this.box.width, this.box.height);
38471         }
38472         this.fireEvent("panelactivated", this, panel);
38473         this.fireEvent("invalidated");
38474     },
38475     
38476     /**
38477      * Show the specified panel.
38478      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38479      * @return {Roo.ContentPanel} The shown panel or null
38480      */
38481     showPanel : function(panel){
38482         panel = this.getPanel(panel);
38483         if(panel){
38484             this.setActivePanel(panel);
38485         }
38486         return panel;
38487     },
38488     
38489     /**
38490      * Get the active panel for this region.
38491      * @return {Roo.ContentPanel} The active panel or null
38492      */
38493     getActivePanel : function(){
38494         return this.activePanel;
38495     },
38496     
38497     /**
38498      * Add the passed ContentPanel(s)
38499      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38500      * @return {Roo.ContentPanel} The panel added (if only one was added)
38501      */
38502     add : function(panel){
38503         if(arguments.length > 1){
38504             for(var i = 0, len = arguments.length; i < len; i++) {
38505                 this.add(arguments[i]);
38506             }
38507             return null;
38508         }
38509         if(this.hasPanel(panel)){
38510             this.showPanel(panel);
38511             return panel;
38512         }
38513         var el = panel.getEl();
38514         if(el.dom.parentNode != this.mgr.el.dom){
38515             this.mgr.el.dom.appendChild(el.dom);
38516         }
38517         if(panel.setRegion){
38518             panel.setRegion(this);
38519         }
38520         this.panels.add(panel);
38521         el.setStyle("position", "absolute");
38522         if(!panel.background){
38523             this.setActivePanel(panel);
38524             if(this.config.initialSize && this.panels.getCount()==1){
38525                 this.resizeTo(this.config.initialSize);
38526             }
38527         }
38528         this.fireEvent("paneladded", this, panel);
38529         return panel;
38530     },
38531     
38532     /**
38533      * Returns true if the panel is in this region.
38534      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38535      * @return {Boolean}
38536      */
38537     hasPanel : function(panel){
38538         if(typeof panel == "object"){ // must be panel obj
38539             panel = panel.getId();
38540         }
38541         return this.getPanel(panel) ? true : false;
38542     },
38543     
38544     /**
38545      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38546      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38547      * @param {Boolean} preservePanel Overrides the config preservePanel option
38548      * @return {Roo.ContentPanel} The panel that was removed
38549      */
38550     remove : function(panel, preservePanel){
38551         panel = this.getPanel(panel);
38552         if(!panel){
38553             return null;
38554         }
38555         var e = {};
38556         this.fireEvent("beforeremove", this, panel, e);
38557         if(e.cancel === true){
38558             return null;
38559         }
38560         var panelId = panel.getId();
38561         this.panels.removeKey(panelId);
38562         return panel;
38563     },
38564     
38565     /**
38566      * Returns the panel specified or null if it's not in this region.
38567      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38568      * @return {Roo.ContentPanel}
38569      */
38570     getPanel : function(id){
38571         if(typeof id == "object"){ // must be panel obj
38572             return id;
38573         }
38574         return this.panels.get(id);
38575     },
38576     
38577     /**
38578      * Returns this regions position (north/south/east/west/center).
38579      * @return {String} 
38580      */
38581     getPosition: function(){
38582         return this.position;    
38583     }
38584 });/*
38585  * Based on:
38586  * Ext JS Library 1.1.1
38587  * Copyright(c) 2006-2007, Ext JS, LLC.
38588  *
38589  * Originally Released Under LGPL - original licence link has changed is not relivant.
38590  *
38591  * Fork - LGPL
38592  * <script type="text/javascript">
38593  */
38594  
38595 /**
38596  * @class Roo.bootstrap.layout.Region
38597  * @extends Roo.bootstrap.layout.Basic
38598  * This class represents a region in a layout manager.
38599  
38600  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38601  * @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})
38602  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38603  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38604  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38605  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38606  * @cfg {String}    title           The title for the region (overrides panel titles)
38607  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38608  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38609  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38610  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38611  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38612  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38613  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38614  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38615  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38616  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38617
38618  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38619  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38620  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38621  * @cfg {Number}    width           For East/West panels
38622  * @cfg {Number}    height          For North/South panels
38623  * @cfg {Boolean}   split           To show the splitter
38624  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38625  * 
38626  * @cfg {string}   cls             Extra CSS classes to add to region
38627  * 
38628  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38629  * @cfg {string}   region  the region that it inhabits..
38630  *
38631
38632  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38633  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38634
38635  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38636  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38637  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38638  */
38639 Roo.bootstrap.layout.Region = function(config)
38640 {
38641     this.applyConfig(config);
38642
38643     var mgr = config.mgr;
38644     var pos = config.region;
38645     config.skipConfig = true;
38646     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38647     
38648     if (mgr.el) {
38649         this.onRender(mgr.el);   
38650     }
38651      
38652     this.visible = true;
38653     this.collapsed = false;
38654     this.unrendered_panels = [];
38655 };
38656
38657 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38658
38659     position: '', // set by wrapper (eg. north/south etc..)
38660     unrendered_panels : null,  // unrendered panels.
38661     
38662     tabPosition : false,
38663     
38664     mgr: false, // points to 'Border'
38665     
38666     
38667     createBody : function(){
38668         /** This region's body element 
38669         * @type Roo.Element */
38670         this.bodyEl = this.el.createChild({
38671                 tag: "div",
38672                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38673         });
38674     },
38675
38676     onRender: function(ctr, pos)
38677     {
38678         var dh = Roo.DomHelper;
38679         /** This region's container element 
38680         * @type Roo.Element */
38681         this.el = dh.append(ctr.dom, {
38682                 tag: "div",
38683                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38684             }, true);
38685         /** This region's title element 
38686         * @type Roo.Element */
38687     
38688         this.titleEl = dh.append(this.el.dom,  {
38689                 tag: "div",
38690                 unselectable: "on",
38691                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38692                 children:[
38693                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38694                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38695                 ]
38696             }, true);
38697         
38698         this.titleEl.enableDisplayMode();
38699         /** This region's title text element 
38700         * @type HTMLElement */
38701         this.titleTextEl = this.titleEl.dom.firstChild;
38702         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38703         /*
38704         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38705         this.closeBtn.enableDisplayMode();
38706         this.closeBtn.on("click", this.closeClicked, this);
38707         this.closeBtn.hide();
38708     */
38709         this.createBody(this.config);
38710         if(this.config.hideWhenEmpty){
38711             this.hide();
38712             this.on("paneladded", this.validateVisibility, this);
38713             this.on("panelremoved", this.validateVisibility, this);
38714         }
38715         if(this.autoScroll){
38716             this.bodyEl.setStyle("overflow", "auto");
38717         }else{
38718             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38719         }
38720         //if(c.titlebar !== false){
38721             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38722                 this.titleEl.hide();
38723             }else{
38724                 this.titleEl.show();
38725                 if(this.config.title){
38726                     this.titleTextEl.innerHTML = this.config.title;
38727                 }
38728             }
38729         //}
38730         if(this.config.collapsed){
38731             this.collapse(true);
38732         }
38733         if(this.config.hidden){
38734             this.hide();
38735         }
38736         
38737         if (this.unrendered_panels && this.unrendered_panels.length) {
38738             for (var i =0;i< this.unrendered_panels.length; i++) {
38739                 this.add(this.unrendered_panels[i]);
38740             }
38741             this.unrendered_panels = null;
38742             
38743         }
38744         
38745     },
38746     
38747     applyConfig : function(c)
38748     {
38749         /*
38750          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38751             var dh = Roo.DomHelper;
38752             if(c.titlebar !== false){
38753                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38754                 this.collapseBtn.on("click", this.collapse, this);
38755                 this.collapseBtn.enableDisplayMode();
38756                 /*
38757                 if(c.showPin === true || this.showPin){
38758                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38759                     this.stickBtn.enableDisplayMode();
38760                     this.stickBtn.on("click", this.expand, this);
38761                     this.stickBtn.hide();
38762                 }
38763                 
38764             }
38765             */
38766             /** This region's collapsed element
38767             * @type Roo.Element */
38768             /*
38769              *
38770             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38771                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38772             ]}, true);
38773             
38774             if(c.floatable !== false){
38775                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38776                this.collapsedEl.on("click", this.collapseClick, this);
38777             }
38778
38779             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38780                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38781                    id: "message", unselectable: "on", style:{"float":"left"}});
38782                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38783              }
38784             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38785             this.expandBtn.on("click", this.expand, this);
38786             
38787         }
38788         
38789         if(this.collapseBtn){
38790             this.collapseBtn.setVisible(c.collapsible == true);
38791         }
38792         
38793         this.cmargins = c.cmargins || this.cmargins ||
38794                          (this.position == "west" || this.position == "east" ?
38795                              {top: 0, left: 2, right:2, bottom: 0} :
38796                              {top: 2, left: 0, right:0, bottom: 2});
38797         */
38798         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38799         
38800         
38801         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38802         
38803         this.autoScroll = c.autoScroll || false;
38804         
38805         
38806        
38807         
38808         this.duration = c.duration || .30;
38809         this.slideDuration = c.slideDuration || .45;
38810         this.config = c;
38811        
38812     },
38813     /**
38814      * Returns true if this region is currently visible.
38815      * @return {Boolean}
38816      */
38817     isVisible : function(){
38818         return this.visible;
38819     },
38820
38821     /**
38822      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38823      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38824      */
38825     //setCollapsedTitle : function(title){
38826     //    title = title || "&#160;";
38827      //   if(this.collapsedTitleTextEl){
38828       //      this.collapsedTitleTextEl.innerHTML = title;
38829        // }
38830     //},
38831
38832     getBox : function(){
38833         var b;
38834       //  if(!this.collapsed){
38835             b = this.el.getBox(false, true);
38836        // }else{
38837           //  b = this.collapsedEl.getBox(false, true);
38838         //}
38839         return b;
38840     },
38841
38842     getMargins : function(){
38843         return this.margins;
38844         //return this.collapsed ? this.cmargins : this.margins;
38845     },
38846 /*
38847     highlight : function(){
38848         this.el.addClass("x-layout-panel-dragover");
38849     },
38850
38851     unhighlight : function(){
38852         this.el.removeClass("x-layout-panel-dragover");
38853     },
38854 */
38855     updateBox : function(box)
38856     {
38857         if (!this.bodyEl) {
38858             return; // not rendered yet..
38859         }
38860         
38861         this.box = box;
38862         if(!this.collapsed){
38863             this.el.dom.style.left = box.x + "px";
38864             this.el.dom.style.top = box.y + "px";
38865             this.updateBody(box.width, box.height);
38866         }else{
38867             this.collapsedEl.dom.style.left = box.x + "px";
38868             this.collapsedEl.dom.style.top = box.y + "px";
38869             this.collapsedEl.setSize(box.width, box.height);
38870         }
38871         if(this.tabs){
38872             this.tabs.autoSizeTabs();
38873         }
38874     },
38875
38876     updateBody : function(w, h)
38877     {
38878         if(w !== null){
38879             this.el.setWidth(w);
38880             w -= this.el.getBorderWidth("rl");
38881             if(this.config.adjustments){
38882                 w += this.config.adjustments[0];
38883             }
38884         }
38885         if(h !== null && h > 0){
38886             this.el.setHeight(h);
38887             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38888             h -= this.el.getBorderWidth("tb");
38889             if(this.config.adjustments){
38890                 h += this.config.adjustments[1];
38891             }
38892             this.bodyEl.setHeight(h);
38893             if(this.tabs){
38894                 h = this.tabs.syncHeight(h);
38895             }
38896         }
38897         if(this.panelSize){
38898             w = w !== null ? w : this.panelSize.width;
38899             h = h !== null ? h : this.panelSize.height;
38900         }
38901         if(this.activePanel){
38902             var el = this.activePanel.getEl();
38903             w = w !== null ? w : el.getWidth();
38904             h = h !== null ? h : el.getHeight();
38905             this.panelSize = {width: w, height: h};
38906             this.activePanel.setSize(w, h);
38907         }
38908         if(Roo.isIE && this.tabs){
38909             this.tabs.el.repaint();
38910         }
38911     },
38912
38913     /**
38914      * Returns the container element for this region.
38915      * @return {Roo.Element}
38916      */
38917     getEl : function(){
38918         return this.el;
38919     },
38920
38921     /**
38922      * Hides this region.
38923      */
38924     hide : function(){
38925         //if(!this.collapsed){
38926             this.el.dom.style.left = "-2000px";
38927             this.el.hide();
38928         //}else{
38929          //   this.collapsedEl.dom.style.left = "-2000px";
38930          //   this.collapsedEl.hide();
38931        // }
38932         this.visible = false;
38933         this.fireEvent("visibilitychange", this, false);
38934     },
38935
38936     /**
38937      * Shows this region if it was previously hidden.
38938      */
38939     show : function(){
38940         //if(!this.collapsed){
38941             this.el.show();
38942         //}else{
38943         //    this.collapsedEl.show();
38944        // }
38945         this.visible = true;
38946         this.fireEvent("visibilitychange", this, true);
38947     },
38948 /*
38949     closeClicked : function(){
38950         if(this.activePanel){
38951             this.remove(this.activePanel);
38952         }
38953     },
38954
38955     collapseClick : function(e){
38956         if(this.isSlid){
38957            e.stopPropagation();
38958            this.slideIn();
38959         }else{
38960            e.stopPropagation();
38961            this.slideOut();
38962         }
38963     },
38964 */
38965     /**
38966      * Collapses this region.
38967      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38968      */
38969     /*
38970     collapse : function(skipAnim, skipCheck = false){
38971         if(this.collapsed) {
38972             return;
38973         }
38974         
38975         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38976             
38977             this.collapsed = true;
38978             if(this.split){
38979                 this.split.el.hide();
38980             }
38981             if(this.config.animate && skipAnim !== true){
38982                 this.fireEvent("invalidated", this);
38983                 this.animateCollapse();
38984             }else{
38985                 this.el.setLocation(-20000,-20000);
38986                 this.el.hide();
38987                 this.collapsedEl.show();
38988                 this.fireEvent("collapsed", this);
38989                 this.fireEvent("invalidated", this);
38990             }
38991         }
38992         
38993     },
38994 */
38995     animateCollapse : function(){
38996         // overridden
38997     },
38998
38999     /**
39000      * Expands this region if it was previously collapsed.
39001      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39002      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39003      */
39004     /*
39005     expand : function(e, skipAnim){
39006         if(e) {
39007             e.stopPropagation();
39008         }
39009         if(!this.collapsed || this.el.hasActiveFx()) {
39010             return;
39011         }
39012         if(this.isSlid){
39013             this.afterSlideIn();
39014             skipAnim = true;
39015         }
39016         this.collapsed = false;
39017         if(this.config.animate && skipAnim !== true){
39018             this.animateExpand();
39019         }else{
39020             this.el.show();
39021             if(this.split){
39022                 this.split.el.show();
39023             }
39024             this.collapsedEl.setLocation(-2000,-2000);
39025             this.collapsedEl.hide();
39026             this.fireEvent("invalidated", this);
39027             this.fireEvent("expanded", this);
39028         }
39029     },
39030 */
39031     animateExpand : function(){
39032         // overridden
39033     },
39034
39035     initTabs : function()
39036     {
39037         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39038         
39039         var ts = new Roo.bootstrap.panel.Tabs({
39040             el: this.bodyEl.dom,
39041             region : this,
39042             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39043             disableTooltips: this.config.disableTabTips,
39044             toolbar : this.config.toolbar
39045         });
39046         
39047         if(this.config.hideTabs){
39048             ts.stripWrap.setDisplayed(false);
39049         }
39050         this.tabs = ts;
39051         ts.resizeTabs = this.config.resizeTabs === true;
39052         ts.minTabWidth = this.config.minTabWidth || 40;
39053         ts.maxTabWidth = this.config.maxTabWidth || 250;
39054         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39055         ts.monitorResize = false;
39056         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39057         ts.bodyEl.addClass('roo-layout-tabs-body');
39058         this.panels.each(this.initPanelAsTab, this);
39059     },
39060
39061     initPanelAsTab : function(panel){
39062         var ti = this.tabs.addTab(
39063             panel.getEl().id,
39064             panel.getTitle(),
39065             null,
39066             this.config.closeOnTab && panel.isClosable(),
39067             panel.tpl
39068         );
39069         if(panel.tabTip !== undefined){
39070             ti.setTooltip(panel.tabTip);
39071         }
39072         ti.on("activate", function(){
39073               this.setActivePanel(panel);
39074         }, this);
39075         
39076         if(this.config.closeOnTab){
39077             ti.on("beforeclose", function(t, e){
39078                 e.cancel = true;
39079                 this.remove(panel);
39080             }, this);
39081         }
39082         
39083         panel.tabItem = ti;
39084         
39085         return ti;
39086     },
39087
39088     updatePanelTitle : function(panel, title)
39089     {
39090         if(this.activePanel == panel){
39091             this.updateTitle(title);
39092         }
39093         if(this.tabs){
39094             var ti = this.tabs.getTab(panel.getEl().id);
39095             ti.setText(title);
39096             if(panel.tabTip !== undefined){
39097                 ti.setTooltip(panel.tabTip);
39098             }
39099         }
39100     },
39101
39102     updateTitle : function(title){
39103         if(this.titleTextEl && !this.config.title){
39104             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39105         }
39106     },
39107
39108     setActivePanel : function(panel)
39109     {
39110         panel = this.getPanel(panel);
39111         if(this.activePanel && this.activePanel != panel){
39112             if(this.activePanel.setActiveState(false) === false){
39113                 return;
39114             }
39115         }
39116         this.activePanel = panel;
39117         panel.setActiveState(true);
39118         if(this.panelSize){
39119             panel.setSize(this.panelSize.width, this.panelSize.height);
39120         }
39121         if(this.closeBtn){
39122             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39123         }
39124         this.updateTitle(panel.getTitle());
39125         if(this.tabs){
39126             this.fireEvent("invalidated", this);
39127         }
39128         this.fireEvent("panelactivated", this, panel);
39129     },
39130
39131     /**
39132      * Shows the specified panel.
39133      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39134      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39135      */
39136     showPanel : function(panel)
39137     {
39138         panel = this.getPanel(panel);
39139         if(panel){
39140             if(this.tabs){
39141                 var tab = this.tabs.getTab(panel.getEl().id);
39142                 if(tab.isHidden()){
39143                     this.tabs.unhideTab(tab.id);
39144                 }
39145                 tab.activate();
39146             }else{
39147                 this.setActivePanel(panel);
39148             }
39149         }
39150         return panel;
39151     },
39152
39153     /**
39154      * Get the active panel for this region.
39155      * @return {Roo.ContentPanel} The active panel or null
39156      */
39157     getActivePanel : function(){
39158         return this.activePanel;
39159     },
39160
39161     validateVisibility : function(){
39162         if(this.panels.getCount() < 1){
39163             this.updateTitle("&#160;");
39164             this.closeBtn.hide();
39165             this.hide();
39166         }else{
39167             if(!this.isVisible()){
39168                 this.show();
39169             }
39170         }
39171     },
39172
39173     /**
39174      * Adds the passed ContentPanel(s) to this region.
39175      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39176      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39177      */
39178     add : function(panel)
39179     {
39180         if(arguments.length > 1){
39181             for(var i = 0, len = arguments.length; i < len; i++) {
39182                 this.add(arguments[i]);
39183             }
39184             return null;
39185         }
39186         
39187         // if we have not been rendered yet, then we can not really do much of this..
39188         if (!this.bodyEl) {
39189             this.unrendered_panels.push(panel);
39190             return panel;
39191         }
39192         
39193         
39194         
39195         
39196         if(this.hasPanel(panel)){
39197             this.showPanel(panel);
39198             return panel;
39199         }
39200         panel.setRegion(this);
39201         this.panels.add(panel);
39202        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39203             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39204             // and hide them... ???
39205             this.bodyEl.dom.appendChild(panel.getEl().dom);
39206             if(panel.background !== true){
39207                 this.setActivePanel(panel);
39208             }
39209             this.fireEvent("paneladded", this, panel);
39210             return panel;
39211         }
39212         */
39213         if(!this.tabs){
39214             this.initTabs();
39215         }else{
39216             this.initPanelAsTab(panel);
39217         }
39218         
39219         
39220         if(panel.background !== true){
39221             this.tabs.activate(panel.getEl().id);
39222         }
39223         this.fireEvent("paneladded", this, panel);
39224         return panel;
39225     },
39226
39227     /**
39228      * Hides the tab for the specified panel.
39229      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39230      */
39231     hidePanel : function(panel){
39232         if(this.tabs && (panel = this.getPanel(panel))){
39233             this.tabs.hideTab(panel.getEl().id);
39234         }
39235     },
39236
39237     /**
39238      * Unhides the tab for a previously hidden panel.
39239      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39240      */
39241     unhidePanel : function(panel){
39242         if(this.tabs && (panel = this.getPanel(panel))){
39243             this.tabs.unhideTab(panel.getEl().id);
39244         }
39245     },
39246
39247     clearPanels : function(){
39248         while(this.panels.getCount() > 0){
39249              this.remove(this.panels.first());
39250         }
39251     },
39252
39253     /**
39254      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39255      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39256      * @param {Boolean} preservePanel Overrides the config preservePanel option
39257      * @return {Roo.ContentPanel} The panel that was removed
39258      */
39259     remove : function(panel, preservePanel)
39260     {
39261         panel = this.getPanel(panel);
39262         if(!panel){
39263             return null;
39264         }
39265         var e = {};
39266         this.fireEvent("beforeremove", this, panel, e);
39267         if(e.cancel === true){
39268             return null;
39269         }
39270         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39271         var panelId = panel.getId();
39272         this.panels.removeKey(panelId);
39273         if(preservePanel){
39274             document.body.appendChild(panel.getEl().dom);
39275         }
39276         if(this.tabs){
39277             this.tabs.removeTab(panel.getEl().id);
39278         }else if (!preservePanel){
39279             this.bodyEl.dom.removeChild(panel.getEl().dom);
39280         }
39281         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39282             var p = this.panels.first();
39283             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39284             tempEl.appendChild(p.getEl().dom);
39285             this.bodyEl.update("");
39286             this.bodyEl.dom.appendChild(p.getEl().dom);
39287             tempEl = null;
39288             this.updateTitle(p.getTitle());
39289             this.tabs = null;
39290             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39291             this.setActivePanel(p);
39292         }
39293         panel.setRegion(null);
39294         if(this.activePanel == panel){
39295             this.activePanel = null;
39296         }
39297         if(this.config.autoDestroy !== false && preservePanel !== true){
39298             try{panel.destroy();}catch(e){}
39299         }
39300         this.fireEvent("panelremoved", this, panel);
39301         return panel;
39302     },
39303
39304     /**
39305      * Returns the TabPanel component used by this region
39306      * @return {Roo.TabPanel}
39307      */
39308     getTabs : function(){
39309         return this.tabs;
39310     },
39311
39312     createTool : function(parentEl, className){
39313         var btn = Roo.DomHelper.append(parentEl, {
39314             tag: "div",
39315             cls: "x-layout-tools-button",
39316             children: [ {
39317                 tag: "div",
39318                 cls: "roo-layout-tools-button-inner " + className,
39319                 html: "&#160;"
39320             }]
39321         }, true);
39322         btn.addClassOnOver("roo-layout-tools-button-over");
39323         return btn;
39324     }
39325 });/*
39326  * Based on:
39327  * Ext JS Library 1.1.1
39328  * Copyright(c) 2006-2007, Ext JS, LLC.
39329  *
39330  * Originally Released Under LGPL - original licence link has changed is not relivant.
39331  *
39332  * Fork - LGPL
39333  * <script type="text/javascript">
39334  */
39335  
39336
39337
39338 /**
39339  * @class Roo.SplitLayoutRegion
39340  * @extends Roo.LayoutRegion
39341  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39342  */
39343 Roo.bootstrap.layout.Split = function(config){
39344     this.cursor = config.cursor;
39345     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39346 };
39347
39348 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39349 {
39350     splitTip : "Drag to resize.",
39351     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39352     useSplitTips : false,
39353
39354     applyConfig : function(config){
39355         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39356     },
39357     
39358     onRender : function(ctr,pos) {
39359         
39360         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39361         if(!this.config.split){
39362             return;
39363         }
39364         if(!this.split){
39365             
39366             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39367                             tag: "div",
39368                             id: this.el.id + "-split",
39369                             cls: "roo-layout-split roo-layout-split-"+this.position,
39370                             html: "&#160;"
39371             });
39372             /** The SplitBar for this region 
39373             * @type Roo.SplitBar */
39374             // does not exist yet...
39375             Roo.log([this.position, this.orientation]);
39376             
39377             this.split = new Roo.bootstrap.SplitBar({
39378                 dragElement : splitEl,
39379                 resizingElement: this.el,
39380                 orientation : this.orientation
39381             });
39382             
39383             this.split.on("moved", this.onSplitMove, this);
39384             this.split.useShim = this.config.useShim === true;
39385             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39386             if(this.useSplitTips){
39387                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39388             }
39389             //if(config.collapsible){
39390             //    this.split.el.on("dblclick", this.collapse,  this);
39391             //}
39392         }
39393         if(typeof this.config.minSize != "undefined"){
39394             this.split.minSize = this.config.minSize;
39395         }
39396         if(typeof this.config.maxSize != "undefined"){
39397             this.split.maxSize = this.config.maxSize;
39398         }
39399         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39400             this.hideSplitter();
39401         }
39402         
39403     },
39404
39405     getHMaxSize : function(){
39406          var cmax = this.config.maxSize || 10000;
39407          var center = this.mgr.getRegion("center");
39408          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39409     },
39410
39411     getVMaxSize : function(){
39412          var cmax = this.config.maxSize || 10000;
39413          var center = this.mgr.getRegion("center");
39414          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39415     },
39416
39417     onSplitMove : function(split, newSize){
39418         this.fireEvent("resized", this, newSize);
39419     },
39420     
39421     /** 
39422      * Returns the {@link Roo.SplitBar} for this region.
39423      * @return {Roo.SplitBar}
39424      */
39425     getSplitBar : function(){
39426         return this.split;
39427     },
39428     
39429     hide : function(){
39430         this.hideSplitter();
39431         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39432     },
39433
39434     hideSplitter : function(){
39435         if(this.split){
39436             this.split.el.setLocation(-2000,-2000);
39437             this.split.el.hide();
39438         }
39439     },
39440
39441     show : function(){
39442         if(this.split){
39443             this.split.el.show();
39444         }
39445         Roo.bootstrap.layout.Split.superclass.show.call(this);
39446     },
39447     
39448     beforeSlide: function(){
39449         if(Roo.isGecko){// firefox overflow auto bug workaround
39450             this.bodyEl.clip();
39451             if(this.tabs) {
39452                 this.tabs.bodyEl.clip();
39453             }
39454             if(this.activePanel){
39455                 this.activePanel.getEl().clip();
39456                 
39457                 if(this.activePanel.beforeSlide){
39458                     this.activePanel.beforeSlide();
39459                 }
39460             }
39461         }
39462     },
39463     
39464     afterSlide : function(){
39465         if(Roo.isGecko){// firefox overflow auto bug workaround
39466             this.bodyEl.unclip();
39467             if(this.tabs) {
39468                 this.tabs.bodyEl.unclip();
39469             }
39470             if(this.activePanel){
39471                 this.activePanel.getEl().unclip();
39472                 if(this.activePanel.afterSlide){
39473                     this.activePanel.afterSlide();
39474                 }
39475             }
39476         }
39477     },
39478
39479     initAutoHide : function(){
39480         if(this.autoHide !== false){
39481             if(!this.autoHideHd){
39482                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39483                 this.autoHideHd = {
39484                     "mouseout": function(e){
39485                         if(!e.within(this.el, true)){
39486                             st.delay(500);
39487                         }
39488                     },
39489                     "mouseover" : function(e){
39490                         st.cancel();
39491                     },
39492                     scope : this
39493                 };
39494             }
39495             this.el.on(this.autoHideHd);
39496         }
39497     },
39498
39499     clearAutoHide : function(){
39500         if(this.autoHide !== false){
39501             this.el.un("mouseout", this.autoHideHd.mouseout);
39502             this.el.un("mouseover", this.autoHideHd.mouseover);
39503         }
39504     },
39505
39506     clearMonitor : function(){
39507         Roo.get(document).un("click", this.slideInIf, this);
39508     },
39509
39510     // these names are backwards but not changed for compat
39511     slideOut : function(){
39512         if(this.isSlid || this.el.hasActiveFx()){
39513             return;
39514         }
39515         this.isSlid = true;
39516         if(this.collapseBtn){
39517             this.collapseBtn.hide();
39518         }
39519         this.closeBtnState = this.closeBtn.getStyle('display');
39520         this.closeBtn.hide();
39521         if(this.stickBtn){
39522             this.stickBtn.show();
39523         }
39524         this.el.show();
39525         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39526         this.beforeSlide();
39527         this.el.setStyle("z-index", 10001);
39528         this.el.slideIn(this.getSlideAnchor(), {
39529             callback: function(){
39530                 this.afterSlide();
39531                 this.initAutoHide();
39532                 Roo.get(document).on("click", this.slideInIf, this);
39533                 this.fireEvent("slideshow", this);
39534             },
39535             scope: this,
39536             block: true
39537         });
39538     },
39539
39540     afterSlideIn : function(){
39541         this.clearAutoHide();
39542         this.isSlid = false;
39543         this.clearMonitor();
39544         this.el.setStyle("z-index", "");
39545         if(this.collapseBtn){
39546             this.collapseBtn.show();
39547         }
39548         this.closeBtn.setStyle('display', this.closeBtnState);
39549         if(this.stickBtn){
39550             this.stickBtn.hide();
39551         }
39552         this.fireEvent("slidehide", this);
39553     },
39554
39555     slideIn : function(cb){
39556         if(!this.isSlid || this.el.hasActiveFx()){
39557             Roo.callback(cb);
39558             return;
39559         }
39560         this.isSlid = false;
39561         this.beforeSlide();
39562         this.el.slideOut(this.getSlideAnchor(), {
39563             callback: function(){
39564                 this.el.setLeftTop(-10000, -10000);
39565                 this.afterSlide();
39566                 this.afterSlideIn();
39567                 Roo.callback(cb);
39568             },
39569             scope: this,
39570             block: true
39571         });
39572     },
39573     
39574     slideInIf : function(e){
39575         if(!e.within(this.el)){
39576             this.slideIn();
39577         }
39578     },
39579
39580     animateCollapse : function(){
39581         this.beforeSlide();
39582         this.el.setStyle("z-index", 20000);
39583         var anchor = this.getSlideAnchor();
39584         this.el.slideOut(anchor, {
39585             callback : function(){
39586                 this.el.setStyle("z-index", "");
39587                 this.collapsedEl.slideIn(anchor, {duration:.3});
39588                 this.afterSlide();
39589                 this.el.setLocation(-10000,-10000);
39590                 this.el.hide();
39591                 this.fireEvent("collapsed", this);
39592             },
39593             scope: this,
39594             block: true
39595         });
39596     },
39597
39598     animateExpand : function(){
39599         this.beforeSlide();
39600         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39601         this.el.setStyle("z-index", 20000);
39602         this.collapsedEl.hide({
39603             duration:.1
39604         });
39605         this.el.slideIn(this.getSlideAnchor(), {
39606             callback : function(){
39607                 this.el.setStyle("z-index", "");
39608                 this.afterSlide();
39609                 if(this.split){
39610                     this.split.el.show();
39611                 }
39612                 this.fireEvent("invalidated", this);
39613                 this.fireEvent("expanded", this);
39614             },
39615             scope: this,
39616             block: true
39617         });
39618     },
39619
39620     anchors : {
39621         "west" : "left",
39622         "east" : "right",
39623         "north" : "top",
39624         "south" : "bottom"
39625     },
39626
39627     sanchors : {
39628         "west" : "l",
39629         "east" : "r",
39630         "north" : "t",
39631         "south" : "b"
39632     },
39633
39634     canchors : {
39635         "west" : "tl-tr",
39636         "east" : "tr-tl",
39637         "north" : "tl-bl",
39638         "south" : "bl-tl"
39639     },
39640
39641     getAnchor : function(){
39642         return this.anchors[this.position];
39643     },
39644
39645     getCollapseAnchor : function(){
39646         return this.canchors[this.position];
39647     },
39648
39649     getSlideAnchor : function(){
39650         return this.sanchors[this.position];
39651     },
39652
39653     getAlignAdj : function(){
39654         var cm = this.cmargins;
39655         switch(this.position){
39656             case "west":
39657                 return [0, 0];
39658             break;
39659             case "east":
39660                 return [0, 0];
39661             break;
39662             case "north":
39663                 return [0, 0];
39664             break;
39665             case "south":
39666                 return [0, 0];
39667             break;
39668         }
39669     },
39670
39671     getExpandAdj : function(){
39672         var c = this.collapsedEl, cm = this.cmargins;
39673         switch(this.position){
39674             case "west":
39675                 return [-(cm.right+c.getWidth()+cm.left), 0];
39676             break;
39677             case "east":
39678                 return [cm.right+c.getWidth()+cm.left, 0];
39679             break;
39680             case "north":
39681                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39682             break;
39683             case "south":
39684                 return [0, cm.top+cm.bottom+c.getHeight()];
39685             break;
39686         }
39687     }
39688 });/*
39689  * Based on:
39690  * Ext JS Library 1.1.1
39691  * Copyright(c) 2006-2007, Ext JS, LLC.
39692  *
39693  * Originally Released Under LGPL - original licence link has changed is not relivant.
39694  *
39695  * Fork - LGPL
39696  * <script type="text/javascript">
39697  */
39698 /*
39699  * These classes are private internal classes
39700  */
39701 Roo.bootstrap.layout.Center = function(config){
39702     config.region = "center";
39703     Roo.bootstrap.layout.Region.call(this, config);
39704     this.visible = true;
39705     this.minWidth = config.minWidth || 20;
39706     this.minHeight = config.minHeight || 20;
39707 };
39708
39709 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39710     hide : function(){
39711         // center panel can't be hidden
39712     },
39713     
39714     show : function(){
39715         // center panel can't be hidden
39716     },
39717     
39718     getMinWidth: function(){
39719         return this.minWidth;
39720     },
39721     
39722     getMinHeight: function(){
39723         return this.minHeight;
39724     }
39725 });
39726
39727
39728
39729
39730  
39731
39732
39733
39734
39735
39736
39737 Roo.bootstrap.layout.North = function(config)
39738 {
39739     config.region = 'north';
39740     config.cursor = 'n-resize';
39741     
39742     Roo.bootstrap.layout.Split.call(this, config);
39743     
39744     
39745     if(this.split){
39746         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39747         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39748         this.split.el.addClass("roo-layout-split-v");
39749     }
39750     //var size = config.initialSize || config.height;
39751     //if(this.el && typeof size != "undefined"){
39752     //    this.el.setHeight(size);
39753     //}
39754 };
39755 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39756 {
39757     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39758      
39759      
39760     onRender : function(ctr, pos)
39761     {
39762         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39763         var size = this.config.initialSize || this.config.height;
39764         if(this.el && typeof size != "undefined"){
39765             this.el.setHeight(size);
39766         }
39767     
39768     },
39769     
39770     getBox : function(){
39771         if(this.collapsed){
39772             return this.collapsedEl.getBox();
39773         }
39774         var box = this.el.getBox();
39775         if(this.split){
39776             box.height += this.split.el.getHeight();
39777         }
39778         return box;
39779     },
39780     
39781     updateBox : function(box){
39782         if(this.split && !this.collapsed){
39783             box.height -= this.split.el.getHeight();
39784             this.split.el.setLeft(box.x);
39785             this.split.el.setTop(box.y+box.height);
39786             this.split.el.setWidth(box.width);
39787         }
39788         if(this.collapsed){
39789             this.updateBody(box.width, null);
39790         }
39791         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39792     }
39793 });
39794
39795
39796
39797
39798
39799 Roo.bootstrap.layout.South = function(config){
39800     config.region = 'south';
39801     config.cursor = 's-resize';
39802     Roo.bootstrap.layout.Split.call(this, config);
39803     if(this.split){
39804         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39805         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39806         this.split.el.addClass("roo-layout-split-v");
39807     }
39808     
39809 };
39810
39811 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39812     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39813     
39814     onRender : function(ctr, pos)
39815     {
39816         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39817         var size = this.config.initialSize || this.config.height;
39818         if(this.el && typeof size != "undefined"){
39819             this.el.setHeight(size);
39820         }
39821     
39822     },
39823     
39824     getBox : function(){
39825         if(this.collapsed){
39826             return this.collapsedEl.getBox();
39827         }
39828         var box = this.el.getBox();
39829         if(this.split){
39830             var sh = this.split.el.getHeight();
39831             box.height += sh;
39832             box.y -= sh;
39833         }
39834         return box;
39835     },
39836     
39837     updateBox : function(box){
39838         if(this.split && !this.collapsed){
39839             var sh = this.split.el.getHeight();
39840             box.height -= sh;
39841             box.y += sh;
39842             this.split.el.setLeft(box.x);
39843             this.split.el.setTop(box.y-sh);
39844             this.split.el.setWidth(box.width);
39845         }
39846         if(this.collapsed){
39847             this.updateBody(box.width, null);
39848         }
39849         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39850     }
39851 });
39852
39853 Roo.bootstrap.layout.East = function(config){
39854     config.region = "east";
39855     config.cursor = "e-resize";
39856     Roo.bootstrap.layout.Split.call(this, config);
39857     if(this.split){
39858         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39859         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39860         this.split.el.addClass("roo-layout-split-h");
39861     }
39862     
39863 };
39864 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39865     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39866     
39867     onRender : function(ctr, pos)
39868     {
39869         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39870         var size = this.config.initialSize || this.config.width;
39871         if(this.el && typeof size != "undefined"){
39872             this.el.setWidth(size);
39873         }
39874     
39875     },
39876     
39877     getBox : function(){
39878         if(this.collapsed){
39879             return this.collapsedEl.getBox();
39880         }
39881         var box = this.el.getBox();
39882         if(this.split){
39883             var sw = this.split.el.getWidth();
39884             box.width += sw;
39885             box.x -= sw;
39886         }
39887         return box;
39888     },
39889
39890     updateBox : function(box){
39891         if(this.split && !this.collapsed){
39892             var sw = this.split.el.getWidth();
39893             box.width -= sw;
39894             this.split.el.setLeft(box.x);
39895             this.split.el.setTop(box.y);
39896             this.split.el.setHeight(box.height);
39897             box.x += sw;
39898         }
39899         if(this.collapsed){
39900             this.updateBody(null, box.height);
39901         }
39902         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39903     }
39904 });
39905
39906 Roo.bootstrap.layout.West = function(config){
39907     config.region = "west";
39908     config.cursor = "w-resize";
39909     
39910     Roo.bootstrap.layout.Split.call(this, config);
39911     if(this.split){
39912         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39913         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39914         this.split.el.addClass("roo-layout-split-h");
39915     }
39916     
39917 };
39918 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39919     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39920     
39921     onRender: function(ctr, pos)
39922     {
39923         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39924         var size = this.config.initialSize || this.config.width;
39925         if(typeof size != "undefined"){
39926             this.el.setWidth(size);
39927         }
39928     },
39929     
39930     getBox : function(){
39931         if(this.collapsed){
39932             return this.collapsedEl.getBox();
39933         }
39934         var box = this.el.getBox();
39935         if (box.width == 0) {
39936             box.width = this.config.width; // kludge?
39937         }
39938         if(this.split){
39939             box.width += this.split.el.getWidth();
39940         }
39941         return box;
39942     },
39943     
39944     updateBox : function(box){
39945         if(this.split && !this.collapsed){
39946             var sw = this.split.el.getWidth();
39947             box.width -= sw;
39948             this.split.el.setLeft(box.x+box.width);
39949             this.split.el.setTop(box.y);
39950             this.split.el.setHeight(box.height);
39951         }
39952         if(this.collapsed){
39953             this.updateBody(null, box.height);
39954         }
39955         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39956     }
39957 });Roo.namespace("Roo.bootstrap.panel");/*
39958  * Based on:
39959  * Ext JS Library 1.1.1
39960  * Copyright(c) 2006-2007, Ext JS, LLC.
39961  *
39962  * Originally Released Under LGPL - original licence link has changed is not relivant.
39963  *
39964  * Fork - LGPL
39965  * <script type="text/javascript">
39966  */
39967 /**
39968  * @class Roo.ContentPanel
39969  * @extends Roo.util.Observable
39970  * A basic ContentPanel element.
39971  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39972  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39973  * @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
39974  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39975  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39976  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39977  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39978  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39979  * @cfg {String} title          The title for this panel
39980  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39981  * @cfg {String} url            Calls {@link #setUrl} with this value
39982  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39983  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39984  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39985  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39986  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39987  * @cfg {Boolean} badges render the badges
39988  * @cfg {String} cls  extra classes to use  
39989  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39990
39991  * @constructor
39992  * Create a new ContentPanel.
39993  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39994  * @param {String/Object} config A string to set only the title or a config object
39995  * @param {String} content (optional) Set the HTML content for this panel
39996  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39997  */
39998 Roo.bootstrap.panel.Content = function( config){
39999     
40000     this.tpl = config.tpl || false;
40001     
40002     var el = config.el;
40003     var content = config.content;
40004
40005     if(config.autoCreate){ // xtype is available if this is called from factory
40006         el = Roo.id();
40007     }
40008     this.el = Roo.get(el);
40009     if(!this.el && config && config.autoCreate){
40010         if(typeof config.autoCreate == "object"){
40011             if(!config.autoCreate.id){
40012                 config.autoCreate.id = config.id||el;
40013             }
40014             this.el = Roo.DomHelper.append(document.body,
40015                         config.autoCreate, true);
40016         }else{
40017             var elcfg =  {
40018                 tag: "div",
40019                 cls: (config.cls || '') +
40020                     (config.background ? ' bg-' + config.background : '') +
40021                     " roo-layout-inactive-content",
40022                 id: config.id||el
40023             };
40024             if (config.iframe) {
40025                 elcfg.cn = [
40026                     {
40027                         tag : 'iframe',
40028                         style : 'border: 0px',
40029                         src : 'about:blank'
40030                     }
40031                 ];
40032             }
40033               
40034             if (config.html) {
40035                 elcfg.html = config.html;
40036                 
40037             }
40038                         
40039             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40040             if (config.iframe) {
40041                 this.iframeEl = this.el.select('iframe',true).first();
40042             }
40043             
40044         }
40045     } 
40046     this.closable = false;
40047     this.loaded = false;
40048     this.active = false;
40049    
40050       
40051     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40052         
40053         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40054         
40055         this.wrapEl = this.el; //this.el.wrap();
40056         var ti = [];
40057         if (config.toolbar.items) {
40058             ti = config.toolbar.items ;
40059             delete config.toolbar.items ;
40060         }
40061         
40062         var nitems = [];
40063         this.toolbar.render(this.wrapEl, 'before');
40064         for(var i =0;i < ti.length;i++) {
40065           //  Roo.log(['add child', items[i]]);
40066             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40067         }
40068         this.toolbar.items = nitems;
40069         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40070         delete config.toolbar;
40071         
40072     }
40073     /*
40074     // xtype created footer. - not sure if will work as we normally have to render first..
40075     if (this.footer && !this.footer.el && this.footer.xtype) {
40076         if (!this.wrapEl) {
40077             this.wrapEl = this.el.wrap();
40078         }
40079     
40080         this.footer.container = this.wrapEl.createChild();
40081          
40082         this.footer = Roo.factory(this.footer, Roo);
40083         
40084     }
40085     */
40086     
40087      if(typeof config == "string"){
40088         this.title = config;
40089     }else{
40090         Roo.apply(this, config);
40091     }
40092     
40093     if(this.resizeEl){
40094         this.resizeEl = Roo.get(this.resizeEl, true);
40095     }else{
40096         this.resizeEl = this.el;
40097     }
40098     // handle view.xtype
40099     
40100  
40101     
40102     
40103     this.addEvents({
40104         /**
40105          * @event activate
40106          * Fires when this panel is activated. 
40107          * @param {Roo.ContentPanel} this
40108          */
40109         "activate" : true,
40110         /**
40111          * @event deactivate
40112          * Fires when this panel is activated. 
40113          * @param {Roo.ContentPanel} this
40114          */
40115         "deactivate" : true,
40116
40117         /**
40118          * @event resize
40119          * Fires when this panel is resized if fitToFrame is true.
40120          * @param {Roo.ContentPanel} this
40121          * @param {Number} width The width after any component adjustments
40122          * @param {Number} height The height after any component adjustments
40123          */
40124         "resize" : true,
40125         
40126          /**
40127          * @event render
40128          * Fires when this tab is created
40129          * @param {Roo.ContentPanel} this
40130          */
40131         "render" : true
40132         
40133         
40134         
40135     });
40136     
40137
40138     
40139     
40140     if(this.autoScroll && !this.iframe){
40141         this.resizeEl.setStyle("overflow", "auto");
40142     } else {
40143         // fix randome scrolling
40144         //this.el.on('scroll', function() {
40145         //    Roo.log('fix random scolling');
40146         //    this.scrollTo('top',0); 
40147         //});
40148     }
40149     content = content || this.content;
40150     if(content){
40151         this.setContent(content);
40152     }
40153     if(config && config.url){
40154         this.setUrl(this.url, this.params, this.loadOnce);
40155     }
40156     
40157     
40158     
40159     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40160     
40161     if (this.view && typeof(this.view.xtype) != 'undefined') {
40162         this.view.el = this.el.appendChild(document.createElement("div"));
40163         this.view = Roo.factory(this.view); 
40164         this.view.render  &&  this.view.render(false, '');  
40165     }
40166     
40167     
40168     this.fireEvent('render', this);
40169 };
40170
40171 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40172     
40173     cls : '',
40174     background : '',
40175     
40176     tabTip : '',
40177     
40178     iframe : false,
40179     iframeEl : false,
40180     
40181     setRegion : function(region){
40182         this.region = region;
40183         this.setActiveClass(region && !this.background);
40184     },
40185     
40186     
40187     setActiveClass: function(state)
40188     {
40189         if(state){
40190            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40191            this.el.setStyle('position','relative');
40192         }else{
40193            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40194            this.el.setStyle('position', 'absolute');
40195         } 
40196     },
40197     
40198     /**
40199      * Returns the toolbar for this Panel if one was configured. 
40200      * @return {Roo.Toolbar} 
40201      */
40202     getToolbar : function(){
40203         return this.toolbar;
40204     },
40205     
40206     setActiveState : function(active)
40207     {
40208         this.active = active;
40209         this.setActiveClass(active);
40210         if(!active){
40211             if(this.fireEvent("deactivate", this) === false){
40212                 return false;
40213             }
40214             return true;
40215         }
40216         this.fireEvent("activate", this);
40217         return true;
40218     },
40219     /**
40220      * Updates this panel's element (not for iframe)
40221      * @param {String} content The new content
40222      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40223     */
40224     setContent : function(content, loadScripts){
40225         if (this.iframe) {
40226             return;
40227         }
40228         
40229         this.el.update(content, loadScripts);
40230     },
40231
40232     ignoreResize : function(w, h){
40233         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40234             return true;
40235         }else{
40236             this.lastSize = {width: w, height: h};
40237             return false;
40238         }
40239     },
40240     /**
40241      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40242      * @return {Roo.UpdateManager} The UpdateManager
40243      */
40244     getUpdateManager : function(){
40245         if (this.iframe) {
40246             return false;
40247         }
40248         return this.el.getUpdateManager();
40249     },
40250      /**
40251      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40252      * Does not work with IFRAME contents
40253      * @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:
40254 <pre><code>
40255 panel.load({
40256     url: "your-url.php",
40257     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40258     callback: yourFunction,
40259     scope: yourObject, //(optional scope)
40260     discardUrl: false,
40261     nocache: false,
40262     text: "Loading...",
40263     timeout: 30,
40264     scripts: false
40265 });
40266 </code></pre>
40267      
40268      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40269      * 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.
40270      * @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}
40271      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40272      * @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.
40273      * @return {Roo.ContentPanel} this
40274      */
40275     load : function(){
40276         
40277         if (this.iframe) {
40278             return this;
40279         }
40280         
40281         var um = this.el.getUpdateManager();
40282         um.update.apply(um, arguments);
40283         return this;
40284     },
40285
40286
40287     /**
40288      * 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.
40289      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40290      * @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)
40291      * @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)
40292      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40293      */
40294     setUrl : function(url, params, loadOnce){
40295         if (this.iframe) {
40296             this.iframeEl.dom.src = url;
40297             return false;
40298         }
40299         
40300         if(this.refreshDelegate){
40301             this.removeListener("activate", this.refreshDelegate);
40302         }
40303         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40304         this.on("activate", this.refreshDelegate);
40305         return this.el.getUpdateManager();
40306     },
40307     
40308     _handleRefresh : function(url, params, loadOnce){
40309         if(!loadOnce || !this.loaded){
40310             var updater = this.el.getUpdateManager();
40311             updater.update(url, params, this._setLoaded.createDelegate(this));
40312         }
40313     },
40314     
40315     _setLoaded : function(){
40316         this.loaded = true;
40317     }, 
40318     
40319     /**
40320      * Returns this panel's id
40321      * @return {String} 
40322      */
40323     getId : function(){
40324         return this.el.id;
40325     },
40326     
40327     /** 
40328      * Returns this panel's element - used by regiosn to add.
40329      * @return {Roo.Element} 
40330      */
40331     getEl : function(){
40332         return this.wrapEl || this.el;
40333     },
40334     
40335    
40336     
40337     adjustForComponents : function(width, height)
40338     {
40339         //Roo.log('adjustForComponents ');
40340         if(this.resizeEl != this.el){
40341             width -= this.el.getFrameWidth('lr');
40342             height -= this.el.getFrameWidth('tb');
40343         }
40344         if(this.toolbar){
40345             var te = this.toolbar.getEl();
40346             te.setWidth(width);
40347             height -= te.getHeight();
40348         }
40349         if(this.footer){
40350             var te = this.footer.getEl();
40351             te.setWidth(width);
40352             height -= te.getHeight();
40353         }
40354         
40355         
40356         if(this.adjustments){
40357             width += this.adjustments[0];
40358             height += this.adjustments[1];
40359         }
40360         return {"width": width, "height": height};
40361     },
40362     
40363     setSize : function(width, height){
40364         if(this.fitToFrame && !this.ignoreResize(width, height)){
40365             if(this.fitContainer && this.resizeEl != this.el){
40366                 this.el.setSize(width, height);
40367             }
40368             var size = this.adjustForComponents(width, height);
40369             if (this.iframe) {
40370                 this.iframeEl.setSize(width,height);
40371             }
40372             
40373             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40374             this.fireEvent('resize', this, size.width, size.height);
40375             
40376             
40377         }
40378     },
40379     
40380     /**
40381      * Returns this panel's title
40382      * @return {String} 
40383      */
40384     getTitle : function(){
40385         
40386         if (typeof(this.title) != 'object') {
40387             return this.title;
40388         }
40389         
40390         var t = '';
40391         for (var k in this.title) {
40392             if (!this.title.hasOwnProperty(k)) {
40393                 continue;
40394             }
40395             
40396             if (k.indexOf('-') >= 0) {
40397                 var s = k.split('-');
40398                 for (var i = 0; i<s.length; i++) {
40399                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40400                 }
40401             } else {
40402                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40403             }
40404         }
40405         return t;
40406     },
40407     
40408     /**
40409      * Set this panel's title
40410      * @param {String} title
40411      */
40412     setTitle : function(title){
40413         this.title = title;
40414         if(this.region){
40415             this.region.updatePanelTitle(this, title);
40416         }
40417     },
40418     
40419     /**
40420      * Returns true is this panel was configured to be closable
40421      * @return {Boolean} 
40422      */
40423     isClosable : function(){
40424         return this.closable;
40425     },
40426     
40427     beforeSlide : function(){
40428         this.el.clip();
40429         this.resizeEl.clip();
40430     },
40431     
40432     afterSlide : function(){
40433         this.el.unclip();
40434         this.resizeEl.unclip();
40435     },
40436     
40437     /**
40438      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40439      *   Will fail silently if the {@link #setUrl} method has not been called.
40440      *   This does not activate the panel, just updates its content.
40441      */
40442     refresh : function(){
40443         if(this.refreshDelegate){
40444            this.loaded = false;
40445            this.refreshDelegate();
40446         }
40447     },
40448     
40449     /**
40450      * Destroys this panel
40451      */
40452     destroy : function(){
40453         this.el.removeAllListeners();
40454         var tempEl = document.createElement("span");
40455         tempEl.appendChild(this.el.dom);
40456         tempEl.innerHTML = "";
40457         this.el.remove();
40458         this.el = null;
40459     },
40460     
40461     /**
40462      * form - if the content panel contains a form - this is a reference to it.
40463      * @type {Roo.form.Form}
40464      */
40465     form : false,
40466     /**
40467      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40468      *    This contains a reference to it.
40469      * @type {Roo.View}
40470      */
40471     view : false,
40472     
40473       /**
40474      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40475      * <pre><code>
40476
40477 layout.addxtype({
40478        xtype : 'Form',
40479        items: [ .... ]
40480    }
40481 );
40482
40483 </code></pre>
40484      * @param {Object} cfg Xtype definition of item to add.
40485      */
40486     
40487     
40488     getChildContainer: function () {
40489         return this.getEl();
40490     }
40491     
40492     
40493     /*
40494         var  ret = new Roo.factory(cfg);
40495         return ret;
40496         
40497         
40498         // add form..
40499         if (cfg.xtype.match(/^Form$/)) {
40500             
40501             var el;
40502             //if (this.footer) {
40503             //    el = this.footer.container.insertSibling(false, 'before');
40504             //} else {
40505                 el = this.el.createChild();
40506             //}
40507
40508             this.form = new  Roo.form.Form(cfg);
40509             
40510             
40511             if ( this.form.allItems.length) {
40512                 this.form.render(el.dom);
40513             }
40514             return this.form;
40515         }
40516         // should only have one of theses..
40517         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40518             // views.. should not be just added - used named prop 'view''
40519             
40520             cfg.el = this.el.appendChild(document.createElement("div"));
40521             // factory?
40522             
40523             var ret = new Roo.factory(cfg);
40524              
40525              ret.render && ret.render(false, ''); // render blank..
40526             this.view = ret;
40527             return ret;
40528         }
40529         return false;
40530     }
40531     \*/
40532 });
40533  
40534 /**
40535  * @class Roo.bootstrap.panel.Grid
40536  * @extends Roo.bootstrap.panel.Content
40537  * @constructor
40538  * Create a new GridPanel.
40539  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40540  * @param {Object} config A the config object
40541   
40542  */
40543
40544
40545
40546 Roo.bootstrap.panel.Grid = function(config)
40547 {
40548     
40549       
40550     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40551         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40552
40553     config.el = this.wrapper;
40554     //this.el = this.wrapper;
40555     
40556       if (config.container) {
40557         // ctor'ed from a Border/panel.grid
40558         
40559         
40560         this.wrapper.setStyle("overflow", "hidden");
40561         this.wrapper.addClass('roo-grid-container');
40562
40563     }
40564     
40565     
40566     if(config.toolbar){
40567         var tool_el = this.wrapper.createChild();    
40568         this.toolbar = Roo.factory(config.toolbar);
40569         var ti = [];
40570         if (config.toolbar.items) {
40571             ti = config.toolbar.items ;
40572             delete config.toolbar.items ;
40573         }
40574         
40575         var nitems = [];
40576         this.toolbar.render(tool_el);
40577         for(var i =0;i < ti.length;i++) {
40578           //  Roo.log(['add child', items[i]]);
40579             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40580         }
40581         this.toolbar.items = nitems;
40582         
40583         delete config.toolbar;
40584     }
40585     
40586     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40587     config.grid.scrollBody = true;;
40588     config.grid.monitorWindowResize = false; // turn off autosizing
40589     config.grid.autoHeight = false;
40590     config.grid.autoWidth = false;
40591     
40592     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40593     
40594     if (config.background) {
40595         // render grid on panel activation (if panel background)
40596         this.on('activate', function(gp) {
40597             if (!gp.grid.rendered) {
40598                 gp.grid.render(this.wrapper);
40599                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40600             }
40601         });
40602             
40603     } else {
40604         this.grid.render(this.wrapper);
40605         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40606
40607     }
40608     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40609     // ??? needed ??? config.el = this.wrapper;
40610     
40611     
40612     
40613   
40614     // xtype created footer. - not sure if will work as we normally have to render first..
40615     if (this.footer && !this.footer.el && this.footer.xtype) {
40616         
40617         var ctr = this.grid.getView().getFooterPanel(true);
40618         this.footer.dataSource = this.grid.dataSource;
40619         this.footer = Roo.factory(this.footer, Roo);
40620         this.footer.render(ctr);
40621         
40622     }
40623     
40624     
40625     
40626     
40627      
40628 };
40629
40630 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40631     getId : function(){
40632         return this.grid.id;
40633     },
40634     
40635     /**
40636      * Returns the grid for this panel
40637      * @return {Roo.bootstrap.Table} 
40638      */
40639     getGrid : function(){
40640         return this.grid;    
40641     },
40642     
40643     setSize : function(width, height){
40644         if(!this.ignoreResize(width, height)){
40645             var grid = this.grid;
40646             var size = this.adjustForComponents(width, height);
40647             // tfoot is not a footer?
40648           
40649             
40650             var gridel = grid.getGridEl();
40651             gridel.setSize(size.width, size.height);
40652             
40653             var tbd = grid.getGridEl().select('tbody', true).first();
40654             var thd = grid.getGridEl().select('thead',true).first();
40655             var tbf= grid.getGridEl().select('tfoot', true).first();
40656
40657             if (tbf) {
40658                 size.height -= tbf.getHeight();
40659             }
40660             if (thd) {
40661                 size.height -= thd.getHeight();
40662             }
40663             
40664             tbd.setSize(size.width, size.height );
40665             // this is for the account management tab -seems to work there.
40666             var thd = grid.getGridEl().select('thead',true).first();
40667             //if (tbd) {
40668             //    tbd.setSize(size.width, size.height - thd.getHeight());
40669             //}
40670              
40671             grid.autoSize();
40672         }
40673     },
40674      
40675     
40676     
40677     beforeSlide : function(){
40678         this.grid.getView().scroller.clip();
40679     },
40680     
40681     afterSlide : function(){
40682         this.grid.getView().scroller.unclip();
40683     },
40684     
40685     destroy : function(){
40686         this.grid.destroy();
40687         delete this.grid;
40688         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40689     }
40690 });
40691
40692 /**
40693  * @class Roo.bootstrap.panel.Nest
40694  * @extends Roo.bootstrap.panel.Content
40695  * @constructor
40696  * Create a new Panel, that can contain a layout.Border.
40697  * 
40698  * 
40699  * @param {Roo.BorderLayout} layout The layout for this panel
40700  * @param {String/Object} config A string to set only the title or a config object
40701  */
40702 Roo.bootstrap.panel.Nest = function(config)
40703 {
40704     // construct with only one argument..
40705     /* FIXME - implement nicer consturctors
40706     if (layout.layout) {
40707         config = layout;
40708         layout = config.layout;
40709         delete config.layout;
40710     }
40711     if (layout.xtype && !layout.getEl) {
40712         // then layout needs constructing..
40713         layout = Roo.factory(layout, Roo);
40714     }
40715     */
40716     
40717     config.el =  config.layout.getEl();
40718     
40719     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40720     
40721     config.layout.monitorWindowResize = false; // turn off autosizing
40722     this.layout = config.layout;
40723     this.layout.getEl().addClass("roo-layout-nested-layout");
40724     this.layout.parent = this;
40725     
40726     
40727     
40728     
40729 };
40730
40731 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40732
40733     setSize : function(width, height){
40734         if(!this.ignoreResize(width, height)){
40735             var size = this.adjustForComponents(width, height);
40736             var el = this.layout.getEl();
40737             if (size.height < 1) {
40738                 el.setWidth(size.width);   
40739             } else {
40740                 el.setSize(size.width, size.height);
40741             }
40742             var touch = el.dom.offsetWidth;
40743             this.layout.layout();
40744             // ie requires a double layout on the first pass
40745             if(Roo.isIE && !this.initialized){
40746                 this.initialized = true;
40747                 this.layout.layout();
40748             }
40749         }
40750     },
40751     
40752     // activate all subpanels if not currently active..
40753     
40754     setActiveState : function(active){
40755         this.active = active;
40756         this.setActiveClass(active);
40757         
40758         if(!active){
40759             this.fireEvent("deactivate", this);
40760             return;
40761         }
40762         
40763         this.fireEvent("activate", this);
40764         // not sure if this should happen before or after..
40765         if (!this.layout) {
40766             return; // should not happen..
40767         }
40768         var reg = false;
40769         for (var r in this.layout.regions) {
40770             reg = this.layout.getRegion(r);
40771             if (reg.getActivePanel()) {
40772                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40773                 reg.setActivePanel(reg.getActivePanel());
40774                 continue;
40775             }
40776             if (!reg.panels.length) {
40777                 continue;
40778             }
40779             reg.showPanel(reg.getPanel(0));
40780         }
40781         
40782         
40783         
40784         
40785     },
40786     
40787     /**
40788      * Returns the nested BorderLayout for this panel
40789      * @return {Roo.BorderLayout} 
40790      */
40791     getLayout : function(){
40792         return this.layout;
40793     },
40794     
40795      /**
40796      * Adds a xtype elements to the layout of the nested panel
40797      * <pre><code>
40798
40799 panel.addxtype({
40800        xtype : 'ContentPanel',
40801        region: 'west',
40802        items: [ .... ]
40803    }
40804 );
40805
40806 panel.addxtype({
40807         xtype : 'NestedLayoutPanel',
40808         region: 'west',
40809         layout: {
40810            center: { },
40811            west: { }   
40812         },
40813         items : [ ... list of content panels or nested layout panels.. ]
40814    }
40815 );
40816 </code></pre>
40817      * @param {Object} cfg Xtype definition of item to add.
40818      */
40819     addxtype : function(cfg) {
40820         return this.layout.addxtype(cfg);
40821     
40822     }
40823 });/*
40824  * Based on:
40825  * Ext JS Library 1.1.1
40826  * Copyright(c) 2006-2007, Ext JS, LLC.
40827  *
40828  * Originally Released Under LGPL - original licence link has changed is not relivant.
40829  *
40830  * Fork - LGPL
40831  * <script type="text/javascript">
40832  */
40833 /**
40834  * @class Roo.TabPanel
40835  * @extends Roo.util.Observable
40836  * A lightweight tab container.
40837  * <br><br>
40838  * Usage:
40839  * <pre><code>
40840 // basic tabs 1, built from existing content
40841 var tabs = new Roo.TabPanel("tabs1");
40842 tabs.addTab("script", "View Script");
40843 tabs.addTab("markup", "View Markup");
40844 tabs.activate("script");
40845
40846 // more advanced tabs, built from javascript
40847 var jtabs = new Roo.TabPanel("jtabs");
40848 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40849
40850 // set up the UpdateManager
40851 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40852 var updater = tab2.getUpdateManager();
40853 updater.setDefaultUrl("ajax1.htm");
40854 tab2.on('activate', updater.refresh, updater, true);
40855
40856 // Use setUrl for Ajax loading
40857 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40858 tab3.setUrl("ajax2.htm", null, true);
40859
40860 // Disabled tab
40861 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40862 tab4.disable();
40863
40864 jtabs.activate("jtabs-1");
40865  * </code></pre>
40866  * @constructor
40867  * Create a new TabPanel.
40868  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40869  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40870  */
40871 Roo.bootstrap.panel.Tabs = function(config){
40872     /**
40873     * The container element for this TabPanel.
40874     * @type Roo.Element
40875     */
40876     this.el = Roo.get(config.el);
40877     delete config.el;
40878     if(config){
40879         if(typeof config == "boolean"){
40880             this.tabPosition = config ? "bottom" : "top";
40881         }else{
40882             Roo.apply(this, config);
40883         }
40884     }
40885     
40886     if(this.tabPosition == "bottom"){
40887         // if tabs are at the bottom = create the body first.
40888         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40889         this.el.addClass("roo-tabs-bottom");
40890     }
40891     // next create the tabs holders
40892     
40893     if (this.tabPosition == "west"){
40894         
40895         var reg = this.region; // fake it..
40896         while (reg) {
40897             if (!reg.mgr.parent) {
40898                 break;
40899             }
40900             reg = reg.mgr.parent.region;
40901         }
40902         Roo.log("got nest?");
40903         Roo.log(reg);
40904         if (reg.mgr.getRegion('west')) {
40905             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40906             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40907             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40908             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40909             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40910         
40911             
40912         }
40913         
40914         
40915     } else {
40916      
40917         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40918         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40919         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40920         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40921     }
40922     
40923     
40924     if(Roo.isIE){
40925         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40926     }
40927     
40928     // finally - if tabs are at the top, then create the body last..
40929     if(this.tabPosition != "bottom"){
40930         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40931          * @type Roo.Element
40932          */
40933         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40934         this.el.addClass("roo-tabs-top");
40935     }
40936     this.items = [];
40937
40938     this.bodyEl.setStyle("position", "relative");
40939
40940     this.active = null;
40941     this.activateDelegate = this.activate.createDelegate(this);
40942
40943     this.addEvents({
40944         /**
40945          * @event tabchange
40946          * Fires when the active tab changes
40947          * @param {Roo.TabPanel} this
40948          * @param {Roo.TabPanelItem} activePanel The new active tab
40949          */
40950         "tabchange": true,
40951         /**
40952          * @event beforetabchange
40953          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40954          * @param {Roo.TabPanel} this
40955          * @param {Object} e Set cancel to true on this object to cancel the tab change
40956          * @param {Roo.TabPanelItem} tab The tab being changed to
40957          */
40958         "beforetabchange" : true
40959     });
40960
40961     Roo.EventManager.onWindowResize(this.onResize, this);
40962     this.cpad = this.el.getPadding("lr");
40963     this.hiddenCount = 0;
40964
40965
40966     // toolbar on the tabbar support...
40967     if (this.toolbar) {
40968         alert("no toolbar support yet");
40969         this.toolbar  = false;
40970         /*
40971         var tcfg = this.toolbar;
40972         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40973         this.toolbar = new Roo.Toolbar(tcfg);
40974         if (Roo.isSafari) {
40975             var tbl = tcfg.container.child('table', true);
40976             tbl.setAttribute('width', '100%');
40977         }
40978         */
40979         
40980     }
40981    
40982
40983
40984     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40985 };
40986
40987 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40988     /*
40989      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40990      */
40991     tabPosition : "top",
40992     /*
40993      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40994      */
40995     currentTabWidth : 0,
40996     /*
40997      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40998      */
40999     minTabWidth : 40,
41000     /*
41001      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41002      */
41003     maxTabWidth : 250,
41004     /*
41005      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41006      */
41007     preferredTabWidth : 175,
41008     /*
41009      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41010      */
41011     resizeTabs : false,
41012     /*
41013      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41014      */
41015     monitorResize : true,
41016     /*
41017      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41018      */
41019     toolbar : false,  // set by caller..
41020     
41021     region : false, /// set by caller
41022     
41023     disableTooltips : true, // not used yet...
41024
41025     /**
41026      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41027      * @param {String} id The id of the div to use <b>or create</b>
41028      * @param {String} text The text for the tab
41029      * @param {String} content (optional) Content to put in the TabPanelItem body
41030      * @param {Boolean} closable (optional) True to create a close icon on the tab
41031      * @return {Roo.TabPanelItem} The created TabPanelItem
41032      */
41033     addTab : function(id, text, content, closable, tpl)
41034     {
41035         var item = new Roo.bootstrap.panel.TabItem({
41036             panel: this,
41037             id : id,
41038             text : text,
41039             closable : closable,
41040             tpl : tpl
41041         });
41042         this.addTabItem(item);
41043         if(content){
41044             item.setContent(content);
41045         }
41046         return item;
41047     },
41048
41049     /**
41050      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41051      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41052      * @return {Roo.TabPanelItem}
41053      */
41054     getTab : function(id){
41055         return this.items[id];
41056     },
41057
41058     /**
41059      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41060      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41061      */
41062     hideTab : function(id){
41063         var t = this.items[id];
41064         if(!t.isHidden()){
41065            t.setHidden(true);
41066            this.hiddenCount++;
41067            this.autoSizeTabs();
41068         }
41069     },
41070
41071     /**
41072      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41073      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41074      */
41075     unhideTab : function(id){
41076         var t = this.items[id];
41077         if(t.isHidden()){
41078            t.setHidden(false);
41079            this.hiddenCount--;
41080            this.autoSizeTabs();
41081         }
41082     },
41083
41084     /**
41085      * Adds an existing {@link Roo.TabPanelItem}.
41086      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41087      */
41088     addTabItem : function(item)
41089     {
41090         this.items[item.id] = item;
41091         this.items.push(item);
41092         this.autoSizeTabs();
41093       //  if(this.resizeTabs){
41094     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41095   //         this.autoSizeTabs();
41096 //        }else{
41097 //            item.autoSize();
41098        // }
41099     },
41100
41101     /**
41102      * Removes a {@link Roo.TabPanelItem}.
41103      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41104      */
41105     removeTab : function(id){
41106         var items = this.items;
41107         var tab = items[id];
41108         if(!tab) { return; }
41109         var index = items.indexOf(tab);
41110         if(this.active == tab && items.length > 1){
41111             var newTab = this.getNextAvailable(index);
41112             if(newTab) {
41113                 newTab.activate();
41114             }
41115         }
41116         this.stripEl.dom.removeChild(tab.pnode.dom);
41117         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41118             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41119         }
41120         items.splice(index, 1);
41121         delete this.items[tab.id];
41122         tab.fireEvent("close", tab);
41123         tab.purgeListeners();
41124         this.autoSizeTabs();
41125     },
41126
41127     getNextAvailable : function(start){
41128         var items = this.items;
41129         var index = start;
41130         // look for a next tab that will slide over to
41131         // replace the one being removed
41132         while(index < items.length){
41133             var item = items[++index];
41134             if(item && !item.isHidden()){
41135                 return item;
41136             }
41137         }
41138         // if one isn't found select the previous tab (on the left)
41139         index = start;
41140         while(index >= 0){
41141             var item = items[--index];
41142             if(item && !item.isHidden()){
41143                 return item;
41144             }
41145         }
41146         return null;
41147     },
41148
41149     /**
41150      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41151      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41152      */
41153     disableTab : function(id){
41154         var tab = this.items[id];
41155         if(tab && this.active != tab){
41156             tab.disable();
41157         }
41158     },
41159
41160     /**
41161      * Enables a {@link Roo.TabPanelItem} that is disabled.
41162      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41163      */
41164     enableTab : function(id){
41165         var tab = this.items[id];
41166         tab.enable();
41167     },
41168
41169     /**
41170      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41171      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41172      * @return {Roo.TabPanelItem} The TabPanelItem.
41173      */
41174     activate : function(id)
41175     {
41176         //Roo.log('activite:'  + id);
41177         
41178         var tab = this.items[id];
41179         if(!tab){
41180             return null;
41181         }
41182         if(tab == this.active || tab.disabled){
41183             return tab;
41184         }
41185         var e = {};
41186         this.fireEvent("beforetabchange", this, e, tab);
41187         if(e.cancel !== true && !tab.disabled){
41188             if(this.active){
41189                 this.active.hide();
41190             }
41191             this.active = this.items[id];
41192             this.active.show();
41193             this.fireEvent("tabchange", this, this.active);
41194         }
41195         return tab;
41196     },
41197
41198     /**
41199      * Gets the active {@link Roo.TabPanelItem}.
41200      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41201      */
41202     getActiveTab : function(){
41203         return this.active;
41204     },
41205
41206     /**
41207      * Updates the tab body element to fit the height of the container element
41208      * for overflow scrolling
41209      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41210      */
41211     syncHeight : function(targetHeight){
41212         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41213         var bm = this.bodyEl.getMargins();
41214         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41215         this.bodyEl.setHeight(newHeight);
41216         return newHeight;
41217     },
41218
41219     onResize : function(){
41220         if(this.monitorResize){
41221             this.autoSizeTabs();
41222         }
41223     },
41224
41225     /**
41226      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41227      */
41228     beginUpdate : function(){
41229         this.updating = true;
41230     },
41231
41232     /**
41233      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41234      */
41235     endUpdate : function(){
41236         this.updating = false;
41237         this.autoSizeTabs();
41238     },
41239
41240     /**
41241      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41242      */
41243     autoSizeTabs : function()
41244     {
41245         var count = this.items.length;
41246         var vcount = count - this.hiddenCount;
41247         
41248         if (vcount < 2) {
41249             this.stripEl.hide();
41250         } else {
41251             this.stripEl.show();
41252         }
41253         
41254         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41255             return;
41256         }
41257         
41258         
41259         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41260         var availWidth = Math.floor(w / vcount);
41261         var b = this.stripBody;
41262         if(b.getWidth() > w){
41263             var tabs = this.items;
41264             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41265             if(availWidth < this.minTabWidth){
41266                 /*if(!this.sleft){    // incomplete scrolling code
41267                     this.createScrollButtons();
41268                 }
41269                 this.showScroll();
41270                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41271             }
41272         }else{
41273             if(this.currentTabWidth < this.preferredTabWidth){
41274                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41275             }
41276         }
41277     },
41278
41279     /**
41280      * Returns the number of tabs in this TabPanel.
41281      * @return {Number}
41282      */
41283      getCount : function(){
41284          return this.items.length;
41285      },
41286
41287     /**
41288      * Resizes all the tabs to the passed width
41289      * @param {Number} The new width
41290      */
41291     setTabWidth : function(width){
41292         this.currentTabWidth = width;
41293         for(var i = 0, len = this.items.length; i < len; i++) {
41294                 if(!this.items[i].isHidden()) {
41295                 this.items[i].setWidth(width);
41296             }
41297         }
41298     },
41299
41300     /**
41301      * Destroys this TabPanel
41302      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41303      */
41304     destroy : function(removeEl){
41305         Roo.EventManager.removeResizeListener(this.onResize, this);
41306         for(var i = 0, len = this.items.length; i < len; i++){
41307             this.items[i].purgeListeners();
41308         }
41309         if(removeEl === true){
41310             this.el.update("");
41311             this.el.remove();
41312         }
41313     },
41314     
41315     createStrip : function(container)
41316     {
41317         var strip = document.createElement("nav");
41318         strip.className = Roo.bootstrap.version == 4 ?
41319             "navbar-light bg-light" : 
41320             "navbar navbar-default"; //"x-tabs-wrap";
41321         container.appendChild(strip);
41322         return strip;
41323     },
41324     
41325     createStripList : function(strip)
41326     {
41327         // div wrapper for retard IE
41328         // returns the "tr" element.
41329         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41330         //'<div class="x-tabs-strip-wrap">'+
41331           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41332           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41333         return strip.firstChild; //.firstChild.firstChild.firstChild;
41334     },
41335     createBody : function(container)
41336     {
41337         var body = document.createElement("div");
41338         Roo.id(body, "tab-body");
41339         //Roo.fly(body).addClass("x-tabs-body");
41340         Roo.fly(body).addClass("tab-content");
41341         container.appendChild(body);
41342         return body;
41343     },
41344     createItemBody :function(bodyEl, id){
41345         var body = Roo.getDom(id);
41346         if(!body){
41347             body = document.createElement("div");
41348             body.id = id;
41349         }
41350         //Roo.fly(body).addClass("x-tabs-item-body");
41351         Roo.fly(body).addClass("tab-pane");
41352          bodyEl.insertBefore(body, bodyEl.firstChild);
41353         return body;
41354     },
41355     /** @private */
41356     createStripElements :  function(stripEl, text, closable, tpl)
41357     {
41358         var td = document.createElement("li"); // was td..
41359         td.className = 'nav-item';
41360         
41361         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41362         
41363         
41364         stripEl.appendChild(td);
41365         /*if(closable){
41366             td.className = "x-tabs-closable";
41367             if(!this.closeTpl){
41368                 this.closeTpl = new Roo.Template(
41369                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41370                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41371                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41372                 );
41373             }
41374             var el = this.closeTpl.overwrite(td, {"text": text});
41375             var close = el.getElementsByTagName("div")[0];
41376             var inner = el.getElementsByTagName("em")[0];
41377             return {"el": el, "close": close, "inner": inner};
41378         } else {
41379         */
41380         // not sure what this is..
41381 //            if(!this.tabTpl){
41382                 //this.tabTpl = new Roo.Template(
41383                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41384                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41385                 //);
41386 //                this.tabTpl = new Roo.Template(
41387 //                   '<a href="#">' +
41388 //                   '<span unselectable="on"' +
41389 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41390 //                            ' >{text}</span></a>'
41391 //                );
41392 //                
41393 //            }
41394
41395
41396             var template = tpl || this.tabTpl || false;
41397             
41398             if(!template){
41399                 template =  new Roo.Template(
41400                         Roo.bootstrap.version == 4 ? 
41401                             (
41402                                 '<a class="nav-link" href="#" unselectable="on"' +
41403                                      (this.disableTooltips ? '' : ' title="{text}"') +
41404                                      ' >{text}</a>'
41405                             ) : (
41406                                 '<a class="nav-link" href="#">' +
41407                                 '<span unselectable="on"' +
41408                                          (this.disableTooltips ? '' : ' title="{text}"') +
41409                                     ' >{text}</span></a>'
41410                             )
41411                 );
41412             }
41413             
41414             switch (typeof(template)) {
41415                 case 'object' :
41416                     break;
41417                 case 'string' :
41418                     template = new Roo.Template(template);
41419                     break;
41420                 default :
41421                     break;
41422             }
41423             
41424             var el = template.overwrite(td, {"text": text});
41425             
41426             var inner = el.getElementsByTagName("span")[0];
41427             
41428             return {"el": el, "inner": inner};
41429             
41430     }
41431         
41432     
41433 });
41434
41435 /**
41436  * @class Roo.TabPanelItem
41437  * @extends Roo.util.Observable
41438  * Represents an individual item (tab plus body) in a TabPanel.
41439  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41440  * @param {String} id The id of this TabPanelItem
41441  * @param {String} text The text for the tab of this TabPanelItem
41442  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41443  */
41444 Roo.bootstrap.panel.TabItem = function(config){
41445     /**
41446      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41447      * @type Roo.TabPanel
41448      */
41449     this.tabPanel = config.panel;
41450     /**
41451      * The id for this TabPanelItem
41452      * @type String
41453      */
41454     this.id = config.id;
41455     /** @private */
41456     this.disabled = false;
41457     /** @private */
41458     this.text = config.text;
41459     /** @private */
41460     this.loaded = false;
41461     this.closable = config.closable;
41462
41463     /**
41464      * The body element for this TabPanelItem.
41465      * @type Roo.Element
41466      */
41467     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41468     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41469     this.bodyEl.setStyle("display", "block");
41470     this.bodyEl.setStyle("zoom", "1");
41471     //this.hideAction();
41472
41473     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41474     /** @private */
41475     this.el = Roo.get(els.el);
41476     this.inner = Roo.get(els.inner, true);
41477      this.textEl = Roo.bootstrap.version == 4 ?
41478         this.el : Roo.get(this.el.dom.firstChild, true);
41479
41480     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41481     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41482
41483     
41484 //    this.el.on("mousedown", this.onTabMouseDown, this);
41485     this.el.on("click", this.onTabClick, this);
41486     /** @private */
41487     if(config.closable){
41488         var c = Roo.get(els.close, true);
41489         c.dom.title = this.closeText;
41490         c.addClassOnOver("close-over");
41491         c.on("click", this.closeClick, this);
41492      }
41493
41494     this.addEvents({
41495          /**
41496          * @event activate
41497          * Fires when this tab becomes the active tab.
41498          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41499          * @param {Roo.TabPanelItem} this
41500          */
41501         "activate": true,
41502         /**
41503          * @event beforeclose
41504          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41505          * @param {Roo.TabPanelItem} this
41506          * @param {Object} e Set cancel to true on this object to cancel the close.
41507          */
41508         "beforeclose": true,
41509         /**
41510          * @event close
41511          * Fires when this tab is closed.
41512          * @param {Roo.TabPanelItem} this
41513          */
41514          "close": true,
41515         /**
41516          * @event deactivate
41517          * Fires when this tab is no longer the active tab.
41518          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41519          * @param {Roo.TabPanelItem} this
41520          */
41521          "deactivate" : true
41522     });
41523     this.hidden = false;
41524
41525     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41526 };
41527
41528 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41529            {
41530     purgeListeners : function(){
41531        Roo.util.Observable.prototype.purgeListeners.call(this);
41532        this.el.removeAllListeners();
41533     },
41534     /**
41535      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41536      */
41537     show : function(){
41538         this.status_node.addClass("active");
41539         this.showAction();
41540         if(Roo.isOpera){
41541             this.tabPanel.stripWrap.repaint();
41542         }
41543         this.fireEvent("activate", this.tabPanel, this);
41544     },
41545
41546     /**
41547      * Returns true if this tab is the active tab.
41548      * @return {Boolean}
41549      */
41550     isActive : function(){
41551         return this.tabPanel.getActiveTab() == this;
41552     },
41553
41554     /**
41555      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41556      */
41557     hide : function(){
41558         this.status_node.removeClass("active");
41559         this.hideAction();
41560         this.fireEvent("deactivate", this.tabPanel, this);
41561     },
41562
41563     hideAction : function(){
41564         this.bodyEl.hide();
41565         this.bodyEl.setStyle("position", "absolute");
41566         this.bodyEl.setLeft("-20000px");
41567         this.bodyEl.setTop("-20000px");
41568     },
41569
41570     showAction : function(){
41571         this.bodyEl.setStyle("position", "relative");
41572         this.bodyEl.setTop("");
41573         this.bodyEl.setLeft("");
41574         this.bodyEl.show();
41575     },
41576
41577     /**
41578      * Set the tooltip for the tab.
41579      * @param {String} tooltip The tab's tooltip
41580      */
41581     setTooltip : function(text){
41582         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41583             this.textEl.dom.qtip = text;
41584             this.textEl.dom.removeAttribute('title');
41585         }else{
41586             this.textEl.dom.title = text;
41587         }
41588     },
41589
41590     onTabClick : function(e){
41591         e.preventDefault();
41592         this.tabPanel.activate(this.id);
41593     },
41594
41595     onTabMouseDown : function(e){
41596         e.preventDefault();
41597         this.tabPanel.activate(this.id);
41598     },
41599 /*
41600     getWidth : function(){
41601         return this.inner.getWidth();
41602     },
41603
41604     setWidth : function(width){
41605         var iwidth = width - this.linode.getPadding("lr");
41606         this.inner.setWidth(iwidth);
41607         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41608         this.linode.setWidth(width);
41609     },
41610 */
41611     /**
41612      * Show or hide the tab
41613      * @param {Boolean} hidden True to hide or false to show.
41614      */
41615     setHidden : function(hidden){
41616         this.hidden = hidden;
41617         this.linode.setStyle("display", hidden ? "none" : "");
41618     },
41619
41620     /**
41621      * Returns true if this tab is "hidden"
41622      * @return {Boolean}
41623      */
41624     isHidden : function(){
41625         return this.hidden;
41626     },
41627
41628     /**
41629      * Returns the text for this tab
41630      * @return {String}
41631      */
41632     getText : function(){
41633         return this.text;
41634     },
41635     /*
41636     autoSize : function(){
41637         //this.el.beginMeasure();
41638         this.textEl.setWidth(1);
41639         /*
41640          *  #2804 [new] Tabs in Roojs
41641          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41642          */
41643         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41644         //this.el.endMeasure();
41645     //},
41646
41647     /**
41648      * Sets the text for the tab (Note: this also sets the tooltip text)
41649      * @param {String} text The tab's text and tooltip
41650      */
41651     setText : function(text){
41652         this.text = text;
41653         this.textEl.update(text);
41654         this.setTooltip(text);
41655         //if(!this.tabPanel.resizeTabs){
41656         //    this.autoSize();
41657         //}
41658     },
41659     /**
41660      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41661      */
41662     activate : function(){
41663         this.tabPanel.activate(this.id);
41664     },
41665
41666     /**
41667      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41668      */
41669     disable : function(){
41670         if(this.tabPanel.active != this){
41671             this.disabled = true;
41672             this.status_node.addClass("disabled");
41673         }
41674     },
41675
41676     /**
41677      * Enables this TabPanelItem if it was previously disabled.
41678      */
41679     enable : function(){
41680         this.disabled = false;
41681         this.status_node.removeClass("disabled");
41682     },
41683
41684     /**
41685      * Sets the content for this TabPanelItem.
41686      * @param {String} content The content
41687      * @param {Boolean} loadScripts true to look for and load scripts
41688      */
41689     setContent : function(content, loadScripts){
41690         this.bodyEl.update(content, loadScripts);
41691     },
41692
41693     /**
41694      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41695      * @return {Roo.UpdateManager} The UpdateManager
41696      */
41697     getUpdateManager : function(){
41698         return this.bodyEl.getUpdateManager();
41699     },
41700
41701     /**
41702      * Set a URL to be used to load the content for this TabPanelItem.
41703      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41704      * @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)
41705      * @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)
41706      * @return {Roo.UpdateManager} The UpdateManager
41707      */
41708     setUrl : function(url, params, loadOnce){
41709         if(this.refreshDelegate){
41710             this.un('activate', this.refreshDelegate);
41711         }
41712         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41713         this.on("activate", this.refreshDelegate);
41714         return this.bodyEl.getUpdateManager();
41715     },
41716
41717     /** @private */
41718     _handleRefresh : function(url, params, loadOnce){
41719         if(!loadOnce || !this.loaded){
41720             var updater = this.bodyEl.getUpdateManager();
41721             updater.update(url, params, this._setLoaded.createDelegate(this));
41722         }
41723     },
41724
41725     /**
41726      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41727      *   Will fail silently if the setUrl method has not been called.
41728      *   This does not activate the panel, just updates its content.
41729      */
41730     refresh : function(){
41731         if(this.refreshDelegate){
41732            this.loaded = false;
41733            this.refreshDelegate();
41734         }
41735     },
41736
41737     /** @private */
41738     _setLoaded : function(){
41739         this.loaded = true;
41740     },
41741
41742     /** @private */
41743     closeClick : function(e){
41744         var o = {};
41745         e.stopEvent();
41746         this.fireEvent("beforeclose", this, o);
41747         if(o.cancel !== true){
41748             this.tabPanel.removeTab(this.id);
41749         }
41750     },
41751     /**
41752      * The text displayed in the tooltip for the close icon.
41753      * @type String
41754      */
41755     closeText : "Close this tab"
41756 });
41757 /**
41758 *    This script refer to:
41759 *    Title: International Telephone Input
41760 *    Author: Jack O'Connor
41761 *    Code version:  v12.1.12
41762 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41763 **/
41764
41765 Roo.bootstrap.PhoneInputData = function() {
41766     var d = [
41767       [
41768         "Afghanistan (‫افغانستان‬‎)",
41769         "af",
41770         "93"
41771       ],
41772       [
41773         "Albania (Shqipëri)",
41774         "al",
41775         "355"
41776       ],
41777       [
41778         "Algeria (‫الجزائر‬‎)",
41779         "dz",
41780         "213"
41781       ],
41782       [
41783         "American Samoa",
41784         "as",
41785         "1684"
41786       ],
41787       [
41788         "Andorra",
41789         "ad",
41790         "376"
41791       ],
41792       [
41793         "Angola",
41794         "ao",
41795         "244"
41796       ],
41797       [
41798         "Anguilla",
41799         "ai",
41800         "1264"
41801       ],
41802       [
41803         "Antigua and Barbuda",
41804         "ag",
41805         "1268"
41806       ],
41807       [
41808         "Argentina",
41809         "ar",
41810         "54"
41811       ],
41812       [
41813         "Armenia (Հայաստան)",
41814         "am",
41815         "374"
41816       ],
41817       [
41818         "Aruba",
41819         "aw",
41820         "297"
41821       ],
41822       [
41823         "Australia",
41824         "au",
41825         "61",
41826         0
41827       ],
41828       [
41829         "Austria (Österreich)",
41830         "at",
41831         "43"
41832       ],
41833       [
41834         "Azerbaijan (Azərbaycan)",
41835         "az",
41836         "994"
41837       ],
41838       [
41839         "Bahamas",
41840         "bs",
41841         "1242"
41842       ],
41843       [
41844         "Bahrain (‫البحرين‬‎)",
41845         "bh",
41846         "973"
41847       ],
41848       [
41849         "Bangladesh (বাংলাদেশ)",
41850         "bd",
41851         "880"
41852       ],
41853       [
41854         "Barbados",
41855         "bb",
41856         "1246"
41857       ],
41858       [
41859         "Belarus (Беларусь)",
41860         "by",
41861         "375"
41862       ],
41863       [
41864         "Belgium (België)",
41865         "be",
41866         "32"
41867       ],
41868       [
41869         "Belize",
41870         "bz",
41871         "501"
41872       ],
41873       [
41874         "Benin (Bénin)",
41875         "bj",
41876         "229"
41877       ],
41878       [
41879         "Bermuda",
41880         "bm",
41881         "1441"
41882       ],
41883       [
41884         "Bhutan (འབྲུག)",
41885         "bt",
41886         "975"
41887       ],
41888       [
41889         "Bolivia",
41890         "bo",
41891         "591"
41892       ],
41893       [
41894         "Bosnia and Herzegovina (Босна и Херцеговина)",
41895         "ba",
41896         "387"
41897       ],
41898       [
41899         "Botswana",
41900         "bw",
41901         "267"
41902       ],
41903       [
41904         "Brazil (Brasil)",
41905         "br",
41906         "55"
41907       ],
41908       [
41909         "British Indian Ocean Territory",
41910         "io",
41911         "246"
41912       ],
41913       [
41914         "British Virgin Islands",
41915         "vg",
41916         "1284"
41917       ],
41918       [
41919         "Brunei",
41920         "bn",
41921         "673"
41922       ],
41923       [
41924         "Bulgaria (България)",
41925         "bg",
41926         "359"
41927       ],
41928       [
41929         "Burkina Faso",
41930         "bf",
41931         "226"
41932       ],
41933       [
41934         "Burundi (Uburundi)",
41935         "bi",
41936         "257"
41937       ],
41938       [
41939         "Cambodia (កម្ពុជា)",
41940         "kh",
41941         "855"
41942       ],
41943       [
41944         "Cameroon (Cameroun)",
41945         "cm",
41946         "237"
41947       ],
41948       [
41949         "Canada",
41950         "ca",
41951         "1",
41952         1,
41953         ["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"]
41954       ],
41955       [
41956         "Cape Verde (Kabu Verdi)",
41957         "cv",
41958         "238"
41959       ],
41960       [
41961         "Caribbean Netherlands",
41962         "bq",
41963         "599",
41964         1
41965       ],
41966       [
41967         "Cayman Islands",
41968         "ky",
41969         "1345"
41970       ],
41971       [
41972         "Central African Republic (République centrafricaine)",
41973         "cf",
41974         "236"
41975       ],
41976       [
41977         "Chad (Tchad)",
41978         "td",
41979         "235"
41980       ],
41981       [
41982         "Chile",
41983         "cl",
41984         "56"
41985       ],
41986       [
41987         "China (中国)",
41988         "cn",
41989         "86"
41990       ],
41991       [
41992         "Christmas Island",
41993         "cx",
41994         "61",
41995         2
41996       ],
41997       [
41998         "Cocos (Keeling) Islands",
41999         "cc",
42000         "61",
42001         1
42002       ],
42003       [
42004         "Colombia",
42005         "co",
42006         "57"
42007       ],
42008       [
42009         "Comoros (‫جزر القمر‬‎)",
42010         "km",
42011         "269"
42012       ],
42013       [
42014         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42015         "cd",
42016         "243"
42017       ],
42018       [
42019         "Congo (Republic) (Congo-Brazzaville)",
42020         "cg",
42021         "242"
42022       ],
42023       [
42024         "Cook Islands",
42025         "ck",
42026         "682"
42027       ],
42028       [
42029         "Costa Rica",
42030         "cr",
42031         "506"
42032       ],
42033       [
42034         "Côte d’Ivoire",
42035         "ci",
42036         "225"
42037       ],
42038       [
42039         "Croatia (Hrvatska)",
42040         "hr",
42041         "385"
42042       ],
42043       [
42044         "Cuba",
42045         "cu",
42046         "53"
42047       ],
42048       [
42049         "Curaçao",
42050         "cw",
42051         "599",
42052         0
42053       ],
42054       [
42055         "Cyprus (Κύπρος)",
42056         "cy",
42057         "357"
42058       ],
42059       [
42060         "Czech Republic (Česká republika)",
42061         "cz",
42062         "420"
42063       ],
42064       [
42065         "Denmark (Danmark)",
42066         "dk",
42067         "45"
42068       ],
42069       [
42070         "Djibouti",
42071         "dj",
42072         "253"
42073       ],
42074       [
42075         "Dominica",
42076         "dm",
42077         "1767"
42078       ],
42079       [
42080         "Dominican Republic (República Dominicana)",
42081         "do",
42082         "1",
42083         2,
42084         ["809", "829", "849"]
42085       ],
42086       [
42087         "Ecuador",
42088         "ec",
42089         "593"
42090       ],
42091       [
42092         "Egypt (‫مصر‬‎)",
42093         "eg",
42094         "20"
42095       ],
42096       [
42097         "El Salvador",
42098         "sv",
42099         "503"
42100       ],
42101       [
42102         "Equatorial Guinea (Guinea Ecuatorial)",
42103         "gq",
42104         "240"
42105       ],
42106       [
42107         "Eritrea",
42108         "er",
42109         "291"
42110       ],
42111       [
42112         "Estonia (Eesti)",
42113         "ee",
42114         "372"
42115       ],
42116       [
42117         "Ethiopia",
42118         "et",
42119         "251"
42120       ],
42121       [
42122         "Falkland Islands (Islas Malvinas)",
42123         "fk",
42124         "500"
42125       ],
42126       [
42127         "Faroe Islands (Føroyar)",
42128         "fo",
42129         "298"
42130       ],
42131       [
42132         "Fiji",
42133         "fj",
42134         "679"
42135       ],
42136       [
42137         "Finland (Suomi)",
42138         "fi",
42139         "358",
42140         0
42141       ],
42142       [
42143         "France",
42144         "fr",
42145         "33"
42146       ],
42147       [
42148         "French Guiana (Guyane française)",
42149         "gf",
42150         "594"
42151       ],
42152       [
42153         "French Polynesia (Polynésie française)",
42154         "pf",
42155         "689"
42156       ],
42157       [
42158         "Gabon",
42159         "ga",
42160         "241"
42161       ],
42162       [
42163         "Gambia",
42164         "gm",
42165         "220"
42166       ],
42167       [
42168         "Georgia (საქართველო)",
42169         "ge",
42170         "995"
42171       ],
42172       [
42173         "Germany (Deutschland)",
42174         "de",
42175         "49"
42176       ],
42177       [
42178         "Ghana (Gaana)",
42179         "gh",
42180         "233"
42181       ],
42182       [
42183         "Gibraltar",
42184         "gi",
42185         "350"
42186       ],
42187       [
42188         "Greece (Ελλάδα)",
42189         "gr",
42190         "30"
42191       ],
42192       [
42193         "Greenland (Kalaallit Nunaat)",
42194         "gl",
42195         "299"
42196       ],
42197       [
42198         "Grenada",
42199         "gd",
42200         "1473"
42201       ],
42202       [
42203         "Guadeloupe",
42204         "gp",
42205         "590",
42206         0
42207       ],
42208       [
42209         "Guam",
42210         "gu",
42211         "1671"
42212       ],
42213       [
42214         "Guatemala",
42215         "gt",
42216         "502"
42217       ],
42218       [
42219         "Guernsey",
42220         "gg",
42221         "44",
42222         1
42223       ],
42224       [
42225         "Guinea (Guinée)",
42226         "gn",
42227         "224"
42228       ],
42229       [
42230         "Guinea-Bissau (Guiné Bissau)",
42231         "gw",
42232         "245"
42233       ],
42234       [
42235         "Guyana",
42236         "gy",
42237         "592"
42238       ],
42239       [
42240         "Haiti",
42241         "ht",
42242         "509"
42243       ],
42244       [
42245         "Honduras",
42246         "hn",
42247         "504"
42248       ],
42249       [
42250         "Hong Kong (香港)",
42251         "hk",
42252         "852"
42253       ],
42254       [
42255         "Hungary (Magyarország)",
42256         "hu",
42257         "36"
42258       ],
42259       [
42260         "Iceland (Ísland)",
42261         "is",
42262         "354"
42263       ],
42264       [
42265         "India (भारत)",
42266         "in",
42267         "91"
42268       ],
42269       [
42270         "Indonesia",
42271         "id",
42272         "62"
42273       ],
42274       [
42275         "Iran (‫ایران‬‎)",
42276         "ir",
42277         "98"
42278       ],
42279       [
42280         "Iraq (‫العراق‬‎)",
42281         "iq",
42282         "964"
42283       ],
42284       [
42285         "Ireland",
42286         "ie",
42287         "353"
42288       ],
42289       [
42290         "Isle of Man",
42291         "im",
42292         "44",
42293         2
42294       ],
42295       [
42296         "Israel (‫ישראל‬‎)",
42297         "il",
42298         "972"
42299       ],
42300       [
42301         "Italy (Italia)",
42302         "it",
42303         "39",
42304         0
42305       ],
42306       [
42307         "Jamaica",
42308         "jm",
42309         "1876"
42310       ],
42311       [
42312         "Japan (日本)",
42313         "jp",
42314         "81"
42315       ],
42316       [
42317         "Jersey",
42318         "je",
42319         "44",
42320         3
42321       ],
42322       [
42323         "Jordan (‫الأردن‬‎)",
42324         "jo",
42325         "962"
42326       ],
42327       [
42328         "Kazakhstan (Казахстан)",
42329         "kz",
42330         "7",
42331         1
42332       ],
42333       [
42334         "Kenya",
42335         "ke",
42336         "254"
42337       ],
42338       [
42339         "Kiribati",
42340         "ki",
42341         "686"
42342       ],
42343       [
42344         "Kosovo",
42345         "xk",
42346         "383"
42347       ],
42348       [
42349         "Kuwait (‫الكويت‬‎)",
42350         "kw",
42351         "965"
42352       ],
42353       [
42354         "Kyrgyzstan (Кыргызстан)",
42355         "kg",
42356         "996"
42357       ],
42358       [
42359         "Laos (ລາວ)",
42360         "la",
42361         "856"
42362       ],
42363       [
42364         "Latvia (Latvija)",
42365         "lv",
42366         "371"
42367       ],
42368       [
42369         "Lebanon (‫لبنان‬‎)",
42370         "lb",
42371         "961"
42372       ],
42373       [
42374         "Lesotho",
42375         "ls",
42376         "266"
42377       ],
42378       [
42379         "Liberia",
42380         "lr",
42381         "231"
42382       ],
42383       [
42384         "Libya (‫ليبيا‬‎)",
42385         "ly",
42386         "218"
42387       ],
42388       [
42389         "Liechtenstein",
42390         "li",
42391         "423"
42392       ],
42393       [
42394         "Lithuania (Lietuva)",
42395         "lt",
42396         "370"
42397       ],
42398       [
42399         "Luxembourg",
42400         "lu",
42401         "352"
42402       ],
42403       [
42404         "Macau (澳門)",
42405         "mo",
42406         "853"
42407       ],
42408       [
42409         "Macedonia (FYROM) (Македонија)",
42410         "mk",
42411         "389"
42412       ],
42413       [
42414         "Madagascar (Madagasikara)",
42415         "mg",
42416         "261"
42417       ],
42418       [
42419         "Malawi",
42420         "mw",
42421         "265"
42422       ],
42423       [
42424         "Malaysia",
42425         "my",
42426         "60"
42427       ],
42428       [
42429         "Maldives",
42430         "mv",
42431         "960"
42432       ],
42433       [
42434         "Mali",
42435         "ml",
42436         "223"
42437       ],
42438       [
42439         "Malta",
42440         "mt",
42441         "356"
42442       ],
42443       [
42444         "Marshall Islands",
42445         "mh",
42446         "692"
42447       ],
42448       [
42449         "Martinique",
42450         "mq",
42451         "596"
42452       ],
42453       [
42454         "Mauritania (‫موريتانيا‬‎)",
42455         "mr",
42456         "222"
42457       ],
42458       [
42459         "Mauritius (Moris)",
42460         "mu",
42461         "230"
42462       ],
42463       [
42464         "Mayotte",
42465         "yt",
42466         "262",
42467         1
42468       ],
42469       [
42470         "Mexico (México)",
42471         "mx",
42472         "52"
42473       ],
42474       [
42475         "Micronesia",
42476         "fm",
42477         "691"
42478       ],
42479       [
42480         "Moldova (Republica Moldova)",
42481         "md",
42482         "373"
42483       ],
42484       [
42485         "Monaco",
42486         "mc",
42487         "377"
42488       ],
42489       [
42490         "Mongolia (Монгол)",
42491         "mn",
42492         "976"
42493       ],
42494       [
42495         "Montenegro (Crna Gora)",
42496         "me",
42497         "382"
42498       ],
42499       [
42500         "Montserrat",
42501         "ms",
42502         "1664"
42503       ],
42504       [
42505         "Morocco (‫المغرب‬‎)",
42506         "ma",
42507         "212",
42508         0
42509       ],
42510       [
42511         "Mozambique (Moçambique)",
42512         "mz",
42513         "258"
42514       ],
42515       [
42516         "Myanmar (Burma) (မြန်မာ)",
42517         "mm",
42518         "95"
42519       ],
42520       [
42521         "Namibia (Namibië)",
42522         "na",
42523         "264"
42524       ],
42525       [
42526         "Nauru",
42527         "nr",
42528         "674"
42529       ],
42530       [
42531         "Nepal (नेपाल)",
42532         "np",
42533         "977"
42534       ],
42535       [
42536         "Netherlands (Nederland)",
42537         "nl",
42538         "31"
42539       ],
42540       [
42541         "New Caledonia (Nouvelle-Calédonie)",
42542         "nc",
42543         "687"
42544       ],
42545       [
42546         "New Zealand",
42547         "nz",
42548         "64"
42549       ],
42550       [
42551         "Nicaragua",
42552         "ni",
42553         "505"
42554       ],
42555       [
42556         "Niger (Nijar)",
42557         "ne",
42558         "227"
42559       ],
42560       [
42561         "Nigeria",
42562         "ng",
42563         "234"
42564       ],
42565       [
42566         "Niue",
42567         "nu",
42568         "683"
42569       ],
42570       [
42571         "Norfolk Island",
42572         "nf",
42573         "672"
42574       ],
42575       [
42576         "North Korea (조선 민주주의 인민 공화국)",
42577         "kp",
42578         "850"
42579       ],
42580       [
42581         "Northern Mariana Islands",
42582         "mp",
42583         "1670"
42584       ],
42585       [
42586         "Norway (Norge)",
42587         "no",
42588         "47",
42589         0
42590       ],
42591       [
42592         "Oman (‫عُمان‬‎)",
42593         "om",
42594         "968"
42595       ],
42596       [
42597         "Pakistan (‫پاکستان‬‎)",
42598         "pk",
42599         "92"
42600       ],
42601       [
42602         "Palau",
42603         "pw",
42604         "680"
42605       ],
42606       [
42607         "Palestine (‫فلسطين‬‎)",
42608         "ps",
42609         "970"
42610       ],
42611       [
42612         "Panama (Panamá)",
42613         "pa",
42614         "507"
42615       ],
42616       [
42617         "Papua New Guinea",
42618         "pg",
42619         "675"
42620       ],
42621       [
42622         "Paraguay",
42623         "py",
42624         "595"
42625       ],
42626       [
42627         "Peru (Perú)",
42628         "pe",
42629         "51"
42630       ],
42631       [
42632         "Philippines",
42633         "ph",
42634         "63"
42635       ],
42636       [
42637         "Poland (Polska)",
42638         "pl",
42639         "48"
42640       ],
42641       [
42642         "Portugal",
42643         "pt",
42644         "351"
42645       ],
42646       [
42647         "Puerto Rico",
42648         "pr",
42649         "1",
42650         3,
42651         ["787", "939"]
42652       ],
42653       [
42654         "Qatar (‫قطر‬‎)",
42655         "qa",
42656         "974"
42657       ],
42658       [
42659         "Réunion (La Réunion)",
42660         "re",
42661         "262",
42662         0
42663       ],
42664       [
42665         "Romania (România)",
42666         "ro",
42667         "40"
42668       ],
42669       [
42670         "Russia (Россия)",
42671         "ru",
42672         "7",
42673         0
42674       ],
42675       [
42676         "Rwanda",
42677         "rw",
42678         "250"
42679       ],
42680       [
42681         "Saint Barthélemy",
42682         "bl",
42683         "590",
42684         1
42685       ],
42686       [
42687         "Saint Helena",
42688         "sh",
42689         "290"
42690       ],
42691       [
42692         "Saint Kitts and Nevis",
42693         "kn",
42694         "1869"
42695       ],
42696       [
42697         "Saint Lucia",
42698         "lc",
42699         "1758"
42700       ],
42701       [
42702         "Saint Martin (Saint-Martin (partie française))",
42703         "mf",
42704         "590",
42705         2
42706       ],
42707       [
42708         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42709         "pm",
42710         "508"
42711       ],
42712       [
42713         "Saint Vincent and the Grenadines",
42714         "vc",
42715         "1784"
42716       ],
42717       [
42718         "Samoa",
42719         "ws",
42720         "685"
42721       ],
42722       [
42723         "San Marino",
42724         "sm",
42725         "378"
42726       ],
42727       [
42728         "São Tomé and Príncipe (São Tomé e Príncipe)",
42729         "st",
42730         "239"
42731       ],
42732       [
42733         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42734         "sa",
42735         "966"
42736       ],
42737       [
42738         "Senegal (Sénégal)",
42739         "sn",
42740         "221"
42741       ],
42742       [
42743         "Serbia (Србија)",
42744         "rs",
42745         "381"
42746       ],
42747       [
42748         "Seychelles",
42749         "sc",
42750         "248"
42751       ],
42752       [
42753         "Sierra Leone",
42754         "sl",
42755         "232"
42756       ],
42757       [
42758         "Singapore",
42759         "sg",
42760         "65"
42761       ],
42762       [
42763         "Sint Maarten",
42764         "sx",
42765         "1721"
42766       ],
42767       [
42768         "Slovakia (Slovensko)",
42769         "sk",
42770         "421"
42771       ],
42772       [
42773         "Slovenia (Slovenija)",
42774         "si",
42775         "386"
42776       ],
42777       [
42778         "Solomon Islands",
42779         "sb",
42780         "677"
42781       ],
42782       [
42783         "Somalia (Soomaaliya)",
42784         "so",
42785         "252"
42786       ],
42787       [
42788         "South Africa",
42789         "za",
42790         "27"
42791       ],
42792       [
42793         "South Korea (대한민국)",
42794         "kr",
42795         "82"
42796       ],
42797       [
42798         "South Sudan (‫جنوب السودان‬‎)",
42799         "ss",
42800         "211"
42801       ],
42802       [
42803         "Spain (España)",
42804         "es",
42805         "34"
42806       ],
42807       [
42808         "Sri Lanka (ශ්‍රී ලංකාව)",
42809         "lk",
42810         "94"
42811       ],
42812       [
42813         "Sudan (‫السودان‬‎)",
42814         "sd",
42815         "249"
42816       ],
42817       [
42818         "Suriname",
42819         "sr",
42820         "597"
42821       ],
42822       [
42823         "Svalbard and Jan Mayen",
42824         "sj",
42825         "47",
42826         1
42827       ],
42828       [
42829         "Swaziland",
42830         "sz",
42831         "268"
42832       ],
42833       [
42834         "Sweden (Sverige)",
42835         "se",
42836         "46"
42837       ],
42838       [
42839         "Switzerland (Schweiz)",
42840         "ch",
42841         "41"
42842       ],
42843       [
42844         "Syria (‫سوريا‬‎)",
42845         "sy",
42846         "963"
42847       ],
42848       [
42849         "Taiwan (台灣)",
42850         "tw",
42851         "886"
42852       ],
42853       [
42854         "Tajikistan",
42855         "tj",
42856         "992"
42857       ],
42858       [
42859         "Tanzania",
42860         "tz",
42861         "255"
42862       ],
42863       [
42864         "Thailand (ไทย)",
42865         "th",
42866         "66"
42867       ],
42868       [
42869         "Timor-Leste",
42870         "tl",
42871         "670"
42872       ],
42873       [
42874         "Togo",
42875         "tg",
42876         "228"
42877       ],
42878       [
42879         "Tokelau",
42880         "tk",
42881         "690"
42882       ],
42883       [
42884         "Tonga",
42885         "to",
42886         "676"
42887       ],
42888       [
42889         "Trinidad and Tobago",
42890         "tt",
42891         "1868"
42892       ],
42893       [
42894         "Tunisia (‫تونس‬‎)",
42895         "tn",
42896         "216"
42897       ],
42898       [
42899         "Turkey (Türkiye)",
42900         "tr",
42901         "90"
42902       ],
42903       [
42904         "Turkmenistan",
42905         "tm",
42906         "993"
42907       ],
42908       [
42909         "Turks and Caicos Islands",
42910         "tc",
42911         "1649"
42912       ],
42913       [
42914         "Tuvalu",
42915         "tv",
42916         "688"
42917       ],
42918       [
42919         "U.S. Virgin Islands",
42920         "vi",
42921         "1340"
42922       ],
42923       [
42924         "Uganda",
42925         "ug",
42926         "256"
42927       ],
42928       [
42929         "Ukraine (Україна)",
42930         "ua",
42931         "380"
42932       ],
42933       [
42934         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42935         "ae",
42936         "971"
42937       ],
42938       [
42939         "United Kingdom",
42940         "gb",
42941         "44",
42942         0
42943       ],
42944       [
42945         "United States",
42946         "us",
42947         "1",
42948         0
42949       ],
42950       [
42951         "Uruguay",
42952         "uy",
42953         "598"
42954       ],
42955       [
42956         "Uzbekistan (Oʻzbekiston)",
42957         "uz",
42958         "998"
42959       ],
42960       [
42961         "Vanuatu",
42962         "vu",
42963         "678"
42964       ],
42965       [
42966         "Vatican City (Città del Vaticano)",
42967         "va",
42968         "39",
42969         1
42970       ],
42971       [
42972         "Venezuela",
42973         "ve",
42974         "58"
42975       ],
42976       [
42977         "Vietnam (Việt Nam)",
42978         "vn",
42979         "84"
42980       ],
42981       [
42982         "Wallis and Futuna (Wallis-et-Futuna)",
42983         "wf",
42984         "681"
42985       ],
42986       [
42987         "Western Sahara (‫الصحراء الغربية‬‎)",
42988         "eh",
42989         "212",
42990         1
42991       ],
42992       [
42993         "Yemen (‫اليمن‬‎)",
42994         "ye",
42995         "967"
42996       ],
42997       [
42998         "Zambia",
42999         "zm",
43000         "260"
43001       ],
43002       [
43003         "Zimbabwe",
43004         "zw",
43005         "263"
43006       ],
43007       [
43008         "Åland Islands",
43009         "ax",
43010         "358",
43011         1
43012       ]
43013   ];
43014   
43015   return d;
43016 }/**
43017 *    This script refer to:
43018 *    Title: International Telephone Input
43019 *    Author: Jack O'Connor
43020 *    Code version:  v12.1.12
43021 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43022 **/
43023
43024 /**
43025  * @class Roo.bootstrap.PhoneInput
43026  * @extends Roo.bootstrap.TriggerField
43027  * An input with International dial-code selection
43028  
43029  * @cfg {String} defaultDialCode default '+852'
43030  * @cfg {Array} preferedCountries default []
43031   
43032  * @constructor
43033  * Create a new PhoneInput.
43034  * @param {Object} config Configuration options
43035  */
43036
43037 Roo.bootstrap.PhoneInput = function(config) {
43038     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43039 };
43040
43041 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43042         
43043         listWidth: undefined,
43044         
43045         selectedClass: 'active',
43046         
43047         invalidClass : "has-warning",
43048         
43049         validClass: 'has-success',
43050         
43051         allowed: '0123456789',
43052         
43053         max_length: 15,
43054         
43055         /**
43056          * @cfg {String} defaultDialCode The default dial code when initializing the input
43057          */
43058         defaultDialCode: '+852',
43059         
43060         /**
43061          * @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
43062          */
43063         preferedCountries: false,
43064         
43065         getAutoCreate : function()
43066         {
43067             var data = Roo.bootstrap.PhoneInputData();
43068             var align = this.labelAlign || this.parentLabelAlign();
43069             var id = Roo.id();
43070             
43071             this.allCountries = [];
43072             this.dialCodeMapping = [];
43073             
43074             for (var i = 0; i < data.length; i++) {
43075               var c = data[i];
43076               this.allCountries[i] = {
43077                 name: c[0],
43078                 iso2: c[1],
43079                 dialCode: c[2],
43080                 priority: c[3] || 0,
43081                 areaCodes: c[4] || null
43082               };
43083               this.dialCodeMapping[c[2]] = {
43084                   name: c[0],
43085                   iso2: c[1],
43086                   priority: c[3] || 0,
43087                   areaCodes: c[4] || null
43088               };
43089             }
43090             
43091             var cfg = {
43092                 cls: 'form-group',
43093                 cn: []
43094             };
43095             
43096             var input =  {
43097                 tag: 'input',
43098                 id : id,
43099                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43100                 maxlength: this.max_length,
43101                 cls : 'form-control tel-input',
43102                 autocomplete: 'new-password'
43103             };
43104             
43105             var hiddenInput = {
43106                 tag: 'input',
43107                 type: 'hidden',
43108                 cls: 'hidden-tel-input'
43109             };
43110             
43111             if (this.name) {
43112                 hiddenInput.name = this.name;
43113             }
43114             
43115             if (this.disabled) {
43116                 input.disabled = true;
43117             }
43118             
43119             var flag_container = {
43120                 tag: 'div',
43121                 cls: 'flag-box',
43122                 cn: [
43123                     {
43124                         tag: 'div',
43125                         cls: 'flag'
43126                     },
43127                     {
43128                         tag: 'div',
43129                         cls: 'caret'
43130                     }
43131                 ]
43132             };
43133             
43134             var box = {
43135                 tag: 'div',
43136                 cls: this.hasFeedback ? 'has-feedback' : '',
43137                 cn: [
43138                     hiddenInput,
43139                     input,
43140                     {
43141                         tag: 'input',
43142                         cls: 'dial-code-holder',
43143                         disabled: true
43144                     }
43145                 ]
43146             };
43147             
43148             var container = {
43149                 cls: 'roo-select2-container input-group',
43150                 cn: [
43151                     flag_container,
43152                     box
43153                 ]
43154             };
43155             
43156             if (this.fieldLabel.length) {
43157                 var indicator = {
43158                     tag: 'i',
43159                     tooltip: 'This field is required'
43160                 };
43161                 
43162                 var label = {
43163                     tag: 'label',
43164                     'for':  id,
43165                     cls: 'control-label',
43166                     cn: []
43167                 };
43168                 
43169                 var label_text = {
43170                     tag: 'span',
43171                     html: this.fieldLabel
43172                 };
43173                 
43174                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43175                 label.cn = [
43176                     indicator,
43177                     label_text
43178                 ];
43179                 
43180                 if(this.indicatorpos == 'right') {
43181                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43182                     label.cn = [
43183                         label_text,
43184                         indicator
43185                     ];
43186                 }
43187                 
43188                 if(align == 'left') {
43189                     container = {
43190                         tag: 'div',
43191                         cn: [
43192                             container
43193                         ]
43194                     };
43195                     
43196                     if(this.labelWidth > 12){
43197                         label.style = "width: " + this.labelWidth + 'px';
43198                     }
43199                     if(this.labelWidth < 13 && this.labelmd == 0){
43200                         this.labelmd = this.labelWidth;
43201                     }
43202                     if(this.labellg > 0){
43203                         label.cls += ' col-lg-' + this.labellg;
43204                         input.cls += ' col-lg-' + (12 - this.labellg);
43205                     }
43206                     if(this.labelmd > 0){
43207                         label.cls += ' col-md-' + this.labelmd;
43208                         container.cls += ' col-md-' + (12 - this.labelmd);
43209                     }
43210                     if(this.labelsm > 0){
43211                         label.cls += ' col-sm-' + this.labelsm;
43212                         container.cls += ' col-sm-' + (12 - this.labelsm);
43213                     }
43214                     if(this.labelxs > 0){
43215                         label.cls += ' col-xs-' + this.labelxs;
43216                         container.cls += ' col-xs-' + (12 - this.labelxs);
43217                     }
43218                 }
43219             }
43220             
43221             cfg.cn = [
43222                 label,
43223                 container
43224             ];
43225             
43226             var settings = this;
43227             
43228             ['xs','sm','md','lg'].map(function(size){
43229                 if (settings[size]) {
43230                     cfg.cls += ' col-' + size + '-' + settings[size];
43231                 }
43232             });
43233             
43234             this.store = new Roo.data.Store({
43235                 proxy : new Roo.data.MemoryProxy({}),
43236                 reader : new Roo.data.JsonReader({
43237                     fields : [
43238                         {
43239                             'name' : 'name',
43240                             'type' : 'string'
43241                         },
43242                         {
43243                             'name' : 'iso2',
43244                             'type' : 'string'
43245                         },
43246                         {
43247                             'name' : 'dialCode',
43248                             'type' : 'string'
43249                         },
43250                         {
43251                             'name' : 'priority',
43252                             'type' : 'string'
43253                         },
43254                         {
43255                             'name' : 'areaCodes',
43256                             'type' : 'string'
43257                         }
43258                     ]
43259                 })
43260             });
43261             
43262             if(!this.preferedCountries) {
43263                 this.preferedCountries = [
43264                     'hk',
43265                     'gb',
43266                     'us'
43267                 ];
43268             }
43269             
43270             var p = this.preferedCountries.reverse();
43271             
43272             if(p) {
43273                 for (var i = 0; i < p.length; i++) {
43274                     for (var j = 0; j < this.allCountries.length; j++) {
43275                         if(this.allCountries[j].iso2 == p[i]) {
43276                             var t = this.allCountries[j];
43277                             this.allCountries.splice(j,1);
43278                             this.allCountries.unshift(t);
43279                         }
43280                     } 
43281                 }
43282             }
43283             
43284             this.store.proxy.data = {
43285                 success: true,
43286                 data: this.allCountries
43287             };
43288             
43289             return cfg;
43290         },
43291         
43292         initEvents : function()
43293         {
43294             this.createList();
43295             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43296             
43297             this.indicator = this.indicatorEl();
43298             this.flag = this.flagEl();
43299             this.dialCodeHolder = this.dialCodeHolderEl();
43300             
43301             this.trigger = this.el.select('div.flag-box',true).first();
43302             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43303             
43304             var _this = this;
43305             
43306             (function(){
43307                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43308                 _this.list.setWidth(lw);
43309             }).defer(100);
43310             
43311             this.list.on('mouseover', this.onViewOver, this);
43312             this.list.on('mousemove', this.onViewMove, this);
43313             this.inputEl().on("keyup", this.onKeyUp, this);
43314             this.inputEl().on("keypress", this.onKeyPress, this);
43315             
43316             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43317
43318             this.view = new Roo.View(this.list, this.tpl, {
43319                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43320             });
43321             
43322             this.view.on('click', this.onViewClick, this);
43323             this.setValue(this.defaultDialCode);
43324         },
43325         
43326         onTriggerClick : function(e)
43327         {
43328             Roo.log('trigger click');
43329             if(this.disabled){
43330                 return;
43331             }
43332             
43333             if(this.isExpanded()){
43334                 this.collapse();
43335                 this.hasFocus = false;
43336             }else {
43337                 this.store.load({});
43338                 this.hasFocus = true;
43339                 this.expand();
43340             }
43341         },
43342         
43343         isExpanded : function()
43344         {
43345             return this.list.isVisible();
43346         },
43347         
43348         collapse : function()
43349         {
43350             if(!this.isExpanded()){
43351                 return;
43352             }
43353             this.list.hide();
43354             Roo.get(document).un('mousedown', this.collapseIf, this);
43355             Roo.get(document).un('mousewheel', this.collapseIf, this);
43356             this.fireEvent('collapse', this);
43357             this.validate();
43358         },
43359         
43360         expand : function()
43361         {
43362             Roo.log('expand');
43363
43364             if(this.isExpanded() || !this.hasFocus){
43365                 return;
43366             }
43367             
43368             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43369             this.list.setWidth(lw);
43370             
43371             this.list.show();
43372             this.restrictHeight();
43373             
43374             Roo.get(document).on('mousedown', this.collapseIf, this);
43375             Roo.get(document).on('mousewheel', this.collapseIf, this);
43376             
43377             this.fireEvent('expand', this);
43378         },
43379         
43380         restrictHeight : function()
43381         {
43382             this.list.alignTo(this.inputEl(), this.listAlign);
43383             this.list.alignTo(this.inputEl(), this.listAlign);
43384         },
43385         
43386         onViewOver : function(e, t)
43387         {
43388             if(this.inKeyMode){
43389                 return;
43390             }
43391             var item = this.view.findItemFromChild(t);
43392             
43393             if(item){
43394                 var index = this.view.indexOf(item);
43395                 this.select(index, false);
43396             }
43397         },
43398
43399         // private
43400         onViewClick : function(view, doFocus, el, e)
43401         {
43402             var index = this.view.getSelectedIndexes()[0];
43403             
43404             var r = this.store.getAt(index);
43405             
43406             if(r){
43407                 this.onSelect(r, index);
43408             }
43409             if(doFocus !== false && !this.blockFocus){
43410                 this.inputEl().focus();
43411             }
43412         },
43413         
43414         onViewMove : function(e, t)
43415         {
43416             this.inKeyMode = false;
43417         },
43418         
43419         select : function(index, scrollIntoView)
43420         {
43421             this.selectedIndex = index;
43422             this.view.select(index);
43423             if(scrollIntoView !== false){
43424                 var el = this.view.getNode(index);
43425                 if(el){
43426                     this.list.scrollChildIntoView(el, false);
43427                 }
43428             }
43429         },
43430         
43431         createList : function()
43432         {
43433             this.list = Roo.get(document.body).createChild({
43434                 tag: 'ul',
43435                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43436                 style: 'display:none'
43437             });
43438             
43439             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43440         },
43441         
43442         collapseIf : function(e)
43443         {
43444             var in_combo  = e.within(this.el);
43445             var in_list =  e.within(this.list);
43446             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43447             
43448             if (in_combo || in_list || is_list) {
43449                 return;
43450             }
43451             this.collapse();
43452         },
43453         
43454         onSelect : function(record, index)
43455         {
43456             if(this.fireEvent('beforeselect', this, record, index) !== false){
43457                 
43458                 this.setFlagClass(record.data.iso2);
43459                 this.setDialCode(record.data.dialCode);
43460                 this.hasFocus = false;
43461                 this.collapse();
43462                 this.fireEvent('select', this, record, index);
43463             }
43464         },
43465         
43466         flagEl : function()
43467         {
43468             var flag = this.el.select('div.flag',true).first();
43469             if(!flag){
43470                 return false;
43471             }
43472             return flag;
43473         },
43474         
43475         dialCodeHolderEl : function()
43476         {
43477             var d = this.el.select('input.dial-code-holder',true).first();
43478             if(!d){
43479                 return false;
43480             }
43481             return d;
43482         },
43483         
43484         setDialCode : function(v)
43485         {
43486             this.dialCodeHolder.dom.value = '+'+v;
43487         },
43488         
43489         setFlagClass : function(n)
43490         {
43491             this.flag.dom.className = 'flag '+n;
43492         },
43493         
43494         getValue : function()
43495         {
43496             var v = this.inputEl().getValue();
43497             if(this.dialCodeHolder) {
43498                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43499             }
43500             return v;
43501         },
43502         
43503         setValue : function(v)
43504         {
43505             var d = this.getDialCode(v);
43506             
43507             //invalid dial code
43508             if(v.length == 0 || !d || d.length == 0) {
43509                 if(this.rendered){
43510                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43511                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43512                 }
43513                 return;
43514             }
43515             
43516             //valid dial code
43517             this.setFlagClass(this.dialCodeMapping[d].iso2);
43518             this.setDialCode(d);
43519             this.inputEl().dom.value = v.replace('+'+d,'');
43520             this.hiddenEl().dom.value = this.getValue();
43521             
43522             this.validate();
43523         },
43524         
43525         getDialCode : function(v)
43526         {
43527             v = v ||  '';
43528             
43529             if (v.length == 0) {
43530                 return this.dialCodeHolder.dom.value;
43531             }
43532             
43533             var dialCode = "";
43534             if (v.charAt(0) != "+") {
43535                 return false;
43536             }
43537             var numericChars = "";
43538             for (var i = 1; i < v.length; i++) {
43539               var c = v.charAt(i);
43540               if (!isNaN(c)) {
43541                 numericChars += c;
43542                 if (this.dialCodeMapping[numericChars]) {
43543                   dialCode = v.substr(1, i);
43544                 }
43545                 if (numericChars.length == 4) {
43546                   break;
43547                 }
43548               }
43549             }
43550             return dialCode;
43551         },
43552         
43553         reset : function()
43554         {
43555             this.setValue(this.defaultDialCode);
43556             this.validate();
43557         },
43558         
43559         hiddenEl : function()
43560         {
43561             return this.el.select('input.hidden-tel-input',true).first();
43562         },
43563         
43564         // after setting val
43565         onKeyUp : function(e){
43566             this.setValue(this.getValue());
43567         },
43568         
43569         onKeyPress : function(e){
43570             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43571                 e.stopEvent();
43572             }
43573         }
43574         
43575 });
43576 /**
43577  * @class Roo.bootstrap.MoneyField
43578  * @extends Roo.bootstrap.ComboBox
43579  * Bootstrap MoneyField class
43580  * 
43581  * @constructor
43582  * Create a new MoneyField.
43583  * @param {Object} config Configuration options
43584  */
43585
43586 Roo.bootstrap.MoneyField = function(config) {
43587     
43588     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43589     
43590 };
43591
43592 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43593     
43594     /**
43595      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43596      */
43597     allowDecimals : true,
43598     /**
43599      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43600      */
43601     decimalSeparator : ".",
43602     /**
43603      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43604      */
43605     decimalPrecision : 0,
43606     /**
43607      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43608      */
43609     allowNegative : true,
43610     /**
43611      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43612      */
43613     allowZero: true,
43614     /**
43615      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43616      */
43617     minValue : Number.NEGATIVE_INFINITY,
43618     /**
43619      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43620      */
43621     maxValue : Number.MAX_VALUE,
43622     /**
43623      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43624      */
43625     minText : "The minimum value for this field is {0}",
43626     /**
43627      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43628      */
43629     maxText : "The maximum value for this field is {0}",
43630     /**
43631      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43632      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43633      */
43634     nanText : "{0} is not a valid number",
43635     /**
43636      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43637      */
43638     castInt : true,
43639     /**
43640      * @cfg {String} defaults currency of the MoneyField
43641      * value should be in lkey
43642      */
43643     defaultCurrency : false,
43644     /**
43645      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43646      */
43647     thousandsDelimiter : false,
43648     /**
43649      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43650      */
43651     max_length: false,
43652     
43653     inputlg : 9,
43654     inputmd : 9,
43655     inputsm : 9,
43656     inputxs : 6,
43657     
43658     store : false,
43659     
43660     getAutoCreate : function()
43661     {
43662         var align = this.labelAlign || this.parentLabelAlign();
43663         
43664         var id = Roo.id();
43665
43666         var cfg = {
43667             cls: 'form-group',
43668             cn: []
43669         };
43670
43671         var input =  {
43672             tag: 'input',
43673             id : id,
43674             cls : 'form-control roo-money-amount-input',
43675             autocomplete: 'new-password'
43676         };
43677         
43678         var hiddenInput = {
43679             tag: 'input',
43680             type: 'hidden',
43681             id: Roo.id(),
43682             cls: 'hidden-number-input'
43683         };
43684         
43685         if(this.max_length) {
43686             input.maxlength = this.max_length; 
43687         }
43688         
43689         if (this.name) {
43690             hiddenInput.name = this.name;
43691         }
43692
43693         if (this.disabled) {
43694             input.disabled = true;
43695         }
43696
43697         var clg = 12 - this.inputlg;
43698         var cmd = 12 - this.inputmd;
43699         var csm = 12 - this.inputsm;
43700         var cxs = 12 - this.inputxs;
43701         
43702         var container = {
43703             tag : 'div',
43704             cls : 'row roo-money-field',
43705             cn : [
43706                 {
43707                     tag : 'div',
43708                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43709                     cn : [
43710                         {
43711                             tag : 'div',
43712                             cls: 'roo-select2-container input-group',
43713                             cn: [
43714                                 {
43715                                     tag : 'input',
43716                                     cls : 'form-control roo-money-currency-input',
43717                                     autocomplete: 'new-password',
43718                                     readOnly : 1,
43719                                     name : this.currencyName
43720                                 },
43721                                 {
43722                                     tag :'span',
43723                                     cls : 'input-group-addon',
43724                                     cn : [
43725                                         {
43726                                             tag: 'span',
43727                                             cls: 'caret'
43728                                         }
43729                                     ]
43730                                 }
43731                             ]
43732                         }
43733                     ]
43734                 },
43735                 {
43736                     tag : 'div',
43737                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43738                     cn : [
43739                         {
43740                             tag: 'div',
43741                             cls: this.hasFeedback ? 'has-feedback' : '',
43742                             cn: [
43743                                 input
43744                             ]
43745                         }
43746                     ]
43747                 }
43748             ]
43749             
43750         };
43751         
43752         if (this.fieldLabel.length) {
43753             var indicator = {
43754                 tag: 'i',
43755                 tooltip: 'This field is required'
43756             };
43757
43758             var label = {
43759                 tag: 'label',
43760                 'for':  id,
43761                 cls: 'control-label',
43762                 cn: []
43763             };
43764
43765             var label_text = {
43766                 tag: 'span',
43767                 html: this.fieldLabel
43768             };
43769
43770             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43771             label.cn = [
43772                 indicator,
43773                 label_text
43774             ];
43775
43776             if(this.indicatorpos == 'right') {
43777                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43778                 label.cn = [
43779                     label_text,
43780                     indicator
43781                 ];
43782             }
43783
43784             if(align == 'left') {
43785                 container = {
43786                     tag: 'div',
43787                     cn: [
43788                         container
43789                     ]
43790                 };
43791
43792                 if(this.labelWidth > 12){
43793                     label.style = "width: " + this.labelWidth + 'px';
43794                 }
43795                 if(this.labelWidth < 13 && this.labelmd == 0){
43796                     this.labelmd = this.labelWidth;
43797                 }
43798                 if(this.labellg > 0){
43799                     label.cls += ' col-lg-' + this.labellg;
43800                     input.cls += ' col-lg-' + (12 - this.labellg);
43801                 }
43802                 if(this.labelmd > 0){
43803                     label.cls += ' col-md-' + this.labelmd;
43804                     container.cls += ' col-md-' + (12 - this.labelmd);
43805                 }
43806                 if(this.labelsm > 0){
43807                     label.cls += ' col-sm-' + this.labelsm;
43808                     container.cls += ' col-sm-' + (12 - this.labelsm);
43809                 }
43810                 if(this.labelxs > 0){
43811                     label.cls += ' col-xs-' + this.labelxs;
43812                     container.cls += ' col-xs-' + (12 - this.labelxs);
43813                 }
43814             }
43815         }
43816
43817         cfg.cn = [
43818             label,
43819             container,
43820             hiddenInput
43821         ];
43822         
43823         var settings = this;
43824
43825         ['xs','sm','md','lg'].map(function(size){
43826             if (settings[size]) {
43827                 cfg.cls += ' col-' + size + '-' + settings[size];
43828             }
43829         });
43830         
43831         return cfg;
43832     },
43833     
43834     initEvents : function()
43835     {
43836         this.indicator = this.indicatorEl();
43837         
43838         this.initCurrencyEvent();
43839         
43840         this.initNumberEvent();
43841     },
43842     
43843     initCurrencyEvent : function()
43844     {
43845         if (!this.store) {
43846             throw "can not find store for combo";
43847         }
43848         
43849         this.store = Roo.factory(this.store, Roo.data);
43850         this.store.parent = this;
43851         
43852         this.createList();
43853         
43854         this.triggerEl = this.el.select('.input-group-addon', true).first();
43855         
43856         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43857         
43858         var _this = this;
43859         
43860         (function(){
43861             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43862             _this.list.setWidth(lw);
43863         }).defer(100);
43864         
43865         this.list.on('mouseover', this.onViewOver, this);
43866         this.list.on('mousemove', this.onViewMove, this);
43867         this.list.on('scroll', this.onViewScroll, this);
43868         
43869         if(!this.tpl){
43870             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43871         }
43872         
43873         this.view = new Roo.View(this.list, this.tpl, {
43874             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43875         });
43876         
43877         this.view.on('click', this.onViewClick, this);
43878         
43879         this.store.on('beforeload', this.onBeforeLoad, this);
43880         this.store.on('load', this.onLoad, this);
43881         this.store.on('loadexception', this.onLoadException, this);
43882         
43883         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43884             "up" : function(e){
43885                 this.inKeyMode = true;
43886                 this.selectPrev();
43887             },
43888
43889             "down" : function(e){
43890                 if(!this.isExpanded()){
43891                     this.onTriggerClick();
43892                 }else{
43893                     this.inKeyMode = true;
43894                     this.selectNext();
43895                 }
43896             },
43897
43898             "enter" : function(e){
43899                 this.collapse();
43900                 
43901                 if(this.fireEvent("specialkey", this, e)){
43902                     this.onViewClick(false);
43903                 }
43904                 
43905                 return true;
43906             },
43907
43908             "esc" : function(e){
43909                 this.collapse();
43910             },
43911
43912             "tab" : function(e){
43913                 this.collapse();
43914                 
43915                 if(this.fireEvent("specialkey", this, e)){
43916                     this.onViewClick(false);
43917                 }
43918                 
43919                 return true;
43920             },
43921
43922             scope : this,
43923
43924             doRelay : function(foo, bar, hname){
43925                 if(hname == 'down' || this.scope.isExpanded()){
43926                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43927                 }
43928                 return true;
43929             },
43930
43931             forceKeyDown: true
43932         });
43933         
43934         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43935         
43936     },
43937     
43938     initNumberEvent : function(e)
43939     {
43940         this.inputEl().on("keydown" , this.fireKey,  this);
43941         this.inputEl().on("focus", this.onFocus,  this);
43942         this.inputEl().on("blur", this.onBlur,  this);
43943         
43944         this.inputEl().relayEvent('keyup', this);
43945         
43946         if(this.indicator){
43947             this.indicator.addClass('invisible');
43948         }
43949  
43950         this.originalValue = this.getValue();
43951         
43952         if(this.validationEvent == 'keyup'){
43953             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43954             this.inputEl().on('keyup', this.filterValidation, this);
43955         }
43956         else if(this.validationEvent !== false){
43957             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43958         }
43959         
43960         if(this.selectOnFocus){
43961             this.on("focus", this.preFocus, this);
43962             
43963         }
43964         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43965             this.inputEl().on("keypress", this.filterKeys, this);
43966         } else {
43967             this.inputEl().relayEvent('keypress', this);
43968         }
43969         
43970         var allowed = "0123456789";
43971         
43972         if(this.allowDecimals){
43973             allowed += this.decimalSeparator;
43974         }
43975         
43976         if(this.allowNegative){
43977             allowed += "-";
43978         }
43979         
43980         if(this.thousandsDelimiter) {
43981             allowed += ",";
43982         }
43983         
43984         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43985         
43986         var keyPress = function(e){
43987             
43988             var k = e.getKey();
43989             
43990             var c = e.getCharCode();
43991             
43992             if(
43993                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43994                     allowed.indexOf(String.fromCharCode(c)) === -1
43995             ){
43996                 e.stopEvent();
43997                 return;
43998             }
43999             
44000             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44001                 return;
44002             }
44003             
44004             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44005                 e.stopEvent();
44006             }
44007         };
44008         
44009         this.inputEl().on("keypress", keyPress, this);
44010         
44011     },
44012     
44013     onTriggerClick : function(e)
44014     {   
44015         if(this.disabled){
44016             return;
44017         }
44018         
44019         this.page = 0;
44020         this.loadNext = false;
44021         
44022         if(this.isExpanded()){
44023             this.collapse();
44024             return;
44025         }
44026         
44027         this.hasFocus = true;
44028         
44029         if(this.triggerAction == 'all') {
44030             this.doQuery(this.allQuery, true);
44031             return;
44032         }
44033         
44034         this.doQuery(this.getRawValue());
44035     },
44036     
44037     getCurrency : function()
44038     {   
44039         var v = this.currencyEl().getValue();
44040         
44041         return v;
44042     },
44043     
44044     restrictHeight : function()
44045     {
44046         this.list.alignTo(this.currencyEl(), this.listAlign);
44047         this.list.alignTo(this.currencyEl(), this.listAlign);
44048     },
44049     
44050     onViewClick : function(view, doFocus, el, e)
44051     {
44052         var index = this.view.getSelectedIndexes()[0];
44053         
44054         var r = this.store.getAt(index);
44055         
44056         if(r){
44057             this.onSelect(r, index);
44058         }
44059     },
44060     
44061     onSelect : function(record, index){
44062         
44063         if(this.fireEvent('beforeselect', this, record, index) !== false){
44064         
44065             this.setFromCurrencyData(index > -1 ? record.data : false);
44066             
44067             this.collapse();
44068             
44069             this.fireEvent('select', this, record, index);
44070         }
44071     },
44072     
44073     setFromCurrencyData : function(o)
44074     {
44075         var currency = '';
44076         
44077         this.lastCurrency = o;
44078         
44079         if (this.currencyField) {
44080             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44081         } else {
44082             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44083         }
44084         
44085         this.lastSelectionText = currency;
44086         
44087         //setting default currency
44088         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44089             this.setCurrency(this.defaultCurrency);
44090             return;
44091         }
44092         
44093         this.setCurrency(currency);
44094     },
44095     
44096     setFromData : function(o)
44097     {
44098         var c = {};
44099         
44100         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44101         
44102         this.setFromCurrencyData(c);
44103         
44104         var value = '';
44105         
44106         if (this.name) {
44107             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44108         } else {
44109             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44110         }
44111         
44112         this.setValue(value);
44113         
44114     },
44115     
44116     setCurrency : function(v)
44117     {   
44118         this.currencyValue = v;
44119         
44120         if(this.rendered){
44121             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44122             this.validate();
44123         }
44124     },
44125     
44126     setValue : function(v)
44127     {
44128         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44129         
44130         this.value = v;
44131         
44132         if(this.rendered){
44133             
44134             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44135             
44136             this.inputEl().dom.value = (v == '') ? '' :
44137                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44138             
44139             if(!this.allowZero && v === '0') {
44140                 this.hiddenEl().dom.value = '';
44141                 this.inputEl().dom.value = '';
44142             }
44143             
44144             this.validate();
44145         }
44146     },
44147     
44148     getRawValue : function()
44149     {
44150         var v = this.inputEl().getValue();
44151         
44152         return v;
44153     },
44154     
44155     getValue : function()
44156     {
44157         return this.fixPrecision(this.parseValue(this.getRawValue()));
44158     },
44159     
44160     parseValue : function(value)
44161     {
44162         if(this.thousandsDelimiter) {
44163             value += "";
44164             r = new RegExp(",", "g");
44165             value = value.replace(r, "");
44166         }
44167         
44168         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44169         return isNaN(value) ? '' : value;
44170         
44171     },
44172     
44173     fixPrecision : function(value)
44174     {
44175         if(this.thousandsDelimiter) {
44176             value += "";
44177             r = new RegExp(",", "g");
44178             value = value.replace(r, "");
44179         }
44180         
44181         var nan = isNaN(value);
44182         
44183         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44184             return nan ? '' : value;
44185         }
44186         return parseFloat(value).toFixed(this.decimalPrecision);
44187     },
44188     
44189     decimalPrecisionFcn : function(v)
44190     {
44191         return Math.floor(v);
44192     },
44193     
44194     validateValue : function(value)
44195     {
44196         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44197             return false;
44198         }
44199         
44200         var num = this.parseValue(value);
44201         
44202         if(isNaN(num)){
44203             this.markInvalid(String.format(this.nanText, value));
44204             return false;
44205         }
44206         
44207         if(num < this.minValue){
44208             this.markInvalid(String.format(this.minText, this.minValue));
44209             return false;
44210         }
44211         
44212         if(num > this.maxValue){
44213             this.markInvalid(String.format(this.maxText, this.maxValue));
44214             return false;
44215         }
44216         
44217         return true;
44218     },
44219     
44220     validate : function()
44221     {
44222         if(this.disabled || this.allowBlank){
44223             this.markValid();
44224             return true;
44225         }
44226         
44227         var currency = this.getCurrency();
44228         
44229         if(this.validateValue(this.getRawValue()) && currency.length){
44230             this.markValid();
44231             return true;
44232         }
44233         
44234         this.markInvalid();
44235         return false;
44236     },
44237     
44238     getName: function()
44239     {
44240         return this.name;
44241     },
44242     
44243     beforeBlur : function()
44244     {
44245         if(!this.castInt){
44246             return;
44247         }
44248         
44249         var v = this.parseValue(this.getRawValue());
44250         
44251         if(v || v == 0){
44252             this.setValue(v);
44253         }
44254     },
44255     
44256     onBlur : function()
44257     {
44258         this.beforeBlur();
44259         
44260         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44261             //this.el.removeClass(this.focusClass);
44262         }
44263         
44264         this.hasFocus = false;
44265         
44266         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44267             this.validate();
44268         }
44269         
44270         var v = this.getValue();
44271         
44272         if(String(v) !== String(this.startValue)){
44273             this.fireEvent('change', this, v, this.startValue);
44274         }
44275         
44276         this.fireEvent("blur", this);
44277     },
44278     
44279     inputEl : function()
44280     {
44281         return this.el.select('.roo-money-amount-input', true).first();
44282     },
44283     
44284     currencyEl : function()
44285     {
44286         return this.el.select('.roo-money-currency-input', true).first();
44287     },
44288     
44289     hiddenEl : function()
44290     {
44291         return this.el.select('input.hidden-number-input',true).first();
44292     }
44293     
44294 });/**
44295  * @class Roo.bootstrap.BezierSignature
44296  * @extends Roo.bootstrap.Component
44297  * Bootstrap BezierSignature class
44298  * This script refer to:
44299  *    Title: Signature Pad
44300  *    Author: szimek
44301  *    Availability: https://github.com/szimek/signature_pad
44302  *
44303  * @constructor
44304  * Create a new BezierSignature
44305  * @param {Object} config The config object
44306  */
44307
44308 Roo.bootstrap.BezierSignature = function(config){
44309     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44310     this.addEvents({
44311         "resize" : true
44312     });
44313 };
44314
44315 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44316 {
44317      
44318     curve_data: [],
44319     
44320     is_empty: true,
44321     
44322     mouse_btn_down: true,
44323     
44324     /**
44325      * @cfg {int} canvas height
44326      */
44327     canvas_height: '200px',
44328     
44329     /**
44330      * @cfg {float|function} Radius of a single dot.
44331      */ 
44332     dot_size: false,
44333     
44334     /**
44335      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44336      */
44337     min_width: 0.5,
44338     
44339     /**
44340      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44341      */
44342     max_width: 2.5,
44343     
44344     /**
44345      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44346      */
44347     throttle: 16,
44348     
44349     /**
44350      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44351      */
44352     min_distance: 5,
44353     
44354     /**
44355      * @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.
44356      */
44357     bg_color: 'rgba(0, 0, 0, 0)',
44358     
44359     /**
44360      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44361      */
44362     dot_color: 'black',
44363     
44364     /**
44365      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44366      */ 
44367     velocity_filter_weight: 0.7,
44368     
44369     /**
44370      * @cfg {function} Callback when stroke begin. 
44371      */
44372     onBegin: false,
44373     
44374     /**
44375      * @cfg {function} Callback when stroke end.
44376      */
44377     onEnd: false,
44378     
44379     getAutoCreate : function()
44380     {
44381         var cls = 'roo-signature column';
44382         
44383         if(this.cls){
44384             cls += ' ' + this.cls;
44385         }
44386         
44387         var col_sizes = [
44388             'lg',
44389             'md',
44390             'sm',
44391             'xs'
44392         ];
44393         
44394         for(var i = 0; i < col_sizes.length; i++) {
44395             if(this[col_sizes[i]]) {
44396                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44397             }
44398         }
44399         
44400         var cfg = {
44401             tag: 'div',
44402             cls: cls,
44403             cn: [
44404                 {
44405                     tag: 'div',
44406                     cls: 'roo-signature-body',
44407                     cn: [
44408                         {
44409                             tag: 'canvas',
44410                             cls: 'roo-signature-body-canvas',
44411                             height: this.canvas_height,
44412                             width: this.canvas_width
44413                         }
44414                     ]
44415                 },
44416                 {
44417                     tag: 'input',
44418                     type: 'file',
44419                     style: 'display: none'
44420                 }
44421             ]
44422         };
44423         
44424         return cfg;
44425     },
44426     
44427     initEvents: function() 
44428     {
44429         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44430         
44431         var canvas = this.canvasEl();
44432         
44433         // mouse && touch event swapping...
44434         canvas.dom.style.touchAction = 'none';
44435         canvas.dom.style.msTouchAction = 'none';
44436         
44437         this.mouse_btn_down = false;
44438         canvas.on('mousedown', this._handleMouseDown, this);
44439         canvas.on('mousemove', this._handleMouseMove, this);
44440         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44441         
44442         if (window.PointerEvent) {
44443             canvas.on('pointerdown', this._handleMouseDown, this);
44444             canvas.on('pointermove', this._handleMouseMove, this);
44445             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44446         }
44447         
44448         if ('ontouchstart' in window) {
44449             canvas.on('touchstart', this._handleTouchStart, this);
44450             canvas.on('touchmove', this._handleTouchMove, this);
44451             canvas.on('touchend', this._handleTouchEnd, this);
44452         }
44453         
44454         Roo.EventManager.onWindowResize(this.resize, this, true);
44455         
44456         // file input event
44457         this.fileEl().on('change', this.uploadImage, this);
44458         
44459         this.clear();
44460         
44461         this.resize();
44462     },
44463     
44464     resize: function(){
44465         
44466         var canvas = this.canvasEl().dom;
44467         var ctx = this.canvasElCtx();
44468         var img_data = false;
44469         
44470         if(canvas.width > 0) {
44471             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44472         }
44473         // setting canvas width will clean img data
44474         canvas.width = 0;
44475         
44476         var style = window.getComputedStyle ? 
44477             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44478             
44479         var padding_left = parseInt(style.paddingLeft) || 0;
44480         var padding_right = parseInt(style.paddingRight) || 0;
44481         
44482         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44483         
44484         if(img_data) {
44485             ctx.putImageData(img_data, 0, 0);
44486         }
44487     },
44488     
44489     _handleMouseDown: function(e)
44490     {
44491         if (e.browserEvent.which === 1) {
44492             this.mouse_btn_down = true;
44493             this.strokeBegin(e);
44494         }
44495     },
44496     
44497     _handleMouseMove: function (e)
44498     {
44499         if (this.mouse_btn_down) {
44500             this.strokeMoveUpdate(e);
44501         }
44502     },
44503     
44504     _handleMouseUp: function (e)
44505     {
44506         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44507             this.mouse_btn_down = false;
44508             this.strokeEnd(e);
44509         }
44510     },
44511     
44512     _handleTouchStart: function (e) {
44513         
44514         e.preventDefault();
44515         if (e.browserEvent.targetTouches.length === 1) {
44516             // var touch = e.browserEvent.changedTouches[0];
44517             // this.strokeBegin(touch);
44518             
44519              this.strokeBegin(e); // assume e catching the correct xy...
44520         }
44521     },
44522     
44523     _handleTouchMove: function (e) {
44524         e.preventDefault();
44525         // var touch = event.targetTouches[0];
44526         // _this._strokeMoveUpdate(touch);
44527         this.strokeMoveUpdate(e);
44528     },
44529     
44530     _handleTouchEnd: function (e) {
44531         var wasCanvasTouched = e.target === this.canvasEl().dom;
44532         if (wasCanvasTouched) {
44533             e.preventDefault();
44534             // var touch = event.changedTouches[0];
44535             // _this._strokeEnd(touch);
44536             this.strokeEnd(e);
44537         }
44538     },
44539     
44540     reset: function () {
44541         this._lastPoints = [];
44542         this._lastVelocity = 0;
44543         this._lastWidth = (this.min_width + this.max_width) / 2;
44544         this.canvasElCtx().fillStyle = this.dot_color;
44545     },
44546     
44547     strokeMoveUpdate: function(e)
44548     {
44549         this.strokeUpdate(e);
44550         
44551         if (this.throttle) {
44552             this.throttleStroke(this.strokeUpdate, this.throttle);
44553         }
44554         else {
44555             this.strokeUpdate(e);
44556         }
44557     },
44558     
44559     strokeBegin: function(e)
44560     {
44561         var newPointGroup = {
44562             color: this.dot_color,
44563             points: []
44564         };
44565         
44566         if (typeof this.onBegin === 'function') {
44567             this.onBegin(e);
44568         }
44569         
44570         this.curve_data.push(newPointGroup);
44571         this.reset();
44572         this.strokeUpdate(e);
44573     },
44574     
44575     strokeUpdate: function(e)
44576     {
44577         var rect = this.canvasEl().dom.getBoundingClientRect();
44578         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44579         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44580         var lastPoints = lastPointGroup.points;
44581         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44582         var isLastPointTooClose = lastPoint
44583             ? point.distanceTo(lastPoint) <= this.min_distance
44584             : false;
44585         var color = lastPointGroup.color;
44586         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44587             var curve = this.addPoint(point);
44588             if (!lastPoint) {
44589                 this.drawDot({color: color, point: point});
44590             }
44591             else if (curve) {
44592                 this.drawCurve({color: color, curve: curve});
44593             }
44594             lastPoints.push({
44595                 time: point.time,
44596                 x: point.x,
44597                 y: point.y
44598             });
44599         }
44600     },
44601     
44602     strokeEnd: function(e)
44603     {
44604         this.strokeUpdate(e);
44605         if (typeof this.onEnd === 'function') {
44606             this.onEnd(e);
44607         }
44608     },
44609     
44610     addPoint:  function (point) {
44611         var _lastPoints = this._lastPoints;
44612         _lastPoints.push(point);
44613         if (_lastPoints.length > 2) {
44614             if (_lastPoints.length === 3) {
44615                 _lastPoints.unshift(_lastPoints[0]);
44616             }
44617             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44618             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44619             _lastPoints.shift();
44620             return curve;
44621         }
44622         return null;
44623     },
44624     
44625     calculateCurveWidths: function (startPoint, endPoint) {
44626         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44627             (1 - this.velocity_filter_weight) * this._lastVelocity;
44628
44629         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44630         var widths = {
44631             end: newWidth,
44632             start: this._lastWidth
44633         };
44634         
44635         this._lastVelocity = velocity;
44636         this._lastWidth = newWidth;
44637         return widths;
44638     },
44639     
44640     drawDot: function (_a) {
44641         var color = _a.color, point = _a.point;
44642         var ctx = this.canvasElCtx();
44643         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44644         ctx.beginPath();
44645         this.drawCurveSegment(point.x, point.y, width);
44646         ctx.closePath();
44647         ctx.fillStyle = color;
44648         ctx.fill();
44649     },
44650     
44651     drawCurve: function (_a) {
44652         var color = _a.color, curve = _a.curve;
44653         var ctx = this.canvasElCtx();
44654         var widthDelta = curve.endWidth - curve.startWidth;
44655         var drawSteps = Math.floor(curve.length()) * 2;
44656         ctx.beginPath();
44657         ctx.fillStyle = color;
44658         for (var i = 0; i < drawSteps; i += 1) {
44659         var t = i / drawSteps;
44660         var tt = t * t;
44661         var ttt = tt * t;
44662         var u = 1 - t;
44663         var uu = u * u;
44664         var uuu = uu * u;
44665         var x = uuu * curve.startPoint.x;
44666         x += 3 * uu * t * curve.control1.x;
44667         x += 3 * u * tt * curve.control2.x;
44668         x += ttt * curve.endPoint.x;
44669         var y = uuu * curve.startPoint.y;
44670         y += 3 * uu * t * curve.control1.y;
44671         y += 3 * u * tt * curve.control2.y;
44672         y += ttt * curve.endPoint.y;
44673         var width = curve.startWidth + ttt * widthDelta;
44674         this.drawCurveSegment(x, y, width);
44675         }
44676         ctx.closePath();
44677         ctx.fill();
44678     },
44679     
44680     drawCurveSegment: function (x, y, width) {
44681         var ctx = this.canvasElCtx();
44682         ctx.moveTo(x, y);
44683         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44684         this.is_empty = false;
44685     },
44686     
44687     clear: function()
44688     {
44689         var ctx = this.canvasElCtx();
44690         var canvas = this.canvasEl().dom;
44691         ctx.fillStyle = this.bg_color;
44692         ctx.clearRect(0, 0, canvas.width, canvas.height);
44693         ctx.fillRect(0, 0, canvas.width, canvas.height);
44694         this.curve_data = [];
44695         this.reset();
44696         this.is_empty = true;
44697     },
44698     
44699     fileEl: function()
44700     {
44701         return  this.el.select('input',true).first();
44702     },
44703     
44704     canvasEl: function()
44705     {
44706         return this.el.select('canvas',true).first();
44707     },
44708     
44709     canvasElCtx: function()
44710     {
44711         return this.el.select('canvas',true).first().dom.getContext('2d');
44712     },
44713     
44714     getImage: function(type)
44715     {
44716         if(this.is_empty) {
44717             return false;
44718         }
44719         
44720         // encryption ?
44721         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44722     },
44723     
44724     drawFromImage: function(img_src)
44725     {
44726         var img = new Image();
44727         
44728         img.onload = function(){
44729             this.canvasElCtx().drawImage(img, 0, 0);
44730         }.bind(this);
44731         
44732         img.src = img_src;
44733         
44734         this.is_empty = false;
44735     },
44736     
44737     selectImage: function()
44738     {
44739         this.fileEl().dom.click();
44740     },
44741     
44742     uploadImage: function(e)
44743     {
44744         var reader = new FileReader();
44745         
44746         reader.onload = function(e){
44747             var img = new Image();
44748             img.onload = function(){
44749                 this.reset();
44750                 this.canvasElCtx().drawImage(img, 0, 0);
44751             }.bind(this);
44752             img.src = e.target.result;
44753         }.bind(this);
44754         
44755         reader.readAsDataURL(e.target.files[0]);
44756     },
44757     
44758     // Bezier Point Constructor
44759     Point: (function () {
44760         function Point(x, y, time) {
44761             this.x = x;
44762             this.y = y;
44763             this.time = time || Date.now();
44764         }
44765         Point.prototype.distanceTo = function (start) {
44766             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44767         };
44768         Point.prototype.equals = function (other) {
44769             return this.x === other.x && this.y === other.y && this.time === other.time;
44770         };
44771         Point.prototype.velocityFrom = function (start) {
44772             return this.time !== start.time
44773             ? this.distanceTo(start) / (this.time - start.time)
44774             : 0;
44775         };
44776         return Point;
44777     }()),
44778     
44779     
44780     // Bezier Constructor
44781     Bezier: (function () {
44782         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44783             this.startPoint = startPoint;
44784             this.control2 = control2;
44785             this.control1 = control1;
44786             this.endPoint = endPoint;
44787             this.startWidth = startWidth;
44788             this.endWidth = endWidth;
44789         }
44790         Bezier.fromPoints = function (points, widths, scope) {
44791             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44792             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44793             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44794         };
44795         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44796             var dx1 = s1.x - s2.x;
44797             var dy1 = s1.y - s2.y;
44798             var dx2 = s2.x - s3.x;
44799             var dy2 = s2.y - s3.y;
44800             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44801             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44802             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44803             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44804             var dxm = m1.x - m2.x;
44805             var dym = m1.y - m2.y;
44806             var k = l2 / (l1 + l2);
44807             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44808             var tx = s2.x - cm.x;
44809             var ty = s2.y - cm.y;
44810             return {
44811                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44812                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44813             };
44814         };
44815         Bezier.prototype.length = function () {
44816             var steps = 10;
44817             var length = 0;
44818             var px;
44819             var py;
44820             for (var i = 0; i <= steps; i += 1) {
44821                 var t = i / steps;
44822                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44823                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44824                 if (i > 0) {
44825                     var xdiff = cx - px;
44826                     var ydiff = cy - py;
44827                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44828                 }
44829                 px = cx;
44830                 py = cy;
44831             }
44832             return length;
44833         };
44834         Bezier.prototype.point = function (t, start, c1, c2, end) {
44835             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44836             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44837             + (3.0 * c2 * (1.0 - t) * t * t)
44838             + (end * t * t * t);
44839         };
44840         return Bezier;
44841     }()),
44842     
44843     throttleStroke: function(fn, wait) {
44844       if (wait === void 0) { wait = 250; }
44845       var previous = 0;
44846       var timeout = null;
44847       var result;
44848       var storedContext;
44849       var storedArgs;
44850       var later = function () {
44851           previous = Date.now();
44852           timeout = null;
44853           result = fn.apply(storedContext, storedArgs);
44854           if (!timeout) {
44855               storedContext = null;
44856               storedArgs = [];
44857           }
44858       };
44859       return function wrapper() {
44860           var args = [];
44861           for (var _i = 0; _i < arguments.length; _i++) {
44862               args[_i] = arguments[_i];
44863           }
44864           var now = Date.now();
44865           var remaining = wait - (now - previous);
44866           storedContext = this;
44867           storedArgs = args;
44868           if (remaining <= 0 || remaining > wait) {
44869               if (timeout) {
44870                   clearTimeout(timeout);
44871                   timeout = null;
44872               }
44873               previous = now;
44874               result = fn.apply(storedContext, storedArgs);
44875               if (!timeout) {
44876                   storedContext = null;
44877                   storedArgs = [];
44878               }
44879           }
44880           else if (!timeout) {
44881               timeout = window.setTimeout(later, remaining);
44882           }
44883           return result;
44884       };
44885   }
44886   
44887 });
44888
44889  
44890
44891