roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
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         this.addColumn(config[i]);
7316         
7317     }
7318
7319     /**
7320      * The width of columns which have no width specified (defaults to 100)
7321      * @type Number
7322      */
7323     this.defaultWidth = 100;
7324
7325     /**
7326      * Default sortable of columns which have no sortable specified (defaults to false)
7327      * @type Boolean
7328      */
7329     this.defaultSortable = false;
7330
7331     this.addEvents({
7332         /**
7333              * @event widthchange
7334              * Fires when the width of a column changes.
7335              * @param {ColumnModel} this
7336              * @param {Number} columnIndex The column index
7337              * @param {Number} newWidth The new width
7338              */
7339             "widthchange": true,
7340         /**
7341              * @event headerchange
7342              * Fires when the text of a header changes.
7343              * @param {ColumnModel} this
7344              * @param {Number} columnIndex The column index
7345              * @param {Number} newText The new header text
7346              */
7347             "headerchange": true,
7348         /**
7349              * @event hiddenchange
7350              * Fires when a column is hidden or "unhidden".
7351              * @param {ColumnModel} this
7352              * @param {Number} columnIndex The column index
7353              * @param {Boolean} hidden true if hidden, false otherwise
7354              */
7355             "hiddenchange": true,
7356             /**
7357          * @event columnmoved
7358          * Fires when a column is moved.
7359          * @param {ColumnModel} this
7360          * @param {Number} oldIndex
7361          * @param {Number} newIndex
7362          */
7363         "columnmoved" : true,
7364         /**
7365          * @event columlockchange
7366          * Fires when a column's locked state is changed
7367          * @param {ColumnModel} this
7368          * @param {Number} colIndex
7369          * @param {Boolean} locked true if locked
7370          */
7371         "columnlockchange" : true
7372     });
7373     Roo.grid.ColumnModel.superclass.constructor.call(this);
7374 };
7375 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7376     /**
7377      * @cfg {String} header The header text to display in the Grid view.
7378      */
7379     /**
7380      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7381      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7382      * specified, the column's index is used as an index into the Record's data Array.
7383      */
7384     /**
7385      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7386      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7387      */
7388     /**
7389      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7390      * Defaults to the value of the {@link #defaultSortable} property.
7391      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7392      */
7393     /**
7394      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7395      */
7396     /**
7397      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7398      */
7399     /**
7400      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7401      */
7402     /**
7403      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7404      */
7405     /**
7406      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7407      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7408      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7409      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7410      */
7411        /**
7412      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7413      */
7414     /**
7415      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7416      */
7417     /**
7418      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7419      */
7420     /**
7421      * @cfg {String} cursor (Optional)
7422      */
7423     /**
7424      * @cfg {String} tooltip (Optional)
7425      */
7426     /**
7427      * @cfg {Number} xs (Optional)
7428      */
7429     /**
7430      * @cfg {Number} sm (Optional)
7431      */
7432     /**
7433      * @cfg {Number} md (Optional)
7434      */
7435     /**
7436      * @cfg {Number} lg (Optional)
7437      */
7438     /**
7439      * Returns the id of the column at the specified index.
7440      * @param {Number} index The column index
7441      * @return {String} the id
7442      */
7443     getColumnId : function(index){
7444         return this.config[index].id;
7445     },
7446
7447     /**
7448      * Returns the column for a specified id.
7449      * @param {String} id The column id
7450      * @return {Object} the column
7451      */
7452     getColumnById : function(id){
7453         return this.lookup[id];
7454     },
7455
7456     
7457     /**
7458      * Returns the column Object for a specified dataIndex.
7459      * @param {String} dataIndex The column dataIndex
7460      * @return {Object|Boolean} the column or false if not found
7461      */
7462     getColumnByDataIndex: function(dataIndex){
7463         var index = this.findColumnIndex(dataIndex);
7464         return index > -1 ? this.config[index] : false;
7465     },
7466     
7467     /**
7468      * Returns the index for a specified column id.
7469      * @param {String} id The column id
7470      * @return {Number} the index, or -1 if not found
7471      */
7472     getIndexById : function(id){
7473         for(var i = 0, len = this.config.length; i < len; i++){
7474             if(this.config[i].id == id){
7475                 return i;
7476             }
7477         }
7478         return -1;
7479     },
7480     
7481     /**
7482      * Returns the index for a specified column dataIndex.
7483      * @param {String} dataIndex The column dataIndex
7484      * @return {Number} the index, or -1 if not found
7485      */
7486     
7487     findColumnIndex : function(dataIndex){
7488         for(var i = 0, len = this.config.length; i < len; i++){
7489             if(this.config[i].dataIndex == dataIndex){
7490                 return i;
7491             }
7492         }
7493         return -1;
7494     },
7495     
7496     
7497     moveColumn : function(oldIndex, newIndex){
7498         var c = this.config[oldIndex];
7499         this.config.splice(oldIndex, 1);
7500         this.config.splice(newIndex, 0, c);
7501         this.dataMap = null;
7502         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7503     },
7504
7505     isLocked : function(colIndex){
7506         return this.config[colIndex].locked === true;
7507     },
7508
7509     setLocked : function(colIndex, value, suppressEvent){
7510         if(this.isLocked(colIndex) == value){
7511             return;
7512         }
7513         this.config[colIndex].locked = value;
7514         if(!suppressEvent){
7515             this.fireEvent("columnlockchange", this, colIndex, value);
7516         }
7517     },
7518
7519     getTotalLockedWidth : function(){
7520         var totalWidth = 0;
7521         for(var i = 0; i < this.config.length; i++){
7522             if(this.isLocked(i) && !this.isHidden(i)){
7523                 this.totalWidth += this.getColumnWidth(i);
7524             }
7525         }
7526         return totalWidth;
7527     },
7528
7529     getLockedCount : function(){
7530         for(var i = 0, len = this.config.length; i < len; i++){
7531             if(!this.isLocked(i)){
7532                 return i;
7533             }
7534         }
7535         
7536         return this.config.length;
7537     },
7538
7539     /**
7540      * Returns the number of columns.
7541      * @return {Number}
7542      */
7543     getColumnCount : function(visibleOnly){
7544         if(visibleOnly === true){
7545             var c = 0;
7546             for(var i = 0, len = this.config.length; i < len; i++){
7547                 if(!this.isHidden(i)){
7548                     c++;
7549                 }
7550             }
7551             return c;
7552         }
7553         return this.config.length;
7554     },
7555
7556     /**
7557      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7558      * @param {Function} fn
7559      * @param {Object} scope (optional)
7560      * @return {Array} result
7561      */
7562     getColumnsBy : function(fn, scope){
7563         var r = [];
7564         for(var i = 0, len = this.config.length; i < len; i++){
7565             var c = this.config[i];
7566             if(fn.call(scope||this, c, i) === true){
7567                 r[r.length] = c;
7568             }
7569         }
7570         return r;
7571     },
7572
7573     /**
7574      * Returns true if the specified column is sortable.
7575      * @param {Number} col The column index
7576      * @return {Boolean}
7577      */
7578     isSortable : function(col){
7579         if(typeof this.config[col].sortable == "undefined"){
7580             return this.defaultSortable;
7581         }
7582         return this.config[col].sortable;
7583     },
7584
7585     /**
7586      * Returns the rendering (formatting) function defined for the column.
7587      * @param {Number} col The column index.
7588      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7589      */
7590     getRenderer : function(col){
7591         if(!this.config[col].renderer){
7592             return Roo.grid.ColumnModel.defaultRenderer;
7593         }
7594         return this.config[col].renderer;
7595     },
7596
7597     /**
7598      * Sets the rendering (formatting) function for a column.
7599      * @param {Number} col The column index
7600      * @param {Function} fn The function to use to process the cell's raw data
7601      * to return HTML markup for the grid view. The render function is called with
7602      * the following parameters:<ul>
7603      * <li>Data value.</li>
7604      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7605      * <li>css A CSS style string to apply to the table cell.</li>
7606      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7607      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7608      * <li>Row index</li>
7609      * <li>Column index</li>
7610      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7611      */
7612     setRenderer : function(col, fn){
7613         this.config[col].renderer = fn;
7614     },
7615
7616     /**
7617      * Returns the width for the specified column.
7618      * @param {Number} col The column index
7619      * @return {Number}
7620      */
7621     getColumnWidth : function(col){
7622         return this.config[col].width * 1 || this.defaultWidth;
7623     },
7624
7625     /**
7626      * Sets the width for a column.
7627      * @param {Number} col The column index
7628      * @param {Number} width The new width
7629      */
7630     setColumnWidth : function(col, width, suppressEvent){
7631         this.config[col].width = width;
7632         this.totalWidth = null;
7633         if(!suppressEvent){
7634              this.fireEvent("widthchange", this, col, width);
7635         }
7636     },
7637
7638     /**
7639      * Returns the total width of all columns.
7640      * @param {Boolean} includeHidden True to include hidden column widths
7641      * @return {Number}
7642      */
7643     getTotalWidth : function(includeHidden){
7644         if(!this.totalWidth){
7645             this.totalWidth = 0;
7646             for(var i = 0, len = this.config.length; i < len; i++){
7647                 if(includeHidden || !this.isHidden(i)){
7648                     this.totalWidth += this.getColumnWidth(i);
7649                 }
7650             }
7651         }
7652         return this.totalWidth;
7653     },
7654
7655     /**
7656      * Returns the header for the specified column.
7657      * @param {Number} col The column index
7658      * @return {String}
7659      */
7660     getColumnHeader : function(col){
7661         return this.config[col].header;
7662     },
7663
7664     /**
7665      * Sets the header for a column.
7666      * @param {Number} col The column index
7667      * @param {String} header The new header
7668      */
7669     setColumnHeader : function(col, header){
7670         this.config[col].header = header;
7671         this.fireEvent("headerchange", this, col, header);
7672     },
7673
7674     /**
7675      * Returns the tooltip for the specified column.
7676      * @param {Number} col The column index
7677      * @return {String}
7678      */
7679     getColumnTooltip : function(col){
7680             return this.config[col].tooltip;
7681     },
7682     /**
7683      * Sets the tooltip for a column.
7684      * @param {Number} col The column index
7685      * @param {String} tooltip The new tooltip
7686      */
7687     setColumnTooltip : function(col, tooltip){
7688             this.config[col].tooltip = tooltip;
7689     },
7690
7691     /**
7692      * Returns the dataIndex for the specified column.
7693      * @param {Number} col The column index
7694      * @return {Number}
7695      */
7696     getDataIndex : function(col){
7697         return this.config[col].dataIndex;
7698     },
7699
7700     /**
7701      * Sets the dataIndex for a column.
7702      * @param {Number} col The column index
7703      * @param {Number} dataIndex The new dataIndex
7704      */
7705     setDataIndex : function(col, dataIndex){
7706         this.config[col].dataIndex = dataIndex;
7707     },
7708
7709     
7710     
7711     /**
7712      * Returns true if the cell is editable.
7713      * @param {Number} colIndex The column index
7714      * @param {Number} rowIndex The row index - this is nto actually used..?
7715      * @return {Boolean}
7716      */
7717     isCellEditable : function(colIndex, rowIndex){
7718         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7719     },
7720
7721     /**
7722      * Returns the editor defined for the cell/column.
7723      * return false or null to disable editing.
7724      * @param {Number} colIndex The column index
7725      * @param {Number} rowIndex The row index
7726      * @return {Object}
7727      */
7728     getCellEditor : function(colIndex, rowIndex){
7729         return this.config[colIndex].editor;
7730     },
7731
7732     /**
7733      * Sets if a column is editable.
7734      * @param {Number} col The column index
7735      * @param {Boolean} editable True if the column is editable
7736      */
7737     setEditable : function(col, editable){
7738         this.config[col].editable = editable;
7739     },
7740
7741
7742     /**
7743      * Returns true if the column is hidden.
7744      * @param {Number} colIndex The column index
7745      * @return {Boolean}
7746      */
7747     isHidden : function(colIndex){
7748         return this.config[colIndex].hidden;
7749     },
7750
7751
7752     /**
7753      * Returns true if the column width cannot be changed
7754      */
7755     isFixed : function(colIndex){
7756         return this.config[colIndex].fixed;
7757     },
7758
7759     /**
7760      * Returns true if the column can be resized
7761      * @return {Boolean}
7762      */
7763     isResizable : function(colIndex){
7764         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7765     },
7766     /**
7767      * Sets if a column is hidden.
7768      * @param {Number} colIndex The column index
7769      * @param {Boolean} hidden True if the column is hidden
7770      */
7771     setHidden : function(colIndex, hidden){
7772         this.config[colIndex].hidden = hidden;
7773         this.totalWidth = null;
7774         this.fireEvent("hiddenchange", this, colIndex, hidden);
7775     },
7776
7777     /**
7778      * Sets the editor for a column.
7779      * @param {Number} col The column index
7780      * @param {Object} editor The editor object
7781      */
7782     setEditor : function(col, editor){
7783         this.config[col].editor = editor;
7784     },
7785     /**
7786      * Add a column (experimental...) - defaults to adding to the end..
7787      * @param {Object} config 
7788     */
7789     addColumn : function(c)
7790     {
7791     
7792         var i = this.config.length;
7793         this.config[i] = c;
7794         
7795         if(typeof c.dataIndex == "undefined"){
7796             c.dataIndex = i;
7797         }
7798         if(typeof c.renderer == "string"){
7799             c.renderer = Roo.util.Format[c.renderer];
7800         }
7801         if(typeof c.id == "undefined"){
7802             c.id = Roo.id();
7803         }
7804         if(c.editor && c.editor.xtype){
7805             c.editor  = Roo.factory(c.editor, Roo.grid);
7806         }
7807         if(c.editor && c.editor.isFormField){
7808             c.editor = new Roo.grid.GridEditor(c.editor);
7809         }
7810         this.lookup[c.id] = c;
7811     }
7812     
7813 });
7814
7815 Roo.grid.ColumnModel.defaultRenderer = function(value)
7816 {
7817     if(typeof value == "object") {
7818         return value;
7819     }
7820         if(typeof value == "string" && value.length < 1){
7821             return "&#160;";
7822         }
7823     
7824         return String.format("{0}", value);
7825 };
7826
7827 // Alias for backwards compatibility
7828 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7829 /*
7830  * Based on:
7831  * Ext JS Library 1.1.1
7832  * Copyright(c) 2006-2007, Ext JS, LLC.
7833  *
7834  * Originally Released Under LGPL - original licence link has changed is not relivant.
7835  *
7836  * Fork - LGPL
7837  * <script type="text/javascript">
7838  */
7839  
7840 /**
7841  * @class Roo.LoadMask
7842  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7843  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7844  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7845  * element's UpdateManager load indicator and will be destroyed after the initial load.
7846  * @constructor
7847  * Create a new LoadMask
7848  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7849  * @param {Object} config The config object
7850  */
7851 Roo.LoadMask = function(el, config){
7852     this.el = Roo.get(el);
7853     Roo.apply(this, config);
7854     if(this.store){
7855         this.store.on('beforeload', this.onBeforeLoad, this);
7856         this.store.on('load', this.onLoad, this);
7857         this.store.on('loadexception', this.onLoadException, this);
7858         this.removeMask = false;
7859     }else{
7860         var um = this.el.getUpdateManager();
7861         um.showLoadIndicator = false; // disable the default indicator
7862         um.on('beforeupdate', this.onBeforeLoad, this);
7863         um.on('update', this.onLoad, this);
7864         um.on('failure', this.onLoad, this);
7865         this.removeMask = true;
7866     }
7867 };
7868
7869 Roo.LoadMask.prototype = {
7870     /**
7871      * @cfg {Boolean} removeMask
7872      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7873      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7874      */
7875     /**
7876      * @cfg {String} msg
7877      * The text to display in a centered loading message box (defaults to 'Loading...')
7878      */
7879     msg : 'Loading...',
7880     /**
7881      * @cfg {String} msgCls
7882      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7883      */
7884     msgCls : 'x-mask-loading',
7885
7886     /**
7887      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7888      * @type Boolean
7889      */
7890     disabled: false,
7891
7892     /**
7893      * Disables the mask to prevent it from being displayed
7894      */
7895     disable : function(){
7896        this.disabled = true;
7897     },
7898
7899     /**
7900      * Enables the mask so that it can be displayed
7901      */
7902     enable : function(){
7903         this.disabled = false;
7904     },
7905     
7906     onLoadException : function()
7907     {
7908         Roo.log(arguments);
7909         
7910         if (typeof(arguments[3]) != 'undefined') {
7911             Roo.MessageBox.alert("Error loading",arguments[3]);
7912         } 
7913         /*
7914         try {
7915             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7916                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7917             }   
7918         } catch(e) {
7919             
7920         }
7921         */
7922     
7923         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7924     },
7925     // private
7926     onLoad : function()
7927     {
7928         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7929     },
7930
7931     // private
7932     onBeforeLoad : function(){
7933         if(!this.disabled){
7934             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7935         }
7936     },
7937
7938     // private
7939     destroy : function(){
7940         if(this.store){
7941             this.store.un('beforeload', this.onBeforeLoad, this);
7942             this.store.un('load', this.onLoad, this);
7943             this.store.un('loadexception', this.onLoadException, this);
7944         }else{
7945             var um = this.el.getUpdateManager();
7946             um.un('beforeupdate', this.onBeforeLoad, this);
7947             um.un('update', this.onLoad, this);
7948             um.un('failure', this.onLoad, this);
7949         }
7950     }
7951 };/*
7952  * - LGPL
7953  *
7954  * table
7955  * 
7956  */
7957
7958 /**
7959  * @class Roo.bootstrap.Table
7960  * @extends Roo.bootstrap.Component
7961  * Bootstrap Table class
7962  * @cfg {String} cls table class
7963  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7964  * @cfg {String} bgcolor Specifies the background color for a table
7965  * @cfg {Number} border Specifies whether the table cells should have borders or not
7966  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7967  * @cfg {Number} cellspacing Specifies the space between cells
7968  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7969  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7970  * @cfg {String} sortable Specifies that the table should be sortable
7971  * @cfg {String} summary Specifies a summary of the content of a table
7972  * @cfg {Number} width Specifies the width of a table
7973  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7974  * 
7975  * @cfg {boolean} striped Should the rows be alternative striped
7976  * @cfg {boolean} bordered Add borders to the table
7977  * @cfg {boolean} hover Add hover highlighting
7978  * @cfg {boolean} condensed Format condensed
7979  * @cfg {boolean} responsive Format condensed
7980  * @cfg {Boolean} loadMask (true|false) default false
7981  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7982  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7983  * @cfg {Boolean} rowSelection (true|false) default false
7984  * @cfg {Boolean} cellSelection (true|false) default false
7985  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7986  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7987  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7988  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7989  
7990  * 
7991  * @constructor
7992  * Create a new Table
7993  * @param {Object} config The config object
7994  */
7995
7996 Roo.bootstrap.Table = function(config){
7997     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7998     
7999   
8000     
8001     // BC...
8002     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
8003     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
8004     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
8005     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
8006     
8007     this.sm = this.sm || {xtype: 'RowSelectionModel'};
8008     if (this.sm) {
8009         this.sm.grid = this;
8010         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
8011         this.sm = this.selModel;
8012         this.sm.xmodule = this.xmodule || false;
8013     }
8014     
8015     if (this.cm && typeof(this.cm.config) == 'undefined') {
8016         this.colModel = new Roo.grid.ColumnModel(this.cm);
8017         this.cm = this.colModel;
8018         this.cm.xmodule = this.xmodule || false;
8019     }
8020     if (this.store) {
8021         this.store= Roo.factory(this.store, Roo.data);
8022         this.ds = this.store;
8023         this.ds.xmodule = this.xmodule || false;
8024          
8025     }
8026     if (this.footer && this.store) {
8027         this.footer.dataSource = this.ds;
8028         this.footer = Roo.factory(this.footer);
8029     }
8030     
8031     /** @private */
8032     this.addEvents({
8033         /**
8034          * @event cellclick
8035          * Fires when a cell is clicked
8036          * @param {Roo.bootstrap.Table} this
8037          * @param {Roo.Element} el
8038          * @param {Number} rowIndex
8039          * @param {Number} columnIndex
8040          * @param {Roo.EventObject} e
8041          */
8042         "cellclick" : true,
8043         /**
8044          * @event celldblclick
8045          * Fires when a cell is double clicked
8046          * @param {Roo.bootstrap.Table} this
8047          * @param {Roo.Element} el
8048          * @param {Number} rowIndex
8049          * @param {Number} columnIndex
8050          * @param {Roo.EventObject} e
8051          */
8052         "celldblclick" : true,
8053         /**
8054          * @event rowclick
8055          * Fires when a row is clicked
8056          * @param {Roo.bootstrap.Table} this
8057          * @param {Roo.Element} el
8058          * @param {Number} rowIndex
8059          * @param {Roo.EventObject} e
8060          */
8061         "rowclick" : true,
8062         /**
8063          * @event rowdblclick
8064          * Fires when a row is double clicked
8065          * @param {Roo.bootstrap.Table} this
8066          * @param {Roo.Element} el
8067          * @param {Number} rowIndex
8068          * @param {Roo.EventObject} e
8069          */
8070         "rowdblclick" : true,
8071         /**
8072          * @event mouseover
8073          * Fires when a mouseover occur
8074          * @param {Roo.bootstrap.Table} this
8075          * @param {Roo.Element} el
8076          * @param {Number} rowIndex
8077          * @param {Number} columnIndex
8078          * @param {Roo.EventObject} e
8079          */
8080         "mouseover" : true,
8081         /**
8082          * @event mouseout
8083          * Fires when a mouseout occur
8084          * @param {Roo.bootstrap.Table} this
8085          * @param {Roo.Element} el
8086          * @param {Number} rowIndex
8087          * @param {Number} columnIndex
8088          * @param {Roo.EventObject} e
8089          */
8090         "mouseout" : true,
8091         /**
8092          * @event rowclass
8093          * Fires when a row is rendered, so you can change add a style to it.
8094          * @param {Roo.bootstrap.Table} this
8095          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
8096          */
8097         'rowclass' : true,
8098           /**
8099          * @event rowsrendered
8100          * Fires when all the  rows have been rendered
8101          * @param {Roo.bootstrap.Table} this
8102          */
8103         'rowsrendered' : true,
8104         /**
8105          * @event contextmenu
8106          * The raw contextmenu event for the entire grid.
8107          * @param {Roo.EventObject} e
8108          */
8109         "contextmenu" : true,
8110         /**
8111          * @event rowcontextmenu
8112          * Fires when a row is right clicked
8113          * @param {Roo.bootstrap.Table} this
8114          * @param {Number} rowIndex
8115          * @param {Roo.EventObject} e
8116          */
8117         "rowcontextmenu" : true,
8118         /**
8119          * @event cellcontextmenu
8120          * Fires when a cell is right clicked
8121          * @param {Roo.bootstrap.Table} this
8122          * @param {Number} rowIndex
8123          * @param {Number} cellIndex
8124          * @param {Roo.EventObject} e
8125          */
8126          "cellcontextmenu" : true,
8127          /**
8128          * @event headercontextmenu
8129          * Fires when a header is right clicked
8130          * @param {Roo.bootstrap.Table} this
8131          * @param {Number} columnIndex
8132          * @param {Roo.EventObject} e
8133          */
8134         "headercontextmenu" : true
8135     });
8136 };
8137
8138 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
8139     
8140     cls: false,
8141     align: false,
8142     bgcolor: false,
8143     border: false,
8144     cellpadding: false,
8145     cellspacing: false,
8146     frame: false,
8147     rules: false,
8148     sortable: false,
8149     summary: false,
8150     width: false,
8151     striped : false,
8152     scrollBody : false,
8153     bordered: false,
8154     hover:  false,
8155     condensed : false,
8156     responsive : false,
8157     sm : false,
8158     cm : false,
8159     store : false,
8160     loadMask : false,
8161     footerShow : true,
8162     headerShow : true,
8163   
8164     rowSelection : false,
8165     cellSelection : false,
8166     layout : false,
8167     
8168     // Roo.Element - the tbody
8169     mainBody: false,
8170     // Roo.Element - thead element
8171     mainHead: false,
8172     
8173     container: false, // used by gridpanel...
8174     
8175     lazyLoad : false,
8176     
8177     CSS : Roo.util.CSS,
8178     
8179     auto_hide_footer : false,
8180     
8181     getAutoCreate : function()
8182     {
8183         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
8184         
8185         cfg = {
8186             tag: 'table',
8187             cls : 'table',
8188             cn : []
8189         };
8190         if (this.scrollBody) {
8191             cfg.cls += ' table-body-fixed';
8192         }    
8193         if (this.striped) {
8194             cfg.cls += ' table-striped';
8195         }
8196         
8197         if (this.hover) {
8198             cfg.cls += ' table-hover';
8199         }
8200         if (this.bordered) {
8201             cfg.cls += ' table-bordered';
8202         }
8203         if (this.condensed) {
8204             cfg.cls += ' table-condensed';
8205         }
8206         if (this.responsive) {
8207             cfg.cls += ' table-responsive';
8208         }
8209         
8210         if (this.cls) {
8211             cfg.cls+=  ' ' +this.cls;
8212         }
8213         
8214         // this lot should be simplifed...
8215         var _t = this;
8216         var cp = [
8217             'align',
8218             'bgcolor',
8219             'border',
8220             'cellpadding',
8221             'cellspacing',
8222             'frame',
8223             'rules',
8224             'sortable',
8225             'summary',
8226             'width'
8227         ].forEach(function(k) {
8228             if (_t[k]) {
8229                 cfg[k] = _t[k];
8230             }
8231         });
8232         
8233         
8234         if (this.layout) {
8235             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
8236         }
8237         
8238         if(this.store || this.cm){
8239             if(this.headerShow){
8240                 cfg.cn.push(this.renderHeader());
8241             }
8242             
8243             cfg.cn.push(this.renderBody());
8244             
8245             if(this.footerShow){
8246                 cfg.cn.push(this.renderFooter());
8247             }
8248             // where does this come from?
8249             //cfg.cls+=  ' TableGrid';
8250         }
8251         
8252         return { cn : [ cfg ] };
8253     },
8254     
8255     initEvents : function()
8256     {   
8257         if(!this.store || !this.cm){
8258             return;
8259         }
8260         if (this.selModel) {
8261             this.selModel.initEvents();
8262         }
8263         
8264         
8265         //Roo.log('initEvents with ds!!!!');
8266         
8267         this.mainBody = this.el.select('tbody', true).first();
8268         this.mainHead = this.el.select('thead', true).first();
8269         this.mainFoot = this.el.select('tfoot', true).first();
8270         
8271         
8272         
8273         
8274         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8275             e.on('click', this.sort, this);
8276         }, this);
8277         
8278         this.mainBody.on("click", this.onClick, this);
8279         this.mainBody.on("dblclick", this.onDblClick, this);
8280         
8281         // why is this done????? = it breaks dialogs??
8282         //this.parent().el.setStyle('position', 'relative');
8283         
8284         
8285         if (this.footer) {
8286             this.footer.parentId = this.id;
8287             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8288             
8289             if(this.lazyLoad){
8290                 this.el.select('tfoot tr td').first().addClass('hide');
8291             }
8292         } 
8293         
8294         if(this.loadMask) {
8295             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8296         }
8297         
8298         this.store.on('load', this.onLoad, this);
8299         this.store.on('beforeload', this.onBeforeLoad, this);
8300         this.store.on('update', this.onUpdate, this);
8301         this.store.on('add', this.onAdd, this);
8302         this.store.on("clear", this.clear, this);
8303         
8304         this.el.on("contextmenu", this.onContextMenu, this);
8305         
8306         this.mainBody.on('scroll', this.onBodyScroll, this);
8307         
8308         this.cm.on("headerchange", this.onHeaderChange, this);
8309         
8310         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8311         
8312     },
8313     
8314     onContextMenu : function(e, t)
8315     {
8316         this.processEvent("contextmenu", e);
8317     },
8318     
8319     processEvent : function(name, e)
8320     {
8321         if (name != 'touchstart' ) {
8322             this.fireEvent(name, e);    
8323         }
8324         
8325         var t = e.getTarget();
8326         
8327         var cell = Roo.get(t);
8328         
8329         if(!cell){
8330             return;
8331         }
8332         
8333         if(cell.findParent('tfoot', false, true)){
8334             return;
8335         }
8336         
8337         if(cell.findParent('thead', false, true)){
8338             
8339             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8340                 cell = Roo.get(t).findParent('th', false, true);
8341                 if (!cell) {
8342                     Roo.log("failed to find th in thead?");
8343                     Roo.log(e.getTarget());
8344                     return;
8345                 }
8346             }
8347             
8348             var cellIndex = cell.dom.cellIndex;
8349             
8350             var ename = name == 'touchstart' ? 'click' : name;
8351             this.fireEvent("header" + ename, this, cellIndex, e);
8352             
8353             return;
8354         }
8355         
8356         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8357             cell = Roo.get(t).findParent('td', false, true);
8358             if (!cell) {
8359                 Roo.log("failed to find th in tbody?");
8360                 Roo.log(e.getTarget());
8361                 return;
8362             }
8363         }
8364         
8365         var row = cell.findParent('tr', false, true);
8366         var cellIndex = cell.dom.cellIndex;
8367         var rowIndex = row.dom.rowIndex - 1;
8368         
8369         if(row !== false){
8370             
8371             this.fireEvent("row" + name, this, rowIndex, e);
8372             
8373             if(cell !== false){
8374             
8375                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8376             }
8377         }
8378         
8379     },
8380     
8381     onMouseover : function(e, el)
8382     {
8383         var cell = Roo.get(el);
8384         
8385         if(!cell){
8386             return;
8387         }
8388         
8389         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8390             cell = cell.findParent('td', false, true);
8391         }
8392         
8393         var row = cell.findParent('tr', false, true);
8394         var cellIndex = cell.dom.cellIndex;
8395         var rowIndex = row.dom.rowIndex - 1; // start from 0
8396         
8397         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8398         
8399     },
8400     
8401     onMouseout : function(e, el)
8402     {
8403         var cell = Roo.get(el);
8404         
8405         if(!cell){
8406             return;
8407         }
8408         
8409         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8410             cell = cell.findParent('td', false, true);
8411         }
8412         
8413         var row = cell.findParent('tr', false, true);
8414         var cellIndex = cell.dom.cellIndex;
8415         var rowIndex = row.dom.rowIndex - 1; // start from 0
8416         
8417         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8418         
8419     },
8420     
8421     onClick : function(e, el)
8422     {
8423         var cell = Roo.get(el);
8424         
8425         if(!cell || (!this.cellSelection && !this.rowSelection)){
8426             return;
8427         }
8428         
8429         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8430             cell = cell.findParent('td', false, true);
8431         }
8432         
8433         if(!cell || typeof(cell) == 'undefined'){
8434             return;
8435         }
8436         
8437         var row = cell.findParent('tr', false, true);
8438         
8439         if(!row || typeof(row) == 'undefined'){
8440             return;
8441         }
8442         
8443         var cellIndex = cell.dom.cellIndex;
8444         var rowIndex = this.getRowIndex(row);
8445         
8446         // why??? - should these not be based on SelectionModel?
8447         if(this.cellSelection){
8448             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8449         }
8450         
8451         if(this.rowSelection){
8452             this.fireEvent('rowclick', this, row, rowIndex, e);
8453         }
8454         
8455         
8456     },
8457         
8458     onDblClick : function(e,el)
8459     {
8460         var cell = Roo.get(el);
8461         
8462         if(!cell || (!this.cellSelection && !this.rowSelection)){
8463             return;
8464         }
8465         
8466         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8467             cell = cell.findParent('td', false, true);
8468         }
8469         
8470         if(!cell || typeof(cell) == 'undefined'){
8471             return;
8472         }
8473         
8474         var row = cell.findParent('tr', false, true);
8475         
8476         if(!row || typeof(row) == 'undefined'){
8477             return;
8478         }
8479         
8480         var cellIndex = cell.dom.cellIndex;
8481         var rowIndex = this.getRowIndex(row);
8482         
8483         if(this.cellSelection){
8484             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8485         }
8486         
8487         if(this.rowSelection){
8488             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8489         }
8490     },
8491     
8492     sort : function(e,el)
8493     {
8494         var col = Roo.get(el);
8495         
8496         if(!col.hasClass('sortable')){
8497             return;
8498         }
8499         
8500         var sort = col.attr('sort');
8501         var dir = 'ASC';
8502         
8503         if(col.select('i', true).first().hasClass('fa-arrow-up')){
8504             dir = 'DESC';
8505         }
8506         
8507         this.store.sortInfo = {field : sort, direction : dir};
8508         
8509         if (this.footer) {
8510             Roo.log("calling footer first");
8511             this.footer.onClick('first');
8512         } else {
8513         
8514             this.store.load({ params : { start : 0 } });
8515         }
8516     },
8517     
8518     renderHeader : function()
8519     {
8520         var header = {
8521             tag: 'thead',
8522             cn : []
8523         };
8524         
8525         var cm = this.cm;
8526         this.totalWidth = 0;
8527         
8528         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8529             
8530             var config = cm.config[i];
8531             
8532             var c = {
8533                 tag: 'th',
8534                 cls : 'x-hcol-' + i,
8535                 style : '',
8536                 html: cm.getColumnHeader(i)
8537             };
8538             
8539             var hh = '';
8540             
8541             if(typeof(config.sortable) != 'undefined' && config.sortable){
8542                 c.cls = 'sortable';
8543                 c.html = '<i class="fa"></i>' + c.html;
8544             }
8545             
8546             // could use BS4 hidden-..-down 
8547             
8548             if(typeof(config.lgHeader) != 'undefined'){
8549                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8550             }
8551             
8552             if(typeof(config.mdHeader) != 'undefined'){
8553                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8554             }
8555             
8556             if(typeof(config.smHeader) != 'undefined'){
8557                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8558             }
8559             
8560             if(typeof(config.xsHeader) != 'undefined'){
8561                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8562             }
8563             
8564             if(hh.length){
8565                 c.html = hh;
8566             }
8567             
8568             if(typeof(config.tooltip) != 'undefined'){
8569                 c.tooltip = config.tooltip;
8570             }
8571             
8572             if(typeof(config.colspan) != 'undefined'){
8573                 c.colspan = config.colspan;
8574             }
8575             
8576             if(typeof(config.hidden) != 'undefined' && config.hidden){
8577                 c.style += ' display:none;';
8578             }
8579             
8580             if(typeof(config.dataIndex) != 'undefined'){
8581                 c.sort = config.dataIndex;
8582             }
8583             
8584            
8585             
8586             if(typeof(config.align) != 'undefined' && config.align.length){
8587                 c.style += ' text-align:' + config.align + ';';
8588             }
8589             
8590             if(typeof(config.width) != 'undefined'){
8591                 c.style += ' width:' + config.width + 'px;';
8592                 this.totalWidth += config.width;
8593             } else {
8594                 this.totalWidth += 100; // assume minimum of 100 per column?
8595             }
8596             
8597             if(typeof(config.cls) != 'undefined'){
8598                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8599             }
8600             
8601             ['xs','sm','md','lg'].map(function(size){
8602                 
8603                 if(typeof(config[size]) == 'undefined'){
8604                     return;
8605                 }
8606                  
8607                 if (!config[size]) { // 0 = hidden
8608                     // BS 4 '0' is treated as hide that column and below.
8609                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8610                     return;
8611                 }
8612                 
8613                 c.cls += ' col-' + size + '-' + config[size] + (
8614                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8615                 );
8616                 
8617                 
8618             });
8619             
8620             header.cn.push(c)
8621         }
8622         
8623         return header;
8624     },
8625     
8626     renderBody : function()
8627     {
8628         var body = {
8629             tag: 'tbody',
8630             cn : [
8631                 {
8632                     tag: 'tr',
8633                     cn : [
8634                         {
8635                             tag : 'td',
8636                             colspan :  this.cm.getColumnCount()
8637                         }
8638                     ]
8639                 }
8640             ]
8641         };
8642         
8643         return body;
8644     },
8645     
8646     renderFooter : function()
8647     {
8648         var footer = {
8649             tag: 'tfoot',
8650             cn : [
8651                 {
8652                     tag: 'tr',
8653                     cn : [
8654                         {
8655                             tag : 'td',
8656                             colspan :  this.cm.getColumnCount()
8657                         }
8658                     ]
8659                 }
8660             ]
8661         };
8662         
8663         return footer;
8664     },
8665     
8666     
8667     
8668     onLoad : function()
8669     {
8670 //        Roo.log('ds onload');
8671         this.clear();
8672         
8673         var _this = this;
8674         var cm = this.cm;
8675         var ds = this.store;
8676         
8677         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8678             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8679             if (_this.store.sortInfo) {
8680                     
8681                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8682                     e.select('i', true).addClass(['fa-arrow-up']);
8683                 }
8684                 
8685                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8686                     e.select('i', true).addClass(['fa-arrow-down']);
8687                 }
8688             }
8689         });
8690         
8691         var tbody =  this.mainBody;
8692               
8693         if(ds.getCount() > 0){
8694             ds.data.each(function(d,rowIndex){
8695                 var row =  this.renderRow(cm, ds, rowIndex);
8696                 
8697                 tbody.createChild(row);
8698                 
8699                 var _this = this;
8700                 
8701                 if(row.cellObjects.length){
8702                     Roo.each(row.cellObjects, function(r){
8703                         _this.renderCellObject(r);
8704                     })
8705                 }
8706                 
8707             }, this);
8708         }
8709         
8710         var tfoot = this.el.select('tfoot', true).first();
8711         
8712         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8713             
8714             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8715             
8716             var total = this.ds.getTotalCount();
8717             
8718             if(this.footer.pageSize < total){
8719                 this.mainFoot.show();
8720             }
8721         }
8722         
8723         Roo.each(this.el.select('tbody td', true).elements, function(e){
8724             e.on('mouseover', _this.onMouseover, _this);
8725         });
8726         
8727         Roo.each(this.el.select('tbody td', true).elements, function(e){
8728             e.on('mouseout', _this.onMouseout, _this);
8729         });
8730         this.fireEvent('rowsrendered', this);
8731         
8732         this.autoSize();
8733     },
8734     
8735     
8736     onUpdate : function(ds,record)
8737     {
8738         this.refreshRow(record);
8739         this.autoSize();
8740     },
8741     
8742     onRemove : function(ds, record, index, isUpdate){
8743         if(isUpdate !== true){
8744             this.fireEvent("beforerowremoved", this, index, record);
8745         }
8746         var bt = this.mainBody.dom;
8747         
8748         var rows = this.el.select('tbody > tr', true).elements;
8749         
8750         if(typeof(rows[index]) != 'undefined'){
8751             bt.removeChild(rows[index].dom);
8752         }
8753         
8754 //        if(bt.rows[index]){
8755 //            bt.removeChild(bt.rows[index]);
8756 //        }
8757         
8758         if(isUpdate !== true){
8759             //this.stripeRows(index);
8760             //this.syncRowHeights(index, index);
8761             //this.layout();
8762             this.fireEvent("rowremoved", this, index, record);
8763         }
8764     },
8765     
8766     onAdd : function(ds, records, rowIndex)
8767     {
8768         //Roo.log('on Add called');
8769         // - note this does not handle multiple adding very well..
8770         var bt = this.mainBody.dom;
8771         for (var i =0 ; i < records.length;i++) {
8772             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8773             //Roo.log(records[i]);
8774             //Roo.log(this.store.getAt(rowIndex+i));
8775             this.insertRow(this.store, rowIndex + i, false);
8776             return;
8777         }
8778         
8779     },
8780     
8781     
8782     refreshRow : function(record){
8783         var ds = this.store, index;
8784         if(typeof record == 'number'){
8785             index = record;
8786             record = ds.getAt(index);
8787         }else{
8788             index = ds.indexOf(record);
8789             if (index < 0) {
8790                 return; // should not happen - but seems to 
8791             }
8792         }
8793         this.insertRow(ds, index, true);
8794         this.autoSize();
8795         this.onRemove(ds, record, index+1, true);
8796         this.autoSize();
8797         //this.syncRowHeights(index, index);
8798         //this.layout();
8799         this.fireEvent("rowupdated", this, index, record);
8800     },
8801     
8802     insertRow : function(dm, rowIndex, isUpdate){
8803         
8804         if(!isUpdate){
8805             this.fireEvent("beforerowsinserted", this, rowIndex);
8806         }
8807             //var s = this.getScrollState();
8808         var row = this.renderRow(this.cm, this.store, rowIndex);
8809         // insert before rowIndex..
8810         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8811         
8812         var _this = this;
8813                 
8814         if(row.cellObjects.length){
8815             Roo.each(row.cellObjects, function(r){
8816                 _this.renderCellObject(r);
8817             })
8818         }
8819             
8820         if(!isUpdate){
8821             this.fireEvent("rowsinserted", this, rowIndex);
8822             //this.syncRowHeights(firstRow, lastRow);
8823             //this.stripeRows(firstRow);
8824             //this.layout();
8825         }
8826         
8827     },
8828     
8829     
8830     getRowDom : function(rowIndex)
8831     {
8832         var rows = this.el.select('tbody > tr', true).elements;
8833         
8834         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8835         
8836     },
8837     // returns the object tree for a tr..
8838   
8839     
8840     renderRow : function(cm, ds, rowIndex) 
8841     {
8842         var d = ds.getAt(rowIndex);
8843         
8844         var row = {
8845             tag : 'tr',
8846             cls : 'x-row-' + rowIndex,
8847             cn : []
8848         };
8849             
8850         var cellObjects = [];
8851         
8852         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8853             var config = cm.config[i];
8854             
8855             var renderer = cm.getRenderer(i);
8856             var value = '';
8857             var id = false;
8858             
8859             if(typeof(renderer) !== 'undefined'){
8860                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8861             }
8862             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8863             // and are rendered into the cells after the row is rendered - using the id for the element.
8864             
8865             if(typeof(value) === 'object'){
8866                 id = Roo.id();
8867                 cellObjects.push({
8868                     container : id,
8869                     cfg : value 
8870                 })
8871             }
8872             
8873             var rowcfg = {
8874                 record: d,
8875                 rowIndex : rowIndex,
8876                 colIndex : i,
8877                 rowClass : ''
8878             };
8879
8880             this.fireEvent('rowclass', this, rowcfg);
8881             
8882             var td = {
8883                 tag: 'td',
8884                 // this might end up displaying HTML?
8885                 // this is too messy... - better to only do it on columsn you know are going to be too long
8886                 //tooltip : (typeof(value) === 'object') ? '' : value,
8887                 cls : rowcfg.rowClass + ' x-col-' + i,
8888                 style: '',
8889                 html: (typeof(value) === 'object') ? '' : value
8890             };
8891             
8892             if (id) {
8893                 td.id = id;
8894             }
8895             
8896             if(typeof(config.colspan) != 'undefined'){
8897                 td.colspan = config.colspan;
8898             }
8899             
8900             if(typeof(config.hidden) != 'undefined' && config.hidden){
8901                 td.style += ' display:none;';
8902             }
8903             
8904             if(typeof(config.align) != 'undefined' && config.align.length){
8905                 td.style += ' text-align:' + config.align + ';';
8906             }
8907             if(typeof(config.valign) != 'undefined' && config.valign.length){
8908                 td.style += ' vertical-align:' + config.valign + ';';
8909             }
8910             
8911             if(typeof(config.width) != 'undefined'){
8912                 td.style += ' width:' +  config.width + 'px;';
8913             }
8914             
8915             if(typeof(config.cursor) != 'undefined'){
8916                 td.style += ' cursor:' +  config.cursor + ';';
8917             }
8918             
8919             if(typeof(config.cls) != 'undefined'){
8920                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8921             }
8922             
8923             ['xs','sm','md','lg'].map(function(size){
8924                 
8925                 if(typeof(config[size]) == 'undefined'){
8926                     return;
8927                 }
8928                 
8929                 
8930                   
8931                 if (!config[size]) { // 0 = hidden
8932                     // BS 4 '0' is treated as hide that column and below.
8933                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8934                     return;
8935                 }
8936                 
8937                 td.cls += ' col-' + size + '-' + config[size] + (
8938                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8939                 );
8940                  
8941
8942             });
8943             
8944             row.cn.push(td);
8945            
8946         }
8947         
8948         row.cellObjects = cellObjects;
8949         
8950         return row;
8951           
8952     },
8953     
8954     
8955     
8956     onBeforeLoad : function()
8957     {
8958         
8959     },
8960      /**
8961      * Remove all rows
8962      */
8963     clear : function()
8964     {
8965         this.el.select('tbody', true).first().dom.innerHTML = '';
8966     },
8967     /**
8968      * Show or hide a row.
8969      * @param {Number} rowIndex to show or hide
8970      * @param {Boolean} state hide
8971      */
8972     setRowVisibility : function(rowIndex, state)
8973     {
8974         var bt = this.mainBody.dom;
8975         
8976         var rows = this.el.select('tbody > tr', true).elements;
8977         
8978         if(typeof(rows[rowIndex]) == 'undefined'){
8979             return;
8980         }
8981         rows[rowIndex].dom.style.display = state ? '' : 'none';
8982     },
8983     
8984     
8985     getSelectionModel : function(){
8986         if(!this.selModel){
8987             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8988         }
8989         return this.selModel;
8990     },
8991     /*
8992      * Render the Roo.bootstrap object from renderder
8993      */
8994     renderCellObject : function(r)
8995     {
8996         var _this = this;
8997         
8998         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8999         
9000         var t = r.cfg.render(r.container);
9001         
9002         if(r.cfg.cn){
9003             Roo.each(r.cfg.cn, function(c){
9004                 var child = {
9005                     container: t.getChildContainer(),
9006                     cfg: c
9007                 };
9008                 _this.renderCellObject(child);
9009             })
9010         }
9011     },
9012     
9013     getRowIndex : function(row)
9014     {
9015         var rowIndex = -1;
9016         
9017         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9018             if(el != row){
9019                 return;
9020             }
9021             
9022             rowIndex = index;
9023         });
9024         
9025         return rowIndex;
9026     },
9027      /**
9028      * Returns the grid's underlying element = used by panel.Grid
9029      * @return {Element} The element
9030      */
9031     getGridEl : function(){
9032         return this.el;
9033     },
9034      /**
9035      * Forces a resize - used by panel.Grid
9036      * @return {Element} The element
9037      */
9038     autoSize : function()
9039     {
9040         //var ctr = Roo.get(this.container.dom.parentElement);
9041         var ctr = Roo.get(this.el.dom);
9042         
9043         var thd = this.getGridEl().select('thead',true).first();
9044         var tbd = this.getGridEl().select('tbody', true).first();
9045         var tfd = this.getGridEl().select('tfoot', true).first();
9046         
9047         var cw = ctr.getWidth();
9048         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9049         
9050         if (tbd) {
9051             
9052             tbd.setWidth(ctr.getWidth());
9053             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9054             // this needs fixing for various usage - currently only hydra job advers I think..
9055             //tdb.setHeight(
9056             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9057             //); 
9058             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9059             cw -= barsize;
9060         }
9061         cw = Math.max(cw, this.totalWidth);
9062         this.getGridEl().select('tbody tr',true).setWidth(cw);
9063         
9064         // resize 'expandable coloumn?
9065         
9066         return; // we doe not have a view in this design..
9067         
9068     },
9069     onBodyScroll: function()
9070     {
9071         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9072         if(this.mainHead){
9073             this.mainHead.setStyle({
9074                 'position' : 'relative',
9075                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9076             });
9077         }
9078         
9079         if(this.lazyLoad){
9080             
9081             var scrollHeight = this.mainBody.dom.scrollHeight;
9082             
9083             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9084             
9085             var height = this.mainBody.getHeight();
9086             
9087             if(scrollHeight - height == scrollTop) {
9088                 
9089                 var total = this.ds.getTotalCount();
9090                 
9091                 if(this.footer.cursor + this.footer.pageSize < total){
9092                     
9093                     this.footer.ds.load({
9094                         params : {
9095                             start : this.footer.cursor + this.footer.pageSize,
9096                             limit : this.footer.pageSize
9097                         },
9098                         add : true
9099                     });
9100                 }
9101             }
9102             
9103         }
9104     },
9105     
9106     onHeaderChange : function()
9107     {
9108         var header = this.renderHeader();
9109         var table = this.el.select('table', true).first();
9110         
9111         this.mainHead.remove();
9112         this.mainHead = table.createChild(header, this.mainBody, false);
9113         
9114         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9115             e.on('click', this.sort, this);
9116         }, this);
9117         
9118         
9119     },
9120     
9121     onHiddenChange : function(colModel, colIndex, hidden)
9122     {
9123         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9124         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9125         
9126         this.CSS.updateRule(thSelector, "display", "");
9127         this.CSS.updateRule(tdSelector, "display", "");
9128         
9129         if(hidden){
9130             this.CSS.updateRule(thSelector, "display", "none");
9131             this.CSS.updateRule(tdSelector, "display", "none");
9132         }
9133         
9134         this.onHeaderChange();
9135         this.onLoad();
9136     },
9137     
9138     setColumnWidth: function(col_index, width)
9139     {
9140         // width = "md-2 xs-2..."
9141         if(!this.colModel.config[col_index]) {
9142             return;
9143         }
9144         
9145         var w = width.split(" ");
9146         
9147         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9148         
9149         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9150         
9151         
9152         for(var j = 0; j < w.length; j++) {
9153             
9154             if(!w[j]) {
9155                 continue;
9156             }
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(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9169                 continue;
9170             }
9171             
9172             h_row[0].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             for(var i = 0; i < rows.length; i++) {
9178                 
9179                 var size_cls = w[j].split("-");
9180                 
9181                 if(!Number.isInteger(size_cls[1] * 1)) {
9182                     continue;
9183                 }
9184                 
9185                 if(!this.colModel.config[col_index][size_cls[0]]) {
9186                     continue;
9187                 }
9188                 
9189                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9190                     continue;
9191                 }
9192                 
9193                 rows[i].classList.replace(
9194                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9195                     "col-"+size_cls[0]+"-"+size_cls[1]
9196                 );
9197             }
9198             
9199             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9200         }
9201     }
9202 });
9203
9204  
9205
9206  /*
9207  * - LGPL
9208  *
9209  * table cell
9210  * 
9211  */
9212
9213 /**
9214  * @class Roo.bootstrap.TableCell
9215  * @extends Roo.bootstrap.Component
9216  * Bootstrap TableCell class
9217  * @cfg {String} html cell contain text
9218  * @cfg {String} cls cell class
9219  * @cfg {String} tag cell tag (td|th) default td
9220  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9221  * @cfg {String} align Aligns the content in a cell
9222  * @cfg {String} axis Categorizes cells
9223  * @cfg {String} bgcolor Specifies the background color of a cell
9224  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9225  * @cfg {Number} colspan Specifies the number of columns a cell should span
9226  * @cfg {String} headers Specifies one or more header cells a cell is related to
9227  * @cfg {Number} height Sets the height of a cell
9228  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9229  * @cfg {Number} rowspan Sets the number of rows a cell should span
9230  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9231  * @cfg {String} valign Vertical aligns the content in a cell
9232  * @cfg {Number} width Specifies the width of a cell
9233  * 
9234  * @constructor
9235  * Create a new TableCell
9236  * @param {Object} config The config object
9237  */
9238
9239 Roo.bootstrap.TableCell = function(config){
9240     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9241 };
9242
9243 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9244     
9245     html: false,
9246     cls: false,
9247     tag: false,
9248     abbr: false,
9249     align: false,
9250     axis: false,
9251     bgcolor: false,
9252     charoff: false,
9253     colspan: false,
9254     headers: false,
9255     height: false,
9256     nowrap: false,
9257     rowspan: false,
9258     scope: false,
9259     valign: false,
9260     width: false,
9261     
9262     
9263     getAutoCreate : function(){
9264         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9265         
9266         cfg = {
9267             tag: 'td'
9268         };
9269         
9270         if(this.tag){
9271             cfg.tag = this.tag;
9272         }
9273         
9274         if (this.html) {
9275             cfg.html=this.html
9276         }
9277         if (this.cls) {
9278             cfg.cls=this.cls
9279         }
9280         if (this.abbr) {
9281             cfg.abbr=this.abbr
9282         }
9283         if (this.align) {
9284             cfg.align=this.align
9285         }
9286         if (this.axis) {
9287             cfg.axis=this.axis
9288         }
9289         if (this.bgcolor) {
9290             cfg.bgcolor=this.bgcolor
9291         }
9292         if (this.charoff) {
9293             cfg.charoff=this.charoff
9294         }
9295         if (this.colspan) {
9296             cfg.colspan=this.colspan
9297         }
9298         if (this.headers) {
9299             cfg.headers=this.headers
9300         }
9301         if (this.height) {
9302             cfg.height=this.height
9303         }
9304         if (this.nowrap) {
9305             cfg.nowrap=this.nowrap
9306         }
9307         if (this.rowspan) {
9308             cfg.rowspan=this.rowspan
9309         }
9310         if (this.scope) {
9311             cfg.scope=this.scope
9312         }
9313         if (this.valign) {
9314             cfg.valign=this.valign
9315         }
9316         if (this.width) {
9317             cfg.width=this.width
9318         }
9319         
9320         
9321         return cfg;
9322     }
9323    
9324 });
9325
9326  
9327
9328  /*
9329  * - LGPL
9330  *
9331  * table row
9332  * 
9333  */
9334
9335 /**
9336  * @class Roo.bootstrap.TableRow
9337  * @extends Roo.bootstrap.Component
9338  * Bootstrap TableRow class
9339  * @cfg {String} cls row class
9340  * @cfg {String} align Aligns the content in a table row
9341  * @cfg {String} bgcolor Specifies a background color for a table row
9342  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9343  * @cfg {String} valign Vertical aligns the content in a table row
9344  * 
9345  * @constructor
9346  * Create a new TableRow
9347  * @param {Object} config The config object
9348  */
9349
9350 Roo.bootstrap.TableRow = function(config){
9351     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9352 };
9353
9354 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9355     
9356     cls: false,
9357     align: false,
9358     bgcolor: false,
9359     charoff: false,
9360     valign: false,
9361     
9362     getAutoCreate : function(){
9363         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9364         
9365         cfg = {
9366             tag: 'tr'
9367         };
9368             
9369         if(this.cls){
9370             cfg.cls = this.cls;
9371         }
9372         if(this.align){
9373             cfg.align = this.align;
9374         }
9375         if(this.bgcolor){
9376             cfg.bgcolor = this.bgcolor;
9377         }
9378         if(this.charoff){
9379             cfg.charoff = this.charoff;
9380         }
9381         if(this.valign){
9382             cfg.valign = this.valign;
9383         }
9384         
9385         return cfg;
9386     }
9387    
9388 });
9389
9390  
9391
9392  /*
9393  * - LGPL
9394  *
9395  * table body
9396  * 
9397  */
9398
9399 /**
9400  * @class Roo.bootstrap.TableBody
9401  * @extends Roo.bootstrap.Component
9402  * Bootstrap TableBody class
9403  * @cfg {String} cls element class
9404  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9405  * @cfg {String} align Aligns the content inside the element
9406  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9407  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9408  * 
9409  * @constructor
9410  * Create a new TableBody
9411  * @param {Object} config The config object
9412  */
9413
9414 Roo.bootstrap.TableBody = function(config){
9415     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9416 };
9417
9418 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9419     
9420     cls: false,
9421     tag: false,
9422     align: false,
9423     charoff: false,
9424     valign: false,
9425     
9426     getAutoCreate : function(){
9427         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9428         
9429         cfg = {
9430             tag: 'tbody'
9431         };
9432             
9433         if (this.cls) {
9434             cfg.cls=this.cls
9435         }
9436         if(this.tag){
9437             cfg.tag = this.tag;
9438         }
9439         
9440         if(this.align){
9441             cfg.align = this.align;
9442         }
9443         if(this.charoff){
9444             cfg.charoff = this.charoff;
9445         }
9446         if(this.valign){
9447             cfg.valign = this.valign;
9448         }
9449         
9450         return cfg;
9451     }
9452     
9453     
9454 //    initEvents : function()
9455 //    {
9456 //        
9457 //        if(!this.store){
9458 //            return;
9459 //        }
9460 //        
9461 //        this.store = Roo.factory(this.store, Roo.data);
9462 //        this.store.on('load', this.onLoad, this);
9463 //        
9464 //        this.store.load();
9465 //        
9466 //    },
9467 //    
9468 //    onLoad: function () 
9469 //    {   
9470 //        this.fireEvent('load', this);
9471 //    }
9472 //    
9473 //   
9474 });
9475
9476  
9477
9478  /*
9479  * Based on:
9480  * Ext JS Library 1.1.1
9481  * Copyright(c) 2006-2007, Ext JS, LLC.
9482  *
9483  * Originally Released Under LGPL - original licence link has changed is not relivant.
9484  *
9485  * Fork - LGPL
9486  * <script type="text/javascript">
9487  */
9488
9489 // as we use this in bootstrap.
9490 Roo.namespace('Roo.form');
9491  /**
9492  * @class Roo.form.Action
9493  * Internal Class used to handle form actions
9494  * @constructor
9495  * @param {Roo.form.BasicForm} el The form element or its id
9496  * @param {Object} config Configuration options
9497  */
9498
9499  
9500  
9501 // define the action interface
9502 Roo.form.Action = function(form, options){
9503     this.form = form;
9504     this.options = options || {};
9505 };
9506 /**
9507  * Client Validation Failed
9508  * @const 
9509  */
9510 Roo.form.Action.CLIENT_INVALID = 'client';
9511 /**
9512  * Server Validation Failed
9513  * @const 
9514  */
9515 Roo.form.Action.SERVER_INVALID = 'server';
9516  /**
9517  * Connect to Server Failed
9518  * @const 
9519  */
9520 Roo.form.Action.CONNECT_FAILURE = 'connect';
9521 /**
9522  * Reading Data from Server Failed
9523  * @const 
9524  */
9525 Roo.form.Action.LOAD_FAILURE = 'load';
9526
9527 Roo.form.Action.prototype = {
9528     type : 'default',
9529     failureType : undefined,
9530     response : undefined,
9531     result : undefined,
9532
9533     // interface method
9534     run : function(options){
9535
9536     },
9537
9538     // interface method
9539     success : function(response){
9540
9541     },
9542
9543     // interface method
9544     handleResponse : function(response){
9545
9546     },
9547
9548     // default connection failure
9549     failure : function(response){
9550         
9551         this.response = response;
9552         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9553         this.form.afterAction(this, false);
9554     },
9555
9556     processResponse : function(response){
9557         this.response = response;
9558         if(!response.responseText){
9559             return true;
9560         }
9561         this.result = this.handleResponse(response);
9562         return this.result;
9563     },
9564
9565     // utility functions used internally
9566     getUrl : function(appendParams){
9567         var url = this.options.url || this.form.url || this.form.el.dom.action;
9568         if(appendParams){
9569             var p = this.getParams();
9570             if(p){
9571                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9572             }
9573         }
9574         return url;
9575     },
9576
9577     getMethod : function(){
9578         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9579     },
9580
9581     getParams : function(){
9582         var bp = this.form.baseParams;
9583         var p = this.options.params;
9584         if(p){
9585             if(typeof p == "object"){
9586                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9587             }else if(typeof p == 'string' && bp){
9588                 p += '&' + Roo.urlEncode(bp);
9589             }
9590         }else if(bp){
9591             p = Roo.urlEncode(bp);
9592         }
9593         return p;
9594     },
9595
9596     createCallback : function(){
9597         return {
9598             success: this.success,
9599             failure: this.failure,
9600             scope: this,
9601             timeout: (this.form.timeout*1000),
9602             upload: this.form.fileUpload ? this.success : undefined
9603         };
9604     }
9605 };
9606
9607 Roo.form.Action.Submit = function(form, options){
9608     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9609 };
9610
9611 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9612     type : 'submit',
9613
9614     haveProgress : false,
9615     uploadComplete : false,
9616     
9617     // uploadProgress indicator.
9618     uploadProgress : function()
9619     {
9620         if (!this.form.progressUrl) {
9621             return;
9622         }
9623         
9624         if (!this.haveProgress) {
9625             Roo.MessageBox.progress("Uploading", "Uploading");
9626         }
9627         if (this.uploadComplete) {
9628            Roo.MessageBox.hide();
9629            return;
9630         }
9631         
9632         this.haveProgress = true;
9633    
9634         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9635         
9636         var c = new Roo.data.Connection();
9637         c.request({
9638             url : this.form.progressUrl,
9639             params: {
9640                 id : uid
9641             },
9642             method: 'GET',
9643             success : function(req){
9644                //console.log(data);
9645                 var rdata = false;
9646                 var edata;
9647                 try  {
9648                    rdata = Roo.decode(req.responseText)
9649                 } catch (e) {
9650                     Roo.log("Invalid data from server..");
9651                     Roo.log(edata);
9652                     return;
9653                 }
9654                 if (!rdata || !rdata.success) {
9655                     Roo.log(rdata);
9656                     Roo.MessageBox.alert(Roo.encode(rdata));
9657                     return;
9658                 }
9659                 var data = rdata.data;
9660                 
9661                 if (this.uploadComplete) {
9662                    Roo.MessageBox.hide();
9663                    return;
9664                 }
9665                    
9666                 if (data){
9667                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9668                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9669                     );
9670                 }
9671                 this.uploadProgress.defer(2000,this);
9672             },
9673        
9674             failure: function(data) {
9675                 Roo.log('progress url failed ');
9676                 Roo.log(data);
9677             },
9678             scope : this
9679         });
9680            
9681     },
9682     
9683     
9684     run : function()
9685     {
9686         // run get Values on the form, so it syncs any secondary forms.
9687         this.form.getValues();
9688         
9689         var o = this.options;
9690         var method = this.getMethod();
9691         var isPost = method == 'POST';
9692         if(o.clientValidation === false || this.form.isValid()){
9693             
9694             if (this.form.progressUrl) {
9695                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9696                     (new Date() * 1) + '' + Math.random());
9697                     
9698             } 
9699             
9700             
9701             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9702                 form:this.form.el.dom,
9703                 url:this.getUrl(!isPost),
9704                 method: method,
9705                 params:isPost ? this.getParams() : null,
9706                 isUpload: this.form.fileUpload,
9707                 formData : this.form.formData
9708             }));
9709             
9710             this.uploadProgress();
9711
9712         }else if (o.clientValidation !== false){ // client validation failed
9713             this.failureType = Roo.form.Action.CLIENT_INVALID;
9714             this.form.afterAction(this, false);
9715         }
9716     },
9717
9718     success : function(response)
9719     {
9720         this.uploadComplete= true;
9721         if (this.haveProgress) {
9722             Roo.MessageBox.hide();
9723         }
9724         
9725         
9726         var result = this.processResponse(response);
9727         if(result === true || result.success){
9728             this.form.afterAction(this, true);
9729             return;
9730         }
9731         if(result.errors){
9732             this.form.markInvalid(result.errors);
9733             this.failureType = Roo.form.Action.SERVER_INVALID;
9734         }
9735         this.form.afterAction(this, false);
9736     },
9737     failure : function(response)
9738     {
9739         this.uploadComplete= true;
9740         if (this.haveProgress) {
9741             Roo.MessageBox.hide();
9742         }
9743         
9744         this.response = response;
9745         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9746         this.form.afterAction(this, false);
9747     },
9748     
9749     handleResponse : function(response){
9750         if(this.form.errorReader){
9751             var rs = this.form.errorReader.read(response);
9752             var errors = [];
9753             if(rs.records){
9754                 for(var i = 0, len = rs.records.length; i < len; i++) {
9755                     var r = rs.records[i];
9756                     errors[i] = r.data;
9757                 }
9758             }
9759             if(errors.length < 1){
9760                 errors = null;
9761             }
9762             return {
9763                 success : rs.success,
9764                 errors : errors
9765             };
9766         }
9767         var ret = false;
9768         try {
9769             ret = Roo.decode(response.responseText);
9770         } catch (e) {
9771             ret = {
9772                 success: false,
9773                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9774                 errors : []
9775             };
9776         }
9777         return ret;
9778         
9779     }
9780 });
9781
9782
9783 Roo.form.Action.Load = function(form, options){
9784     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9785     this.reader = this.form.reader;
9786 };
9787
9788 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9789     type : 'load',
9790
9791     run : function(){
9792         
9793         Roo.Ajax.request(Roo.apply(
9794                 this.createCallback(), {
9795                     method:this.getMethod(),
9796                     url:this.getUrl(false),
9797                     params:this.getParams()
9798         }));
9799     },
9800
9801     success : function(response){
9802         
9803         var result = this.processResponse(response);
9804         if(result === true || !result.success || !result.data){
9805             this.failureType = Roo.form.Action.LOAD_FAILURE;
9806             this.form.afterAction(this, false);
9807             return;
9808         }
9809         this.form.clearInvalid();
9810         this.form.setValues(result.data);
9811         this.form.afterAction(this, true);
9812     },
9813
9814     handleResponse : function(response){
9815         if(this.form.reader){
9816             var rs = this.form.reader.read(response);
9817             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9818             return {
9819                 success : rs.success,
9820                 data : data
9821             };
9822         }
9823         return Roo.decode(response.responseText);
9824     }
9825 });
9826
9827 Roo.form.Action.ACTION_TYPES = {
9828     'load' : Roo.form.Action.Load,
9829     'submit' : Roo.form.Action.Submit
9830 };/*
9831  * - LGPL
9832  *
9833  * form
9834  *
9835  */
9836
9837 /**
9838  * @class Roo.bootstrap.Form
9839  * @extends Roo.bootstrap.Component
9840  * Bootstrap Form class
9841  * @cfg {String} method  GET | POST (default POST)
9842  * @cfg {String} labelAlign top | left (default top)
9843  * @cfg {String} align left  | right - for navbars
9844  * @cfg {Boolean} loadMask load mask when submit (default true)
9845
9846  *
9847  * @constructor
9848  * Create a new Form
9849  * @param {Object} config The config object
9850  */
9851
9852
9853 Roo.bootstrap.Form = function(config){
9854     
9855     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9856     
9857     Roo.bootstrap.Form.popover.apply();
9858     
9859     this.addEvents({
9860         /**
9861          * @event clientvalidation
9862          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9863          * @param {Form} this
9864          * @param {Boolean} valid true if the form has passed client-side validation
9865          */
9866         clientvalidation: true,
9867         /**
9868          * @event beforeaction
9869          * Fires before any action is performed. Return false to cancel the action.
9870          * @param {Form} this
9871          * @param {Action} action The action to be performed
9872          */
9873         beforeaction: true,
9874         /**
9875          * @event actionfailed
9876          * Fires when an action fails.
9877          * @param {Form} this
9878          * @param {Action} action The action that failed
9879          */
9880         actionfailed : true,
9881         /**
9882          * @event actioncomplete
9883          * Fires when an action is completed.
9884          * @param {Form} this
9885          * @param {Action} action The action that completed
9886          */
9887         actioncomplete : true
9888     });
9889 };
9890
9891 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9892
9893      /**
9894      * @cfg {String} method
9895      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9896      */
9897     method : 'POST',
9898     /**
9899      * @cfg {String} url
9900      * The URL to use for form actions if one isn't supplied in the action options.
9901      */
9902     /**
9903      * @cfg {Boolean} fileUpload
9904      * Set to true if this form is a file upload.
9905      */
9906
9907     /**
9908      * @cfg {Object} baseParams
9909      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9910      */
9911
9912     /**
9913      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9914      */
9915     timeout: 30,
9916     /**
9917      * @cfg {Sting} align (left|right) for navbar forms
9918      */
9919     align : 'left',
9920
9921     // private
9922     activeAction : null,
9923
9924     /**
9925      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9926      * element by passing it or its id or mask the form itself by passing in true.
9927      * @type Mixed
9928      */
9929     waitMsgTarget : false,
9930
9931     loadMask : true,
9932     
9933     /**
9934      * @cfg {Boolean} errorMask (true|false) default false
9935      */
9936     errorMask : false,
9937     
9938     /**
9939      * @cfg {Number} maskOffset Default 100
9940      */
9941     maskOffset : 100,
9942     
9943     /**
9944      * @cfg {Boolean} maskBody
9945      */
9946     maskBody : false,
9947
9948     getAutoCreate : function(){
9949
9950         var cfg = {
9951             tag: 'form',
9952             method : this.method || 'POST',
9953             id : this.id || Roo.id(),
9954             cls : ''
9955         };
9956         if (this.parent().xtype.match(/^Nav/)) {
9957             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9958
9959         }
9960
9961         if (this.labelAlign == 'left' ) {
9962             cfg.cls += ' form-horizontal';
9963         }
9964
9965
9966         return cfg;
9967     },
9968     initEvents : function()
9969     {
9970         this.el.on('submit', this.onSubmit, this);
9971         // this was added as random key presses on the form where triggering form submit.
9972         this.el.on('keypress', function(e) {
9973             if (e.getCharCode() != 13) {
9974                 return true;
9975             }
9976             // we might need to allow it for textareas.. and some other items.
9977             // check e.getTarget().
9978
9979             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9980                 return true;
9981             }
9982
9983             Roo.log("keypress blocked");
9984
9985             e.preventDefault();
9986             return false;
9987         });
9988         
9989     },
9990     // private
9991     onSubmit : function(e){
9992         e.stopEvent();
9993     },
9994
9995      /**
9996      * Returns true if client-side validation on the form is successful.
9997      * @return Boolean
9998      */
9999     isValid : function(){
10000         var items = this.getItems();
10001         var valid = true;
10002         var target = false;
10003         
10004         items.each(function(f){
10005             
10006             if(f.validate()){
10007                 return;
10008             }
10009             
10010             Roo.log('invalid field: ' + f.name);
10011             
10012             valid = false;
10013
10014             if(!target && f.el.isVisible(true)){
10015                 target = f;
10016             }
10017            
10018         });
10019         
10020         if(this.errorMask && !valid){
10021             Roo.bootstrap.Form.popover.mask(this, target);
10022         }
10023         
10024         return valid;
10025     },
10026     
10027     /**
10028      * Returns true if any fields in this form have changed since their original load.
10029      * @return Boolean
10030      */
10031     isDirty : function(){
10032         var dirty = false;
10033         var items = this.getItems();
10034         items.each(function(f){
10035            if(f.isDirty()){
10036                dirty = true;
10037                return false;
10038            }
10039            return true;
10040         });
10041         return dirty;
10042     },
10043      /**
10044      * Performs a predefined action (submit or load) or custom actions you define on this form.
10045      * @param {String} actionName The name of the action type
10046      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10047      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10048      * accept other config options):
10049      * <pre>
10050 Property          Type             Description
10051 ----------------  ---------------  ----------------------------------------------------------------------------------
10052 url               String           The url for the action (defaults to the form's url)
10053 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10054 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10055 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10056                                    validate the form on the client (defaults to false)
10057      * </pre>
10058      * @return {BasicForm} this
10059      */
10060     doAction : function(action, options){
10061         if(typeof action == 'string'){
10062             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10063         }
10064         if(this.fireEvent('beforeaction', this, action) !== false){
10065             this.beforeAction(action);
10066             action.run.defer(100, action);
10067         }
10068         return this;
10069     },
10070
10071     // private
10072     beforeAction : function(action){
10073         var o = action.options;
10074         
10075         if(this.loadMask){
10076             
10077             if(this.maskBody){
10078                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10079             } else {
10080                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10081             }
10082         }
10083         // not really supported yet.. ??
10084
10085         //if(this.waitMsgTarget === true){
10086         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10087         //}else if(this.waitMsgTarget){
10088         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10089         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10090         //}else {
10091         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10092        // }
10093
10094     },
10095
10096     // private
10097     afterAction : function(action, success){
10098         this.activeAction = null;
10099         var o = action.options;
10100
10101         if(this.loadMask){
10102             
10103             if(this.maskBody){
10104                 Roo.get(document.body).unmask();
10105             } else {
10106                 this.el.unmask();
10107             }
10108         }
10109         
10110         //if(this.waitMsgTarget === true){
10111 //            this.el.unmask();
10112         //}else if(this.waitMsgTarget){
10113         //    this.waitMsgTarget.unmask();
10114         //}else{
10115         //    Roo.MessageBox.updateProgress(1);
10116         //    Roo.MessageBox.hide();
10117        // }
10118         //
10119         if(success){
10120             if(o.reset){
10121                 this.reset();
10122             }
10123             Roo.callback(o.success, o.scope, [this, action]);
10124             this.fireEvent('actioncomplete', this, action);
10125
10126         }else{
10127
10128             // failure condition..
10129             // we have a scenario where updates need confirming.
10130             // eg. if a locking scenario exists..
10131             // we look for { errors : { needs_confirm : true }} in the response.
10132             if (
10133                 (typeof(action.result) != 'undefined')  &&
10134                 (typeof(action.result.errors) != 'undefined')  &&
10135                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10136            ){
10137                 var _t = this;
10138                 Roo.log("not supported yet");
10139                  /*
10140
10141                 Roo.MessageBox.confirm(
10142                     "Change requires confirmation",
10143                     action.result.errorMsg,
10144                     function(r) {
10145                         if (r != 'yes') {
10146                             return;
10147                         }
10148                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10149                     }
10150
10151                 );
10152                 */
10153
10154
10155                 return;
10156             }
10157
10158             Roo.callback(o.failure, o.scope, [this, action]);
10159             // show an error message if no failed handler is set..
10160             if (!this.hasListener('actionfailed')) {
10161                 Roo.log("need to add dialog support");
10162                 /*
10163                 Roo.MessageBox.alert("Error",
10164                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10165                         action.result.errorMsg :
10166                         "Saving Failed, please check your entries or try again"
10167                 );
10168                 */
10169             }
10170
10171             this.fireEvent('actionfailed', this, action);
10172         }
10173
10174     },
10175     /**
10176      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10177      * @param {String} id The value to search for
10178      * @return Field
10179      */
10180     findField : function(id){
10181         var items = this.getItems();
10182         var field = items.get(id);
10183         if(!field){
10184              items.each(function(f){
10185                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10186                     field = f;
10187                     return false;
10188                 }
10189                 return true;
10190             });
10191         }
10192         return field || null;
10193     },
10194      /**
10195      * Mark fields in this form invalid in bulk.
10196      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10197      * @return {BasicForm} this
10198      */
10199     markInvalid : function(errors){
10200         if(errors instanceof Array){
10201             for(var i = 0, len = errors.length; i < len; i++){
10202                 var fieldError = errors[i];
10203                 var f = this.findField(fieldError.id);
10204                 if(f){
10205                     f.markInvalid(fieldError.msg);
10206                 }
10207             }
10208         }else{
10209             var field, id;
10210             for(id in errors){
10211                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10212                     field.markInvalid(errors[id]);
10213                 }
10214             }
10215         }
10216         //Roo.each(this.childForms || [], function (f) {
10217         //    f.markInvalid(errors);
10218         //});
10219
10220         return this;
10221     },
10222
10223     /**
10224      * Set values for fields in this form in bulk.
10225      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10226      * @return {BasicForm} this
10227      */
10228     setValues : function(values){
10229         if(values instanceof Array){ // array of objects
10230             for(var i = 0, len = values.length; i < len; i++){
10231                 var v = values[i];
10232                 var f = this.findField(v.id);
10233                 if(f){
10234                     f.setValue(v.value);
10235                     if(this.trackResetOnLoad){
10236                         f.originalValue = f.getValue();
10237                     }
10238                 }
10239             }
10240         }else{ // object hash
10241             var field, id;
10242             for(id in values){
10243                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10244
10245                     if (field.setFromData &&
10246                         field.valueField &&
10247                         field.displayField &&
10248                         // combos' with local stores can
10249                         // be queried via setValue()
10250                         // to set their value..
10251                         (field.store && !field.store.isLocal)
10252                         ) {
10253                         // it's a combo
10254                         var sd = { };
10255                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10256                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10257                         field.setFromData(sd);
10258
10259                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10260                         
10261                         field.setFromData(values);
10262                         
10263                     } else {
10264                         field.setValue(values[id]);
10265                     }
10266
10267
10268                     if(this.trackResetOnLoad){
10269                         field.originalValue = field.getValue();
10270                     }
10271                 }
10272             }
10273         }
10274
10275         //Roo.each(this.childForms || [], function (f) {
10276         //    f.setValues(values);
10277         //});
10278
10279         return this;
10280     },
10281
10282     /**
10283      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10284      * they are returned as an array.
10285      * @param {Boolean} asString
10286      * @return {Object}
10287      */
10288     getValues : function(asString){
10289         //if (this.childForms) {
10290             // copy values from the child forms
10291         //    Roo.each(this.childForms, function (f) {
10292         //        this.setValues(f.getValues());
10293         //    }, this);
10294         //}
10295
10296
10297
10298         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10299         if(asString === true){
10300             return fs;
10301         }
10302         return Roo.urlDecode(fs);
10303     },
10304
10305     /**
10306      * Returns the fields in this form as an object with key/value pairs.
10307      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10308      * @return {Object}
10309      */
10310     getFieldValues : function(with_hidden)
10311     {
10312         var items = this.getItems();
10313         var ret = {};
10314         items.each(function(f){
10315             
10316             if (!f.getName()) {
10317                 return;
10318             }
10319             
10320             var v = f.getValue();
10321             
10322             if (f.inputType =='radio') {
10323                 if (typeof(ret[f.getName()]) == 'undefined') {
10324                     ret[f.getName()] = ''; // empty..
10325                 }
10326
10327                 if (!f.el.dom.checked) {
10328                     return;
10329
10330                 }
10331                 v = f.el.dom.value;
10332
10333             }
10334             
10335             if(f.xtype == 'MoneyField'){
10336                 ret[f.currencyName] = f.getCurrency();
10337             }
10338
10339             // not sure if this supported any more..
10340             if ((typeof(v) == 'object') && f.getRawValue) {
10341                 v = f.getRawValue() ; // dates..
10342             }
10343             // combo boxes where name != hiddenName...
10344             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10345                 ret[f.name] = f.getRawValue();
10346             }
10347             ret[f.getName()] = v;
10348         });
10349
10350         return ret;
10351     },
10352
10353     /**
10354      * Clears all invalid messages in this form.
10355      * @return {BasicForm} this
10356      */
10357     clearInvalid : function(){
10358         var items = this.getItems();
10359
10360         items.each(function(f){
10361            f.clearInvalid();
10362         });
10363
10364         return this;
10365     },
10366
10367     /**
10368      * Resets this form.
10369      * @return {BasicForm} this
10370      */
10371     reset : function(){
10372         var items = this.getItems();
10373         items.each(function(f){
10374             f.reset();
10375         });
10376
10377         Roo.each(this.childForms || [], function (f) {
10378             f.reset();
10379         });
10380
10381
10382         return this;
10383     },
10384     
10385     getItems : function()
10386     {
10387         var r=new Roo.util.MixedCollection(false, function(o){
10388             return o.id || (o.id = Roo.id());
10389         });
10390         var iter = function(el) {
10391             if (el.inputEl) {
10392                 r.add(el);
10393             }
10394             if (!el.items) {
10395                 return;
10396             }
10397             Roo.each(el.items,function(e) {
10398                 iter(e);
10399             });
10400         };
10401
10402         iter(this);
10403         return r;
10404     },
10405     
10406     hideFields : function(items)
10407     {
10408         Roo.each(items, function(i){
10409             
10410             var f = this.findField(i);
10411             
10412             if(!f){
10413                 return;
10414             }
10415             
10416             f.hide();
10417             
10418         }, this);
10419     },
10420     
10421     showFields : function(items)
10422     {
10423         Roo.each(items, function(i){
10424             
10425             var f = this.findField(i);
10426             
10427             if(!f){
10428                 return;
10429             }
10430             
10431             f.show();
10432             
10433         }, this);
10434     }
10435
10436 });
10437
10438 Roo.apply(Roo.bootstrap.Form, {
10439     
10440     popover : {
10441         
10442         padding : 5,
10443         
10444         isApplied : false,
10445         
10446         isMasked : false,
10447         
10448         form : false,
10449         
10450         target : false,
10451         
10452         toolTip : false,
10453         
10454         intervalID : false,
10455         
10456         maskEl : false,
10457         
10458         apply : function()
10459         {
10460             if(this.isApplied){
10461                 return;
10462             }
10463             
10464             this.maskEl = {
10465                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10466                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10467                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10468                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10469             };
10470             
10471             this.maskEl.top.enableDisplayMode("block");
10472             this.maskEl.left.enableDisplayMode("block");
10473             this.maskEl.bottom.enableDisplayMode("block");
10474             this.maskEl.right.enableDisplayMode("block");
10475             
10476             this.toolTip = new Roo.bootstrap.Tooltip({
10477                 cls : 'roo-form-error-popover',
10478                 alignment : {
10479                     'left' : ['r-l', [-2,0], 'right'],
10480                     'right' : ['l-r', [2,0], 'left'],
10481                     'bottom' : ['tl-bl', [0,2], 'top'],
10482                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10483                 }
10484             });
10485             
10486             this.toolTip.render(Roo.get(document.body));
10487
10488             this.toolTip.el.enableDisplayMode("block");
10489             
10490             Roo.get(document.body).on('click', function(){
10491                 this.unmask();
10492             }, this);
10493             
10494             Roo.get(document.body).on('touchstart', function(){
10495                 this.unmask();
10496             }, this);
10497             
10498             this.isApplied = true
10499         },
10500         
10501         mask : function(form, target)
10502         {
10503             this.form = form;
10504             
10505             this.target = target;
10506             
10507             if(!this.form.errorMask || !target.el){
10508                 return;
10509             }
10510             
10511             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10512             
10513             Roo.log(scrollable);
10514             
10515             var ot = this.target.el.calcOffsetsTo(scrollable);
10516             
10517             var scrollTo = ot[1] - this.form.maskOffset;
10518             
10519             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10520             
10521             scrollable.scrollTo('top', scrollTo);
10522             
10523             var box = this.target.el.getBox();
10524             Roo.log(box);
10525             var zIndex = Roo.bootstrap.Modal.zIndex++;
10526
10527             
10528             this.maskEl.top.setStyle('position', 'absolute');
10529             this.maskEl.top.setStyle('z-index', zIndex);
10530             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10531             this.maskEl.top.setLeft(0);
10532             this.maskEl.top.setTop(0);
10533             this.maskEl.top.show();
10534             
10535             this.maskEl.left.setStyle('position', 'absolute');
10536             this.maskEl.left.setStyle('z-index', zIndex);
10537             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10538             this.maskEl.left.setLeft(0);
10539             this.maskEl.left.setTop(box.y - this.padding);
10540             this.maskEl.left.show();
10541
10542             this.maskEl.bottom.setStyle('position', 'absolute');
10543             this.maskEl.bottom.setStyle('z-index', zIndex);
10544             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10545             this.maskEl.bottom.setLeft(0);
10546             this.maskEl.bottom.setTop(box.bottom + this.padding);
10547             this.maskEl.bottom.show();
10548
10549             this.maskEl.right.setStyle('position', 'absolute');
10550             this.maskEl.right.setStyle('z-index', zIndex);
10551             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10552             this.maskEl.right.setLeft(box.right + this.padding);
10553             this.maskEl.right.setTop(box.y - this.padding);
10554             this.maskEl.right.show();
10555
10556             this.toolTip.bindEl = this.target.el;
10557
10558             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10559
10560             var tip = this.target.blankText;
10561
10562             if(this.target.getValue() !== '' ) {
10563                 
10564                 if (this.target.invalidText.length) {
10565                     tip = this.target.invalidText;
10566                 } else if (this.target.regexText.length){
10567                     tip = this.target.regexText;
10568                 }
10569             }
10570
10571             this.toolTip.show(tip);
10572
10573             this.intervalID = window.setInterval(function() {
10574                 Roo.bootstrap.Form.popover.unmask();
10575             }, 10000);
10576
10577             window.onwheel = function(){ return false;};
10578             
10579             (function(){ this.isMasked = true; }).defer(500, this);
10580             
10581         },
10582         
10583         unmask : function()
10584         {
10585             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10586                 return;
10587             }
10588             
10589             this.maskEl.top.setStyle('position', 'absolute');
10590             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10591             this.maskEl.top.hide();
10592
10593             this.maskEl.left.setStyle('position', 'absolute');
10594             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10595             this.maskEl.left.hide();
10596
10597             this.maskEl.bottom.setStyle('position', 'absolute');
10598             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10599             this.maskEl.bottom.hide();
10600
10601             this.maskEl.right.setStyle('position', 'absolute');
10602             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10603             this.maskEl.right.hide();
10604             
10605             this.toolTip.hide();
10606             
10607             this.toolTip.el.hide();
10608             
10609             window.onwheel = function(){ return true;};
10610             
10611             if(this.intervalID){
10612                 window.clearInterval(this.intervalID);
10613                 this.intervalID = false;
10614             }
10615             
10616             this.isMasked = false;
10617             
10618         }
10619         
10620     }
10621     
10622 });
10623
10624 /*
10625  * Based on:
10626  * Ext JS Library 1.1.1
10627  * Copyright(c) 2006-2007, Ext JS, LLC.
10628  *
10629  * Originally Released Under LGPL - original licence link has changed is not relivant.
10630  *
10631  * Fork - LGPL
10632  * <script type="text/javascript">
10633  */
10634 /**
10635  * @class Roo.form.VTypes
10636  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10637  * @singleton
10638  */
10639 Roo.form.VTypes = function(){
10640     // closure these in so they are only created once.
10641     var alpha = /^[a-zA-Z_]+$/;
10642     var alphanum = /^[a-zA-Z0-9_]+$/;
10643     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10644     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10645
10646     // All these messages and functions are configurable
10647     return {
10648         /**
10649          * The function used to validate email addresses
10650          * @param {String} value The email address
10651          */
10652         'email' : function(v){
10653             return email.test(v);
10654         },
10655         /**
10656          * The error text to display when the email validation function returns false
10657          * @type String
10658          */
10659         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10660         /**
10661          * The keystroke filter mask to be applied on email input
10662          * @type RegExp
10663          */
10664         'emailMask' : /[a-z0-9_\.\-@]/i,
10665
10666         /**
10667          * The function used to validate URLs
10668          * @param {String} value The URL
10669          */
10670         'url' : function(v){
10671             return url.test(v);
10672         },
10673         /**
10674          * The error text to display when the url validation function returns false
10675          * @type String
10676          */
10677         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10678         
10679         /**
10680          * The function used to validate alpha values
10681          * @param {String} value The value
10682          */
10683         'alpha' : function(v){
10684             return alpha.test(v);
10685         },
10686         /**
10687          * The error text to display when the alpha validation function returns false
10688          * @type String
10689          */
10690         'alphaText' : 'This field should only contain letters and _',
10691         /**
10692          * The keystroke filter mask to be applied on alpha input
10693          * @type RegExp
10694          */
10695         'alphaMask' : /[a-z_]/i,
10696
10697         /**
10698          * The function used to validate alphanumeric values
10699          * @param {String} value The value
10700          */
10701         'alphanum' : function(v){
10702             return alphanum.test(v);
10703         },
10704         /**
10705          * The error text to display when the alphanumeric validation function returns false
10706          * @type String
10707          */
10708         'alphanumText' : 'This field should only contain letters, numbers and _',
10709         /**
10710          * The keystroke filter mask to be applied on alphanumeric input
10711          * @type RegExp
10712          */
10713         'alphanumMask' : /[a-z0-9_]/i
10714     };
10715 }();/*
10716  * - LGPL
10717  *
10718  * Input
10719  * 
10720  */
10721
10722 /**
10723  * @class Roo.bootstrap.Input
10724  * @extends Roo.bootstrap.Component
10725  * Bootstrap Input class
10726  * @cfg {Boolean} disabled is it disabled
10727  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10728  * @cfg {String} name name of the input
10729  * @cfg {string} fieldLabel - the label associated
10730  * @cfg {string} placeholder - placeholder to put in text.
10731  * @cfg {string}  before - input group add on before
10732  * @cfg {string} after - input group add on after
10733  * @cfg {string} size - (lg|sm) or leave empty..
10734  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10735  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10736  * @cfg {Number} md colspan out of 12 for computer-sized screens
10737  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10738  * @cfg {string} value default value of the input
10739  * @cfg {Number} labelWidth set the width of label 
10740  * @cfg {Number} labellg set the width of label (1-12)
10741  * @cfg {Number} labelmd set the width of label (1-12)
10742  * @cfg {Number} labelsm set the width of label (1-12)
10743  * @cfg {Number} labelxs set the width of label (1-12)
10744  * @cfg {String} labelAlign (top|left)
10745  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10746  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10747  * @cfg {String} indicatorpos (left|right) default left
10748  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10749  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10750  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10751
10752  * @cfg {String} align (left|center|right) Default left
10753  * @cfg {Boolean} forceFeedback (true|false) Default false
10754  * 
10755  * @constructor
10756  * Create a new Input
10757  * @param {Object} config The config object
10758  */
10759
10760 Roo.bootstrap.Input = function(config){
10761     
10762     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10763     
10764     this.addEvents({
10765         /**
10766          * @event focus
10767          * Fires when this field receives input focus.
10768          * @param {Roo.form.Field} this
10769          */
10770         focus : true,
10771         /**
10772          * @event blur
10773          * Fires when this field loses input focus.
10774          * @param {Roo.form.Field} this
10775          */
10776         blur : true,
10777         /**
10778          * @event specialkey
10779          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10780          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10781          * @param {Roo.form.Field} this
10782          * @param {Roo.EventObject} e The event object
10783          */
10784         specialkey : true,
10785         /**
10786          * @event change
10787          * Fires just before the field blurs if the field value has changed.
10788          * @param {Roo.form.Field} this
10789          * @param {Mixed} newValue The new value
10790          * @param {Mixed} oldValue The original value
10791          */
10792         change : true,
10793         /**
10794          * @event invalid
10795          * Fires after the field has been marked as invalid.
10796          * @param {Roo.form.Field} this
10797          * @param {String} msg The validation message
10798          */
10799         invalid : true,
10800         /**
10801          * @event valid
10802          * Fires after the field has been validated with no errors.
10803          * @param {Roo.form.Field} this
10804          */
10805         valid : true,
10806          /**
10807          * @event keyup
10808          * Fires after the key up
10809          * @param {Roo.form.Field} this
10810          * @param {Roo.EventObject}  e The event Object
10811          */
10812         keyup : true,
10813         /**
10814          * @event paste
10815          * Fires after the user pastes into input
10816          * @param {Roo.form.Field} this
10817          * @param {Roo.EventObject}  e The event Object
10818          */
10819         paste : true
10820     });
10821 };
10822
10823 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10824      /**
10825      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10826       automatic validation (defaults to "keyup").
10827      */
10828     validationEvent : "keyup",
10829      /**
10830      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10831      */
10832     validateOnBlur : true,
10833     /**
10834      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10835      */
10836     validationDelay : 250,
10837      /**
10838      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10839      */
10840     focusClass : "x-form-focus",  // not needed???
10841     
10842        
10843     /**
10844      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10845      */
10846     invalidClass : "has-warning",
10847     
10848     /**
10849      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10850      */
10851     validClass : "has-success",
10852     
10853     /**
10854      * @cfg {Boolean} hasFeedback (true|false) default true
10855      */
10856     hasFeedback : true,
10857     
10858     /**
10859      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10860      */
10861     invalidFeedbackClass : "glyphicon-warning-sign",
10862     
10863     /**
10864      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10865      */
10866     validFeedbackClass : "glyphicon-ok",
10867     
10868     /**
10869      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10870      */
10871     selectOnFocus : false,
10872     
10873      /**
10874      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10875      */
10876     maskRe : null,
10877        /**
10878      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10879      */
10880     vtype : null,
10881     
10882       /**
10883      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10884      */
10885     disableKeyFilter : false,
10886     
10887        /**
10888      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10889      */
10890     disabled : false,
10891      /**
10892      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10893      */
10894     allowBlank : true,
10895     /**
10896      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10897      */
10898     blankText : "Please complete this mandatory field",
10899     
10900      /**
10901      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10902      */
10903     minLength : 0,
10904     /**
10905      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10906      */
10907     maxLength : Number.MAX_VALUE,
10908     /**
10909      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10910      */
10911     minLengthText : "The minimum length for this field is {0}",
10912     /**
10913      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10914      */
10915     maxLengthText : "The maximum length for this field is {0}",
10916   
10917     
10918     /**
10919      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10920      * If available, this function will be called only after the basic validators all return true, and will be passed the
10921      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10922      */
10923     validator : null,
10924     /**
10925      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10926      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10927      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10928      */
10929     regex : null,
10930     /**
10931      * @cfg {String} regexText -- Depricated - use Invalid Text
10932      */
10933     regexText : "",
10934     
10935     /**
10936      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10937      */
10938     invalidText : "",
10939     
10940     
10941     
10942     autocomplete: false,
10943     
10944     
10945     fieldLabel : '',
10946     inputType : 'text',
10947     
10948     name : false,
10949     placeholder: false,
10950     before : false,
10951     after : false,
10952     size : false,
10953     hasFocus : false,
10954     preventMark: false,
10955     isFormField : true,
10956     value : '',
10957     labelWidth : 2,
10958     labelAlign : false,
10959     readOnly : false,
10960     align : false,
10961     formatedValue : false,
10962     forceFeedback : false,
10963     
10964     indicatorpos : 'left',
10965     
10966     labellg : 0,
10967     labelmd : 0,
10968     labelsm : 0,
10969     labelxs : 0,
10970     
10971     capture : '',
10972     accept : '',
10973     
10974     parentLabelAlign : function()
10975     {
10976         var parent = this;
10977         while (parent.parent()) {
10978             parent = parent.parent();
10979             if (typeof(parent.labelAlign) !='undefined') {
10980                 return parent.labelAlign;
10981             }
10982         }
10983         return 'left';
10984         
10985     },
10986     
10987     getAutoCreate : function()
10988     {
10989         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10990         
10991         var id = Roo.id();
10992         
10993         var cfg = {};
10994         
10995         if(this.inputType != 'hidden'){
10996             cfg.cls = 'form-group' //input-group
10997         }
10998         
10999         var input =  {
11000             tag: 'input',
11001             id : id,
11002             type : this.inputType,
11003             value : this.value,
11004             cls : 'form-control',
11005             placeholder : this.placeholder || '',
11006             autocomplete : this.autocomplete || 'new-password'
11007         };
11008         if (this.inputType == 'file') {
11009             input.style = 'overflow:hidden'; // why not in CSS?
11010         }
11011         
11012         if(this.capture.length){
11013             input.capture = this.capture;
11014         }
11015         
11016         if(this.accept.length){
11017             input.accept = this.accept + "/*";
11018         }
11019         
11020         if(this.align){
11021             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11022         }
11023         
11024         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11025             input.maxLength = this.maxLength;
11026         }
11027         
11028         if (this.disabled) {
11029             input.disabled=true;
11030         }
11031         
11032         if (this.readOnly) {
11033             input.readonly=true;
11034         }
11035         
11036         if (this.name) {
11037             input.name = this.name;
11038         }
11039         
11040         if (this.size) {
11041             input.cls += ' input-' + this.size;
11042         }
11043         
11044         var settings=this;
11045         ['xs','sm','md','lg'].map(function(size){
11046             if (settings[size]) {
11047                 cfg.cls += ' col-' + size + '-' + settings[size];
11048             }
11049         });
11050         
11051         var inputblock = input;
11052         
11053         var feedback = {
11054             tag: 'span',
11055             cls: 'glyphicon form-control-feedback'
11056         };
11057             
11058         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11059             
11060             inputblock = {
11061                 cls : 'has-feedback',
11062                 cn :  [
11063                     input,
11064                     feedback
11065                 ] 
11066             };  
11067         }
11068         
11069         if (this.before || this.after) {
11070             
11071             inputblock = {
11072                 cls : 'input-group',
11073                 cn :  [] 
11074             };
11075             
11076             if (this.before && typeof(this.before) == 'string') {
11077                 
11078                 inputblock.cn.push({
11079                     tag :'span',
11080                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11081                     html : this.before
11082                 });
11083             }
11084             if (this.before && typeof(this.before) == 'object') {
11085                 this.before = Roo.factory(this.before);
11086                 
11087                 inputblock.cn.push({
11088                     tag :'span',
11089                     cls : 'roo-input-before input-group-prepend   input-group-' +
11090                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11091                 });
11092             }
11093             
11094             inputblock.cn.push(input);
11095             
11096             if (this.after && typeof(this.after) == 'string') {
11097                 inputblock.cn.push({
11098                     tag :'span',
11099                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11100                     html : this.after
11101                 });
11102             }
11103             if (this.after && typeof(this.after) == 'object') {
11104                 this.after = Roo.factory(this.after);
11105                 
11106                 inputblock.cn.push({
11107                     tag :'span',
11108                     cls : 'roo-input-after input-group-append  input-group-' +
11109                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11110                 });
11111             }
11112             
11113             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11114                 inputblock.cls += ' has-feedback';
11115                 inputblock.cn.push(feedback);
11116             }
11117         };
11118         var indicator = {
11119             tag : 'i',
11120             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11121             tooltip : 'This field is required'
11122         };
11123         if (this.allowBlank ) {
11124             indicator.style = this.allowBlank ? ' display:none' : '';
11125         }
11126         if (align ==='left' && this.fieldLabel.length) {
11127             
11128             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11129             
11130             cfg.cn = [
11131                 indicator,
11132                 {
11133                     tag: 'label',
11134                     'for' :  id,
11135                     cls : 'control-label col-form-label',
11136                     html : this.fieldLabel
11137
11138                 },
11139                 {
11140                     cls : "", 
11141                     cn: [
11142                         inputblock
11143                     ]
11144                 }
11145             ];
11146             
11147             var labelCfg = cfg.cn[1];
11148             var contentCfg = cfg.cn[2];
11149             
11150             if(this.indicatorpos == 'right'){
11151                 cfg.cn = [
11152                     {
11153                         tag: 'label',
11154                         'for' :  id,
11155                         cls : 'control-label col-form-label',
11156                         cn : [
11157                             {
11158                                 tag : 'span',
11159                                 html : this.fieldLabel
11160                             },
11161                             indicator
11162                         ]
11163                     },
11164                     {
11165                         cls : "",
11166                         cn: [
11167                             inputblock
11168                         ]
11169                     }
11170
11171                 ];
11172                 
11173                 labelCfg = cfg.cn[0];
11174                 contentCfg = cfg.cn[1];
11175             
11176             }
11177             
11178             if(this.labelWidth > 12){
11179                 labelCfg.style = "width: " + this.labelWidth + 'px';
11180             }
11181             
11182             if(this.labelWidth < 13 && this.labelmd == 0){
11183                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11184             }
11185             
11186             if(this.labellg > 0){
11187                 labelCfg.cls += ' col-lg-' + this.labellg;
11188                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11189             }
11190             
11191             if(this.labelmd > 0){
11192                 labelCfg.cls += ' col-md-' + this.labelmd;
11193                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11194             }
11195             
11196             if(this.labelsm > 0){
11197                 labelCfg.cls += ' col-sm-' + this.labelsm;
11198                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11199             }
11200             
11201             if(this.labelxs > 0){
11202                 labelCfg.cls += ' col-xs-' + this.labelxs;
11203                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11204             }
11205             
11206             
11207         } else if ( this.fieldLabel.length) {
11208                 
11209             
11210             
11211             cfg.cn = [
11212                 {
11213                     tag : 'i',
11214                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11215                     tooltip : 'This field is required',
11216                     style : this.allowBlank ? ' display:none' : '' 
11217                 },
11218                 {
11219                     tag: 'label',
11220                    //cls : 'input-group-addon',
11221                     html : this.fieldLabel
11222
11223                 },
11224
11225                inputblock
11226
11227            ];
11228            
11229            if(this.indicatorpos == 'right'){
11230        
11231                 cfg.cn = [
11232                     {
11233                         tag: 'label',
11234                        //cls : 'input-group-addon',
11235                         html : this.fieldLabel
11236
11237                     },
11238                     {
11239                         tag : 'i',
11240                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11241                         tooltip : 'This field is required',
11242                         style : this.allowBlank ? ' display:none' : '' 
11243                     },
11244
11245                    inputblock
11246
11247                ];
11248
11249             }
11250
11251         } else {
11252             
11253             cfg.cn = [
11254
11255                     inputblock
11256
11257             ];
11258                 
11259                 
11260         };
11261         
11262         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11263            cfg.cls += ' navbar-form';
11264         }
11265         
11266         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11267             // on BS4 we do this only if not form 
11268             cfg.cls += ' navbar-form';
11269             cfg.tag = 'li';
11270         }
11271         
11272         return cfg;
11273         
11274     },
11275     /**
11276      * return the real input element.
11277      */
11278     inputEl: function ()
11279     {
11280         return this.el.select('input.form-control',true).first();
11281     },
11282     
11283     tooltipEl : function()
11284     {
11285         return this.inputEl();
11286     },
11287     
11288     indicatorEl : function()
11289     {
11290         if (Roo.bootstrap.version == 4) {
11291             return false; // not enabled in v4 yet.
11292         }
11293         
11294         var indicator = this.el.select('i.roo-required-indicator',true).first();
11295         
11296         if(!indicator){
11297             return false;
11298         }
11299         
11300         return indicator;
11301         
11302     },
11303     
11304     setDisabled : function(v)
11305     {
11306         var i  = this.inputEl().dom;
11307         if (!v) {
11308             i.removeAttribute('disabled');
11309             return;
11310             
11311         }
11312         i.setAttribute('disabled','true');
11313     },
11314     initEvents : function()
11315     {
11316           
11317         this.inputEl().on("keydown" , this.fireKey,  this);
11318         this.inputEl().on("focus", this.onFocus,  this);
11319         this.inputEl().on("blur", this.onBlur,  this);
11320         
11321         this.inputEl().relayEvent('keyup', this);
11322         this.inputEl().relayEvent('paste', this);
11323         
11324         this.indicator = this.indicatorEl();
11325         
11326         if(this.indicator){
11327             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11328         }
11329  
11330         // reference to original value for reset
11331         this.originalValue = this.getValue();
11332         //Roo.form.TextField.superclass.initEvents.call(this);
11333         if(this.validationEvent == 'keyup'){
11334             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11335             this.inputEl().on('keyup', this.filterValidation, this);
11336         }
11337         else if(this.validationEvent !== false){
11338             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11339         }
11340         
11341         if(this.selectOnFocus){
11342             this.on("focus", this.preFocus, this);
11343             
11344         }
11345         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11346             this.inputEl().on("keypress", this.filterKeys, this);
11347         } else {
11348             this.inputEl().relayEvent('keypress', this);
11349         }
11350        /* if(this.grow){
11351             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11352             this.el.on("click", this.autoSize,  this);
11353         }
11354         */
11355         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11356             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11357         }
11358         
11359         if (typeof(this.before) == 'object') {
11360             this.before.render(this.el.select('.roo-input-before',true).first());
11361         }
11362         if (typeof(this.after) == 'object') {
11363             this.after.render(this.el.select('.roo-input-after',true).first());
11364         }
11365         
11366         this.inputEl().on('change', this.onChange, this);
11367         
11368     },
11369     filterValidation : function(e){
11370         if(!e.isNavKeyPress()){
11371             this.validationTask.delay(this.validationDelay);
11372         }
11373     },
11374      /**
11375      * Validates the field value
11376      * @return {Boolean} True if the value is valid, else false
11377      */
11378     validate : function(){
11379         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11380         if(this.disabled || this.validateValue(this.getRawValue())){
11381             this.markValid();
11382             return true;
11383         }
11384         
11385         this.markInvalid();
11386         return false;
11387     },
11388     
11389     
11390     /**
11391      * Validates a value according to the field's validation rules and marks the field as invalid
11392      * if the validation fails
11393      * @param {Mixed} value The value to validate
11394      * @return {Boolean} True if the value is valid, else false
11395      */
11396     validateValue : function(value)
11397     {
11398         if(this.getVisibilityEl().hasClass('hidden')){
11399             return true;
11400         }
11401         
11402         if(value.length < 1)  { // if it's blank
11403             if(this.allowBlank){
11404                 return true;
11405             }
11406             return false;
11407         }
11408         
11409         if(value.length < this.minLength){
11410             return false;
11411         }
11412         if(value.length > this.maxLength){
11413             return false;
11414         }
11415         if(this.vtype){
11416             var vt = Roo.form.VTypes;
11417             if(!vt[this.vtype](value, this)){
11418                 return false;
11419             }
11420         }
11421         if(typeof this.validator == "function"){
11422             var msg = this.validator(value);
11423             if(msg !== true){
11424                 return false;
11425             }
11426             if (typeof(msg) == 'string') {
11427                 this.invalidText = msg;
11428             }
11429         }
11430         
11431         if(this.regex && !this.regex.test(value)){
11432             return false;
11433         }
11434         
11435         return true;
11436     },
11437     
11438      // private
11439     fireKey : function(e){
11440         //Roo.log('field ' + e.getKey());
11441         if(e.isNavKeyPress()){
11442             this.fireEvent("specialkey", this, e);
11443         }
11444     },
11445     focus : function (selectText){
11446         if(this.rendered){
11447             this.inputEl().focus();
11448             if(selectText === true){
11449                 this.inputEl().dom.select();
11450             }
11451         }
11452         return this;
11453     } ,
11454     
11455     onFocus : function(){
11456         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11457            // this.el.addClass(this.focusClass);
11458         }
11459         if(!this.hasFocus){
11460             this.hasFocus = true;
11461             this.startValue = this.getValue();
11462             this.fireEvent("focus", this);
11463         }
11464     },
11465     
11466     beforeBlur : Roo.emptyFn,
11467
11468     
11469     // private
11470     onBlur : function(){
11471         this.beforeBlur();
11472         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11473             //this.el.removeClass(this.focusClass);
11474         }
11475         this.hasFocus = false;
11476         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11477             this.validate();
11478         }
11479         var v = this.getValue();
11480         if(String(v) !== String(this.startValue)){
11481             this.fireEvent('change', this, v, this.startValue);
11482         }
11483         this.fireEvent("blur", this);
11484     },
11485     
11486     onChange : function(e)
11487     {
11488         var v = this.getValue();
11489         if(String(v) !== String(this.startValue)){
11490             this.fireEvent('change', this, v, this.startValue);
11491         }
11492         
11493     },
11494     
11495     /**
11496      * Resets the current field value to the originally loaded value and clears any validation messages
11497      */
11498     reset : function(){
11499         this.setValue(this.originalValue);
11500         this.validate();
11501     },
11502      /**
11503      * Returns the name of the field
11504      * @return {Mixed} name The name field
11505      */
11506     getName: function(){
11507         return this.name;
11508     },
11509      /**
11510      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11511      * @return {Mixed} value The field value
11512      */
11513     getValue : function(){
11514         
11515         var v = this.inputEl().getValue();
11516         
11517         return v;
11518     },
11519     /**
11520      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11521      * @return {Mixed} value The field value
11522      */
11523     getRawValue : function(){
11524         var v = this.inputEl().getValue();
11525         
11526         return v;
11527     },
11528     
11529     /**
11530      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11531      * @param {Mixed} value The value to set
11532      */
11533     setRawValue : function(v){
11534         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11535     },
11536     
11537     selectText : function(start, end){
11538         var v = this.getRawValue();
11539         if(v.length > 0){
11540             start = start === undefined ? 0 : start;
11541             end = end === undefined ? v.length : end;
11542             var d = this.inputEl().dom;
11543             if(d.setSelectionRange){
11544                 d.setSelectionRange(start, end);
11545             }else if(d.createTextRange){
11546                 var range = d.createTextRange();
11547                 range.moveStart("character", start);
11548                 range.moveEnd("character", v.length-end);
11549                 range.select();
11550             }
11551         }
11552     },
11553     
11554     /**
11555      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11556      * @param {Mixed} value The value to set
11557      */
11558     setValue : function(v){
11559         this.value = v;
11560         if(this.rendered){
11561             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11562             this.validate();
11563         }
11564     },
11565     
11566     /*
11567     processValue : function(value){
11568         if(this.stripCharsRe){
11569             var newValue = value.replace(this.stripCharsRe, '');
11570             if(newValue !== value){
11571                 this.setRawValue(newValue);
11572                 return newValue;
11573             }
11574         }
11575         return value;
11576     },
11577   */
11578     preFocus : function(){
11579         
11580         if(this.selectOnFocus){
11581             this.inputEl().dom.select();
11582         }
11583     },
11584     filterKeys : function(e){
11585         var k = e.getKey();
11586         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11587             return;
11588         }
11589         var c = e.getCharCode(), cc = String.fromCharCode(c);
11590         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11591             return;
11592         }
11593         if(!this.maskRe.test(cc)){
11594             e.stopEvent();
11595         }
11596     },
11597      /**
11598      * Clear any invalid styles/messages for this field
11599      */
11600     clearInvalid : function(){
11601         
11602         if(!this.el || this.preventMark){ // not rendered
11603             return;
11604         }
11605         
11606         
11607         this.el.removeClass([this.invalidClass, 'is-invalid']);
11608         
11609         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11610             
11611             var feedback = this.el.select('.form-control-feedback', true).first();
11612             
11613             if(feedback){
11614                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11615             }
11616             
11617         }
11618         
11619         if(this.indicator){
11620             this.indicator.removeClass('visible');
11621             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11622         }
11623         
11624         this.fireEvent('valid', this);
11625     },
11626     
11627      /**
11628      * Mark this field as valid
11629      */
11630     markValid : function()
11631     {
11632         if(!this.el  || this.preventMark){ // not rendered...
11633             return;
11634         }
11635         
11636         this.el.removeClass([this.invalidClass, this.validClass]);
11637         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11638
11639         var feedback = this.el.select('.form-control-feedback', true).first();
11640             
11641         if(feedback){
11642             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11643         }
11644         
11645         if(this.indicator){
11646             this.indicator.removeClass('visible');
11647             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11648         }
11649         
11650         if(this.disabled){
11651             return;
11652         }
11653         
11654            
11655         if(this.allowBlank && !this.getRawValue().length){
11656             return;
11657         }
11658         if (Roo.bootstrap.version == 3) {
11659             this.el.addClass(this.validClass);
11660         } else {
11661             this.inputEl().addClass('is-valid');
11662         }
11663
11664         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11665             
11666             var feedback = this.el.select('.form-control-feedback', true).first();
11667             
11668             if(feedback){
11669                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11670                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11671             }
11672             
11673         }
11674         
11675         this.fireEvent('valid', this);
11676     },
11677     
11678      /**
11679      * Mark this field as invalid
11680      * @param {String} msg The validation message
11681      */
11682     markInvalid : function(msg)
11683     {
11684         if(!this.el  || this.preventMark){ // not rendered
11685             return;
11686         }
11687         
11688         this.el.removeClass([this.invalidClass, this.validClass]);
11689         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11690         
11691         var feedback = this.el.select('.form-control-feedback', true).first();
11692             
11693         if(feedback){
11694             this.el.select('.form-control-feedback', true).first().removeClass(
11695                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11696         }
11697
11698         if(this.disabled){
11699             return;
11700         }
11701         
11702         if(this.allowBlank && !this.getRawValue().length){
11703             return;
11704         }
11705         
11706         if(this.indicator){
11707             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11708             this.indicator.addClass('visible');
11709         }
11710         if (Roo.bootstrap.version == 3) {
11711             this.el.addClass(this.invalidClass);
11712         } else {
11713             this.inputEl().addClass('is-invalid');
11714         }
11715         
11716         
11717         
11718         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11719             
11720             var feedback = this.el.select('.form-control-feedback', true).first();
11721             
11722             if(feedback){
11723                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11724                 
11725                 if(this.getValue().length || this.forceFeedback){
11726                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11727                 }
11728                 
11729             }
11730             
11731         }
11732         
11733         this.fireEvent('invalid', this, msg);
11734     },
11735     // private
11736     SafariOnKeyDown : function(event)
11737     {
11738         // this is a workaround for a password hang bug on chrome/ webkit.
11739         if (this.inputEl().dom.type != 'password') {
11740             return;
11741         }
11742         
11743         var isSelectAll = false;
11744         
11745         if(this.inputEl().dom.selectionEnd > 0){
11746             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11747         }
11748         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11749             event.preventDefault();
11750             this.setValue('');
11751             return;
11752         }
11753         
11754         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11755             
11756             event.preventDefault();
11757             // this is very hacky as keydown always get's upper case.
11758             //
11759             var cc = String.fromCharCode(event.getCharCode());
11760             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11761             
11762         }
11763     },
11764     adjustWidth : function(tag, w){
11765         tag = tag.toLowerCase();
11766         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11767             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11768                 if(tag == 'input'){
11769                     return w + 2;
11770                 }
11771                 if(tag == 'textarea'){
11772                     return w-2;
11773                 }
11774             }else if(Roo.isOpera){
11775                 if(tag == 'input'){
11776                     return w + 2;
11777                 }
11778                 if(tag == 'textarea'){
11779                     return w-2;
11780                 }
11781             }
11782         }
11783         return w;
11784     },
11785     
11786     setFieldLabel : function(v)
11787     {
11788         if(!this.rendered){
11789             return;
11790         }
11791         
11792         if(this.indicatorEl()){
11793             var ar = this.el.select('label > span',true);
11794             
11795             if (ar.elements.length) {
11796                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11797                 this.fieldLabel = v;
11798                 return;
11799             }
11800             
11801             var br = this.el.select('label',true);
11802             
11803             if(br.elements.length) {
11804                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11805                 this.fieldLabel = v;
11806                 return;
11807             }
11808             
11809             Roo.log('Cannot Found any of label > span || label in input');
11810             return;
11811         }
11812         
11813         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11814         this.fieldLabel = v;
11815         
11816         
11817     }
11818 });
11819
11820  
11821 /*
11822  * - LGPL
11823  *
11824  * Input
11825  * 
11826  */
11827
11828 /**
11829  * @class Roo.bootstrap.TextArea
11830  * @extends Roo.bootstrap.Input
11831  * Bootstrap TextArea class
11832  * @cfg {Number} cols Specifies the visible width of a text area
11833  * @cfg {Number} rows Specifies the visible number of lines in a text area
11834  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11835  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11836  * @cfg {string} html text
11837  * 
11838  * @constructor
11839  * Create a new TextArea
11840  * @param {Object} config The config object
11841  */
11842
11843 Roo.bootstrap.TextArea = function(config){
11844     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11845    
11846 };
11847
11848 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11849      
11850     cols : false,
11851     rows : 5,
11852     readOnly : false,
11853     warp : 'soft',
11854     resize : false,
11855     value: false,
11856     html: false,
11857     
11858     getAutoCreate : function(){
11859         
11860         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11861         
11862         var id = Roo.id();
11863         
11864         var cfg = {};
11865         
11866         if(this.inputType != 'hidden'){
11867             cfg.cls = 'form-group' //input-group
11868         }
11869         
11870         var input =  {
11871             tag: 'textarea',
11872             id : id,
11873             warp : this.warp,
11874             rows : this.rows,
11875             value : this.value || '',
11876             html: this.html || '',
11877             cls : 'form-control',
11878             placeholder : this.placeholder || '' 
11879             
11880         };
11881         
11882         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11883             input.maxLength = this.maxLength;
11884         }
11885         
11886         if(this.resize){
11887             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11888         }
11889         
11890         if(this.cols){
11891             input.cols = this.cols;
11892         }
11893         
11894         if (this.readOnly) {
11895             input.readonly = true;
11896         }
11897         
11898         if (this.name) {
11899             input.name = this.name;
11900         }
11901         
11902         if (this.size) {
11903             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11904         }
11905         
11906         var settings=this;
11907         ['xs','sm','md','lg'].map(function(size){
11908             if (settings[size]) {
11909                 cfg.cls += ' col-' + size + '-' + settings[size];
11910             }
11911         });
11912         
11913         var inputblock = input;
11914         
11915         if(this.hasFeedback && !this.allowBlank){
11916             
11917             var feedback = {
11918                 tag: 'span',
11919                 cls: 'glyphicon form-control-feedback'
11920             };
11921
11922             inputblock = {
11923                 cls : 'has-feedback',
11924                 cn :  [
11925                     input,
11926                     feedback
11927                 ] 
11928             };  
11929         }
11930         
11931         
11932         if (this.before || this.after) {
11933             
11934             inputblock = {
11935                 cls : 'input-group',
11936                 cn :  [] 
11937             };
11938             if (this.before) {
11939                 inputblock.cn.push({
11940                     tag :'span',
11941                     cls : 'input-group-addon',
11942                     html : this.before
11943                 });
11944             }
11945             
11946             inputblock.cn.push(input);
11947             
11948             if(this.hasFeedback && !this.allowBlank){
11949                 inputblock.cls += ' has-feedback';
11950                 inputblock.cn.push(feedback);
11951             }
11952             
11953             if (this.after) {
11954                 inputblock.cn.push({
11955                     tag :'span',
11956                     cls : 'input-group-addon',
11957                     html : this.after
11958                 });
11959             }
11960             
11961         }
11962         
11963         if (align ==='left' && this.fieldLabel.length) {
11964             cfg.cn = [
11965                 {
11966                     tag: 'label',
11967                     'for' :  id,
11968                     cls : 'control-label',
11969                     html : this.fieldLabel
11970                 },
11971                 {
11972                     cls : "",
11973                     cn: [
11974                         inputblock
11975                     ]
11976                 }
11977
11978             ];
11979             
11980             if(this.labelWidth > 12){
11981                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11982             }
11983
11984             if(this.labelWidth < 13 && this.labelmd == 0){
11985                 this.labelmd = this.labelWidth;
11986             }
11987
11988             if(this.labellg > 0){
11989                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11990                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11991             }
11992
11993             if(this.labelmd > 0){
11994                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11995                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11996             }
11997
11998             if(this.labelsm > 0){
11999                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12000                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12001             }
12002
12003             if(this.labelxs > 0){
12004                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12005                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12006             }
12007             
12008         } else if ( this.fieldLabel.length) {
12009             cfg.cn = [
12010
12011                {
12012                    tag: 'label',
12013                    //cls : 'input-group-addon',
12014                    html : this.fieldLabel
12015
12016                },
12017
12018                inputblock
12019
12020            ];
12021
12022         } else {
12023
12024             cfg.cn = [
12025
12026                 inputblock
12027
12028             ];
12029                 
12030         }
12031         
12032         if (this.disabled) {
12033             input.disabled=true;
12034         }
12035         
12036         return cfg;
12037         
12038     },
12039     /**
12040      * return the real textarea element.
12041      */
12042     inputEl: function ()
12043     {
12044         return this.el.select('textarea.form-control',true).first();
12045     },
12046     
12047     /**
12048      * Clear any invalid styles/messages for this field
12049      */
12050     clearInvalid : function()
12051     {
12052         
12053         if(!this.el || this.preventMark){ // not rendered
12054             return;
12055         }
12056         
12057         var label = this.el.select('label', true).first();
12058         var icon = this.el.select('i.fa-star', true).first();
12059         
12060         if(label && icon){
12061             icon.remove();
12062         }
12063         this.el.removeClass( this.validClass);
12064         this.inputEl().removeClass('is-invalid');
12065          
12066         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12067             
12068             var feedback = this.el.select('.form-control-feedback', true).first();
12069             
12070             if(feedback){
12071                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12072             }
12073             
12074         }
12075         
12076         this.fireEvent('valid', this);
12077     },
12078     
12079      /**
12080      * Mark this field as valid
12081      */
12082     markValid : function()
12083     {
12084         if(!this.el  || this.preventMark){ // not rendered
12085             return;
12086         }
12087         
12088         this.el.removeClass([this.invalidClass, this.validClass]);
12089         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12090         
12091         var feedback = this.el.select('.form-control-feedback', true).first();
12092             
12093         if(feedback){
12094             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12095         }
12096
12097         if(this.disabled || this.allowBlank){
12098             return;
12099         }
12100         
12101         var label = this.el.select('label', true).first();
12102         var icon = this.el.select('i.fa-star', true).first();
12103         
12104         if(label && icon){
12105             icon.remove();
12106         }
12107         if (Roo.bootstrap.version == 3) {
12108             this.el.addClass(this.validClass);
12109         } else {
12110             this.inputEl().addClass('is-valid');
12111         }
12112         
12113         
12114         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12115             
12116             var feedback = this.el.select('.form-control-feedback', true).first();
12117             
12118             if(feedback){
12119                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12120                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12121             }
12122             
12123         }
12124         
12125         this.fireEvent('valid', this);
12126     },
12127     
12128      /**
12129      * Mark this field as invalid
12130      * @param {String} msg The validation message
12131      */
12132     markInvalid : function(msg)
12133     {
12134         if(!this.el  || this.preventMark){ // not rendered
12135             return;
12136         }
12137         
12138         this.el.removeClass([this.invalidClass, this.validClass]);
12139         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12140         
12141         var feedback = this.el.select('.form-control-feedback', true).first();
12142             
12143         if(feedback){
12144             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12145         }
12146
12147         if(this.disabled || this.allowBlank){
12148             return;
12149         }
12150         
12151         var label = this.el.select('label', true).first();
12152         var icon = this.el.select('i.fa-star', true).first();
12153         
12154         if(!this.getValue().length && label && !icon){
12155             this.el.createChild({
12156                 tag : 'i',
12157                 cls : 'text-danger fa fa-lg fa-star',
12158                 tooltip : 'This field is required',
12159                 style : 'margin-right:5px;'
12160             }, label, true);
12161         }
12162         
12163         if (Roo.bootstrap.version == 3) {
12164             this.el.addClass(this.invalidClass);
12165         } else {
12166             this.inputEl().addClass('is-invalid');
12167         }
12168         
12169         // fixme ... this may be depricated need to test..
12170         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12171             
12172             var feedback = this.el.select('.form-control-feedback', true).first();
12173             
12174             if(feedback){
12175                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12176                 
12177                 if(this.getValue().length || this.forceFeedback){
12178                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12179                 }
12180                 
12181             }
12182             
12183         }
12184         
12185         this.fireEvent('invalid', this, msg);
12186     }
12187 });
12188
12189  
12190 /*
12191  * - LGPL
12192  *
12193  * trigger field - base class for combo..
12194  * 
12195  */
12196  
12197 /**
12198  * @class Roo.bootstrap.TriggerField
12199  * @extends Roo.bootstrap.Input
12200  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12201  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12202  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12203  * for which you can provide a custom implementation.  For example:
12204  * <pre><code>
12205 var trigger = new Roo.bootstrap.TriggerField();
12206 trigger.onTriggerClick = myTriggerFn;
12207 trigger.applyTo('my-field');
12208 </code></pre>
12209  *
12210  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12211  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12212  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12213  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12214  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12215
12216  * @constructor
12217  * Create a new TriggerField.
12218  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12219  * to the base TextField)
12220  */
12221 Roo.bootstrap.TriggerField = function(config){
12222     this.mimicing = false;
12223     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12224 };
12225
12226 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12227     /**
12228      * @cfg {String} triggerClass A CSS class to apply to the trigger
12229      */
12230      /**
12231      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12232      */
12233     hideTrigger:false,
12234
12235     /**
12236      * @cfg {Boolean} removable (true|false) special filter default false
12237      */
12238     removable : false,
12239     
12240     /** @cfg {Boolean} grow @hide */
12241     /** @cfg {Number} growMin @hide */
12242     /** @cfg {Number} growMax @hide */
12243
12244     /**
12245      * @hide 
12246      * @method
12247      */
12248     autoSize: Roo.emptyFn,
12249     // private
12250     monitorTab : true,
12251     // private
12252     deferHeight : true,
12253
12254     
12255     actionMode : 'wrap',
12256     
12257     caret : false,
12258     
12259     
12260     getAutoCreate : function(){
12261        
12262         var align = this.labelAlign || this.parentLabelAlign();
12263         
12264         var id = Roo.id();
12265         
12266         var cfg = {
12267             cls: 'form-group' //input-group
12268         };
12269         
12270         
12271         var input =  {
12272             tag: 'input',
12273             id : id,
12274             type : this.inputType,
12275             cls : 'form-control',
12276             autocomplete: 'new-password',
12277             placeholder : this.placeholder || '' 
12278             
12279         };
12280         if (this.name) {
12281             input.name = this.name;
12282         }
12283         if (this.size) {
12284             input.cls += ' input-' + this.size;
12285         }
12286         
12287         if (this.disabled) {
12288             input.disabled=true;
12289         }
12290         
12291         var inputblock = input;
12292         
12293         if(this.hasFeedback && !this.allowBlank){
12294             
12295             var feedback = {
12296                 tag: 'span',
12297                 cls: 'glyphicon form-control-feedback'
12298             };
12299             
12300             if(this.removable && !this.editable  ){
12301                 inputblock = {
12302                     cls : 'has-feedback',
12303                     cn :  [
12304                         inputblock,
12305                         {
12306                             tag: 'button',
12307                             html : 'x',
12308                             cls : 'roo-combo-removable-btn close'
12309                         },
12310                         feedback
12311                     ] 
12312                 };
12313             } else {
12314                 inputblock = {
12315                     cls : 'has-feedback',
12316                     cn :  [
12317                         inputblock,
12318                         feedback
12319                     ] 
12320                 };
12321             }
12322
12323         } else {
12324             if(this.removable && !this.editable ){
12325                 inputblock = {
12326                     cls : 'roo-removable',
12327                     cn :  [
12328                         inputblock,
12329                         {
12330                             tag: 'button',
12331                             html : 'x',
12332                             cls : 'roo-combo-removable-btn close'
12333                         }
12334                     ] 
12335                 };
12336             }
12337         }
12338         
12339         if (this.before || this.after) {
12340             
12341             inputblock = {
12342                 cls : 'input-group',
12343                 cn :  [] 
12344             };
12345             if (this.before) {
12346                 inputblock.cn.push({
12347                     tag :'span',
12348                     cls : 'input-group-addon input-group-prepend input-group-text',
12349                     html : this.before
12350                 });
12351             }
12352             
12353             inputblock.cn.push(input);
12354             
12355             if(this.hasFeedback && !this.allowBlank){
12356                 inputblock.cls += ' has-feedback';
12357                 inputblock.cn.push(feedback);
12358             }
12359             
12360             if (this.after) {
12361                 inputblock.cn.push({
12362                     tag :'span',
12363                     cls : 'input-group-addon input-group-append input-group-text',
12364                     html : this.after
12365                 });
12366             }
12367             
12368         };
12369         
12370       
12371         
12372         var ibwrap = inputblock;
12373         
12374         if(this.multiple){
12375             ibwrap = {
12376                 tag: 'ul',
12377                 cls: 'roo-select2-choices',
12378                 cn:[
12379                     {
12380                         tag: 'li',
12381                         cls: 'roo-select2-search-field',
12382                         cn: [
12383
12384                             inputblock
12385                         ]
12386                     }
12387                 ]
12388             };
12389                 
12390         }
12391         
12392         var combobox = {
12393             cls: 'roo-select2-container input-group',
12394             cn: [
12395                  {
12396                     tag: 'input',
12397                     type : 'hidden',
12398                     cls: 'form-hidden-field'
12399                 },
12400                 ibwrap
12401             ]
12402         };
12403         
12404         if(!this.multiple && this.showToggleBtn){
12405             
12406             var caret = {
12407                         tag: 'span',
12408                         cls: 'caret'
12409              };
12410             if (this.caret != false) {
12411                 caret = {
12412                      tag: 'i',
12413                      cls: 'fa fa-' + this.caret
12414                 };
12415                 
12416             }
12417             
12418             combobox.cn.push({
12419                 tag :'span',
12420                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12421                 cn : [
12422                     Roo.bootstrap.version == 3 ? caret : '',
12423                     {
12424                         tag: 'span',
12425                         cls: 'combobox-clear',
12426                         cn  : [
12427                             {
12428                                 tag : 'i',
12429                                 cls: 'icon-remove'
12430                             }
12431                         ]
12432                     }
12433                 ]
12434
12435             })
12436         }
12437         
12438         if(this.multiple){
12439             combobox.cls += ' roo-select2-container-multi';
12440         }
12441          var indicator = {
12442             tag : 'i',
12443             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12444             tooltip : 'This field is required'
12445         };
12446         if (Roo.bootstrap.version == 4) {
12447             indicator = {
12448                 tag : 'i',
12449                 style : 'display:none'
12450             };
12451         }
12452         
12453         
12454         if (align ==='left' && this.fieldLabel.length) {
12455             
12456             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12457
12458             cfg.cn = [
12459                 indicator,
12460                 {
12461                     tag: 'label',
12462                     'for' :  id,
12463                     cls : 'control-label',
12464                     html : this.fieldLabel
12465
12466                 },
12467                 {
12468                     cls : "", 
12469                     cn: [
12470                         combobox
12471                     ]
12472                 }
12473
12474             ];
12475             
12476             var labelCfg = cfg.cn[1];
12477             var contentCfg = cfg.cn[2];
12478             
12479             if(this.indicatorpos == 'right'){
12480                 cfg.cn = [
12481                     {
12482                         tag: 'label',
12483                         'for' :  id,
12484                         cls : 'control-label',
12485                         cn : [
12486                             {
12487                                 tag : 'span',
12488                                 html : this.fieldLabel
12489                             },
12490                             indicator
12491                         ]
12492                     },
12493                     {
12494                         cls : "", 
12495                         cn: [
12496                             combobox
12497                         ]
12498                     }
12499
12500                 ];
12501                 
12502                 labelCfg = cfg.cn[0];
12503                 contentCfg = cfg.cn[1];
12504             }
12505             
12506             if(this.labelWidth > 12){
12507                 labelCfg.style = "width: " + this.labelWidth + 'px';
12508             }
12509             
12510             if(this.labelWidth < 13 && this.labelmd == 0){
12511                 this.labelmd = this.labelWidth;
12512             }
12513             
12514             if(this.labellg > 0){
12515                 labelCfg.cls += ' col-lg-' + this.labellg;
12516                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12517             }
12518             
12519             if(this.labelmd > 0){
12520                 labelCfg.cls += ' col-md-' + this.labelmd;
12521                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12522             }
12523             
12524             if(this.labelsm > 0){
12525                 labelCfg.cls += ' col-sm-' + this.labelsm;
12526                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12527             }
12528             
12529             if(this.labelxs > 0){
12530                 labelCfg.cls += ' col-xs-' + this.labelxs;
12531                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12532             }
12533             
12534         } else if ( this.fieldLabel.length) {
12535 //                Roo.log(" label");
12536             cfg.cn = [
12537                 indicator,
12538                {
12539                    tag: 'label',
12540                    //cls : 'input-group-addon',
12541                    html : this.fieldLabel
12542
12543                },
12544
12545                combobox
12546
12547             ];
12548             
12549             if(this.indicatorpos == 'right'){
12550                 
12551                 cfg.cn = [
12552                     {
12553                        tag: 'label',
12554                        cn : [
12555                            {
12556                                tag : 'span',
12557                                html : this.fieldLabel
12558                            },
12559                            indicator
12560                        ]
12561
12562                     },
12563                     combobox
12564
12565                 ];
12566
12567             }
12568
12569         } else {
12570             
12571 //                Roo.log(" no label && no align");
12572                 cfg = combobox
12573                      
12574                 
12575         }
12576         
12577         var settings=this;
12578         ['xs','sm','md','lg'].map(function(size){
12579             if (settings[size]) {
12580                 cfg.cls += ' col-' + size + '-' + settings[size];
12581             }
12582         });
12583         
12584         return cfg;
12585         
12586     },
12587     
12588     
12589     
12590     // private
12591     onResize : function(w, h){
12592 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12593 //        if(typeof w == 'number'){
12594 //            var x = w - this.trigger.getWidth();
12595 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12596 //            this.trigger.setStyle('left', x+'px');
12597 //        }
12598     },
12599
12600     // private
12601     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12602
12603     // private
12604     getResizeEl : function(){
12605         return this.inputEl();
12606     },
12607
12608     // private
12609     getPositionEl : function(){
12610         return this.inputEl();
12611     },
12612
12613     // private
12614     alignErrorIcon : function(){
12615         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12616     },
12617
12618     // private
12619     initEvents : function(){
12620         
12621         this.createList();
12622         
12623         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12624         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12625         if(!this.multiple && this.showToggleBtn){
12626             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12627             if(this.hideTrigger){
12628                 this.trigger.setDisplayed(false);
12629             }
12630             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12631         }
12632         
12633         if(this.multiple){
12634             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12635         }
12636         
12637         if(this.removable && !this.editable && !this.tickable){
12638             var close = this.closeTriggerEl();
12639             
12640             if(close){
12641                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12642                 close.on('click', this.removeBtnClick, this, close);
12643             }
12644         }
12645         
12646         //this.trigger.addClassOnOver('x-form-trigger-over');
12647         //this.trigger.addClassOnClick('x-form-trigger-click');
12648         
12649         //if(!this.width){
12650         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12651         //}
12652     },
12653     
12654     closeTriggerEl : function()
12655     {
12656         var close = this.el.select('.roo-combo-removable-btn', true).first();
12657         return close ? close : false;
12658     },
12659     
12660     removeBtnClick : function(e, h, el)
12661     {
12662         e.preventDefault();
12663         
12664         if(this.fireEvent("remove", this) !== false){
12665             this.reset();
12666             this.fireEvent("afterremove", this)
12667         }
12668     },
12669     
12670     createList : function()
12671     {
12672         this.list = Roo.get(document.body).createChild({
12673             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12674             cls: 'typeahead typeahead-long dropdown-menu shadow',
12675             style: 'display:none'
12676         });
12677         
12678         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12679         
12680     },
12681
12682     // private
12683     initTrigger : function(){
12684        
12685     },
12686
12687     // private
12688     onDestroy : function(){
12689         if(this.trigger){
12690             this.trigger.removeAllListeners();
12691           //  this.trigger.remove();
12692         }
12693         //if(this.wrap){
12694         //    this.wrap.remove();
12695         //}
12696         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12697     },
12698
12699     // private
12700     onFocus : function(){
12701         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12702         /*
12703         if(!this.mimicing){
12704             this.wrap.addClass('x-trigger-wrap-focus');
12705             this.mimicing = true;
12706             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12707             if(this.monitorTab){
12708                 this.el.on("keydown", this.checkTab, this);
12709             }
12710         }
12711         */
12712     },
12713
12714     // private
12715     checkTab : function(e){
12716         if(e.getKey() == e.TAB){
12717             this.triggerBlur();
12718         }
12719     },
12720
12721     // private
12722     onBlur : function(){
12723         // do nothing
12724     },
12725
12726     // private
12727     mimicBlur : function(e, t){
12728         /*
12729         if(!this.wrap.contains(t) && this.validateBlur()){
12730             this.triggerBlur();
12731         }
12732         */
12733     },
12734
12735     // private
12736     triggerBlur : function(){
12737         this.mimicing = false;
12738         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12739         if(this.monitorTab){
12740             this.el.un("keydown", this.checkTab, this);
12741         }
12742         //this.wrap.removeClass('x-trigger-wrap-focus');
12743         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12744     },
12745
12746     // private
12747     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12748     validateBlur : function(e, t){
12749         return true;
12750     },
12751
12752     // private
12753     onDisable : function(){
12754         this.inputEl().dom.disabled = true;
12755         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12756         //if(this.wrap){
12757         //    this.wrap.addClass('x-item-disabled');
12758         //}
12759     },
12760
12761     // private
12762     onEnable : function(){
12763         this.inputEl().dom.disabled = false;
12764         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12765         //if(this.wrap){
12766         //    this.el.removeClass('x-item-disabled');
12767         //}
12768     },
12769
12770     // private
12771     onShow : function(){
12772         var ae = this.getActionEl();
12773         
12774         if(ae){
12775             ae.dom.style.display = '';
12776             ae.dom.style.visibility = 'visible';
12777         }
12778     },
12779
12780     // private
12781     
12782     onHide : function(){
12783         var ae = this.getActionEl();
12784         ae.dom.style.display = 'none';
12785     },
12786
12787     /**
12788      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12789      * by an implementing function.
12790      * @method
12791      * @param {EventObject} e
12792      */
12793     onTriggerClick : Roo.emptyFn
12794 });
12795  
12796 /*
12797 * Licence: LGPL
12798 */
12799
12800 /**
12801  * @class Roo.bootstrap.CardUploader
12802  * @extends Roo.bootstrap.Button
12803  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12804  * @cfg {Number} errorTimeout default 3000
12805  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12806  * @cfg {Array}  html The button text.
12807
12808  *
12809  * @constructor
12810  * Create a new CardUploader
12811  * @param {Object} config The config object
12812  */
12813
12814 Roo.bootstrap.CardUploader = function(config){
12815     
12816  
12817     
12818     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12819     
12820     
12821     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12822         return r.data.id
12823      });
12824     
12825      this.addEvents({
12826          // raw events
12827         /**
12828          * @event preview
12829          * When a image is clicked on - and needs to display a slideshow or similar..
12830          * @param {Roo.bootstrap.Card} this
12831          * @param {Object} The image information data 
12832          *
12833          */
12834         'preview' : true,
12835          /**
12836          * @event download
12837          * When a the download link is clicked
12838          * @param {Roo.bootstrap.Card} this
12839          * @param {Object} The image information data  contains 
12840          */
12841         'download' : true
12842         
12843     });
12844 };
12845  
12846 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12847     
12848      
12849     errorTimeout : 3000,
12850      
12851     images : false,
12852    
12853     fileCollection : false,
12854     allowBlank : true,
12855     
12856     getAutoCreate : function()
12857     {
12858         
12859         var cfg =  {
12860             cls :'form-group' ,
12861             cn : [
12862                
12863                 {
12864                     tag: 'label',
12865                    //cls : 'input-group-addon',
12866                     html : this.fieldLabel
12867
12868                 },
12869
12870                 {
12871                     tag: 'input',
12872                     type : 'hidden',
12873                     name : this.name,
12874                     value : this.value,
12875                     cls : 'd-none  form-control'
12876                 },
12877                 
12878                 {
12879                     tag: 'input',
12880                     multiple : 'multiple',
12881                     type : 'file',
12882                     cls : 'd-none  roo-card-upload-selector'
12883                 },
12884                 
12885                 {
12886                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12887                 },
12888                 {
12889                     cls : 'card-columns roo-card-uploader-container'
12890                 }
12891
12892             ]
12893         };
12894            
12895          
12896         return cfg;
12897     },
12898     
12899     getChildContainer : function() /// what children are added to.
12900     {
12901         return this.containerEl;
12902     },
12903    
12904     getButtonContainer : function() /// what children are added to.
12905     {
12906         return this.el.select(".roo-card-uploader-button-container").first();
12907     },
12908    
12909     initEvents : function()
12910     {
12911         
12912         Roo.bootstrap.Input.prototype.initEvents.call(this);
12913         
12914         var t = this;
12915         this.addxtype({
12916             xns: Roo.bootstrap,
12917
12918             xtype : 'Button',
12919             container_method : 'getButtonContainer' ,            
12920             html :  this.html, // fix changable?
12921             cls : 'w-100 ',
12922             listeners : {
12923                 'click' : function(btn, e) {
12924                     t.onClick(e);
12925                 }
12926             }
12927         });
12928         
12929         
12930         
12931         
12932         this.urlAPI = (window.createObjectURL && window) || 
12933                                 (window.URL && URL.revokeObjectURL && URL) || 
12934                                 (window.webkitURL && webkitURL);
12935                         
12936          
12937          
12938          
12939         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12940         
12941         this.selectorEl.on('change', this.onFileSelected, this);
12942         if (this.images) {
12943             var t = this;
12944             this.images.forEach(function(img) {
12945                 t.addCard(img)
12946             });
12947             this.images = false;
12948         }
12949         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12950          
12951        
12952     },
12953     
12954    
12955     onClick : function(e)
12956     {
12957         e.preventDefault();
12958          
12959         this.selectorEl.dom.click();
12960          
12961     },
12962     
12963     onFileSelected : function(e)
12964     {
12965         e.preventDefault();
12966         
12967         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12968             return;
12969         }
12970         
12971         Roo.each(this.selectorEl.dom.files, function(file){    
12972             this.addFile(file);
12973         }, this);
12974          
12975     },
12976     
12977       
12978     
12979       
12980     
12981     addFile : function(file)
12982     {
12983            
12984         if(typeof(file) === 'string'){
12985             throw "Add file by name?"; // should not happen
12986             return;
12987         }
12988         
12989         if(!file || !this.urlAPI){
12990             return;
12991         }
12992         
12993         // file;
12994         // file.type;
12995         
12996         var _this = this;
12997         
12998         
12999         var url = _this.urlAPI.createObjectURL( file);
13000            
13001         this.addCard({
13002             id : Roo.bootstrap.CardUploader.ID--,
13003             is_uploaded : false,
13004             src : url,
13005             srcfile : file,
13006             title : file.name,
13007             mimetype : file.type,
13008             preview : false,
13009             is_deleted : 0
13010         });
13011         
13012     },
13013     
13014     /**
13015      * addCard - add an Attachment to the uploader
13016      * @param data - the data about the image to upload
13017      *
13018      * {
13019           id : 123
13020           title : "Title of file",
13021           is_uploaded : false,
13022           src : "http://.....",
13023           srcfile : { the File upload object },
13024           mimetype : file.type,
13025           preview : false,
13026           is_deleted : 0
13027           .. any other data...
13028         }
13029      *
13030      * 
13031     */
13032     
13033     addCard : function (data)
13034     {
13035         // hidden input element?
13036         // if the file is not an image...
13037         //then we need to use something other that and header_image
13038         var t = this;
13039         //   remove.....
13040         var footer = [
13041             {
13042                 xns : Roo.bootstrap,
13043                 xtype : 'CardFooter',
13044                  items: [
13045                     {
13046                         xns : Roo.bootstrap,
13047                         xtype : 'Element',
13048                         cls : 'd-flex',
13049                         items : [
13050                             
13051                             {
13052                                 xns : Roo.bootstrap,
13053                                 xtype : 'Button',
13054                                 html : String.format("<small>{0}</small>", data.title),
13055                                 cls : 'col-10 text-left',
13056                                 size: 'sm',
13057                                 weight: 'link',
13058                                 fa : 'download',
13059                                 listeners : {
13060                                     click : function() {
13061                                      
13062                                         t.fireEvent( "download", t, data );
13063                                     }
13064                                 }
13065                             },
13066                           
13067                             {
13068                                 xns : Roo.bootstrap,
13069                                 xtype : 'Button',
13070                                 style: 'max-height: 28px; ',
13071                                 size : 'sm',
13072                                 weight: 'danger',
13073                                 cls : 'col-2',
13074                                 fa : 'times',
13075                                 listeners : {
13076                                     click : function() {
13077                                         t.removeCard(data.id)
13078                                     }
13079                                 }
13080                             }
13081                         ]
13082                     }
13083                     
13084                 ] 
13085             }
13086             
13087         ];
13088         
13089         var cn = this.addxtype(
13090             {
13091                  
13092                 xns : Roo.bootstrap,
13093                 xtype : 'Card',
13094                 closeable : true,
13095                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13096                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13097                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13098                 data : data,
13099                 html : false,
13100                  
13101                 items : footer,
13102                 initEvents : function() {
13103                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13104                     var card = this;
13105                     this.imgEl = this.el.select('.card-img-top').first();
13106                     if (this.imgEl) {
13107                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13108                         this.imgEl.set({ 'pointer' : 'cursor' });
13109                                   
13110                     }
13111                     this.getCardFooter().addClass('p-1');
13112                     
13113                   
13114                 }
13115                 
13116             }
13117         );
13118         // dont' really need ot update items.
13119         // this.items.push(cn);
13120         this.fileCollection.add(cn);
13121         
13122         if (!data.srcfile) {
13123             this.updateInput();
13124             return;
13125         }
13126             
13127         var _t = this;
13128         var reader = new FileReader();
13129         reader.addEventListener("load", function() {  
13130             data.srcdata =  reader.result;
13131             _t.updateInput();
13132         });
13133         reader.readAsDataURL(data.srcfile);
13134         
13135         
13136         
13137     },
13138     removeCard : function(id)
13139     {
13140         
13141         var card  = this.fileCollection.get(id);
13142         card.data.is_deleted = 1;
13143         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13144         //this.fileCollection.remove(card);
13145         //this.items = this.items.filter(function(e) { return e != card });
13146         // dont' really need ot update items.
13147         card.el.dom.parentNode.removeChild(card.el.dom);
13148         this.updateInput();
13149
13150         
13151     },
13152     reset: function()
13153     {
13154         this.fileCollection.each(function(card) {
13155             if (card.el.dom && card.el.dom.parentNode) {
13156                 card.el.dom.parentNode.removeChild(card.el.dom);
13157             }
13158         });
13159         this.fileCollection.clear();
13160         this.updateInput();
13161     },
13162     
13163     updateInput : function()
13164     {
13165          var data = [];
13166         this.fileCollection.each(function(e) {
13167             data.push(e.data);
13168             
13169         });
13170         this.inputEl().dom.value = JSON.stringify(data);
13171         
13172         
13173         
13174     }
13175     
13176     
13177 });
13178
13179
13180 Roo.bootstrap.CardUploader.ID = -1;/*
13181  * Based on:
13182  * Ext JS Library 1.1.1
13183  * Copyright(c) 2006-2007, Ext JS, LLC.
13184  *
13185  * Originally Released Under LGPL - original licence link has changed is not relivant.
13186  *
13187  * Fork - LGPL
13188  * <script type="text/javascript">
13189  */
13190
13191
13192 /**
13193  * @class Roo.data.SortTypes
13194  * @singleton
13195  * Defines the default sorting (casting?) comparison functions used when sorting data.
13196  */
13197 Roo.data.SortTypes = {
13198     /**
13199      * Default sort that does nothing
13200      * @param {Mixed} s The value being converted
13201      * @return {Mixed} The comparison value
13202      */
13203     none : function(s){
13204         return s;
13205     },
13206     
13207     /**
13208      * The regular expression used to strip tags
13209      * @type {RegExp}
13210      * @property
13211      */
13212     stripTagsRE : /<\/?[^>]+>/gi,
13213     
13214     /**
13215      * Strips all HTML tags to sort on text only
13216      * @param {Mixed} s The value being converted
13217      * @return {String} The comparison value
13218      */
13219     asText : function(s){
13220         return String(s).replace(this.stripTagsRE, "");
13221     },
13222     
13223     /**
13224      * Strips all HTML tags to sort on text only - Case insensitive
13225      * @param {Mixed} s The value being converted
13226      * @return {String} The comparison value
13227      */
13228     asUCText : function(s){
13229         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13230     },
13231     
13232     /**
13233      * Case insensitive string
13234      * @param {Mixed} s The value being converted
13235      * @return {String} The comparison value
13236      */
13237     asUCString : function(s) {
13238         return String(s).toUpperCase();
13239     },
13240     
13241     /**
13242      * Date sorting
13243      * @param {Mixed} s The value being converted
13244      * @return {Number} The comparison value
13245      */
13246     asDate : function(s) {
13247         if(!s){
13248             return 0;
13249         }
13250         if(s instanceof Date){
13251             return s.getTime();
13252         }
13253         return Date.parse(String(s));
13254     },
13255     
13256     /**
13257      * Float sorting
13258      * @param {Mixed} s The value being converted
13259      * @return {Float} The comparison value
13260      */
13261     asFloat : function(s) {
13262         var val = parseFloat(String(s).replace(/,/g, ""));
13263         if(isNaN(val)) {
13264             val = 0;
13265         }
13266         return val;
13267     },
13268     
13269     /**
13270      * Integer sorting
13271      * @param {Mixed} s The value being converted
13272      * @return {Number} The comparison value
13273      */
13274     asInt : function(s) {
13275         var val = parseInt(String(s).replace(/,/g, ""));
13276         if(isNaN(val)) {
13277             val = 0;
13278         }
13279         return val;
13280     }
13281 };/*
13282  * Based on:
13283  * Ext JS Library 1.1.1
13284  * Copyright(c) 2006-2007, Ext JS, LLC.
13285  *
13286  * Originally Released Under LGPL - original licence link has changed is not relivant.
13287  *
13288  * Fork - LGPL
13289  * <script type="text/javascript">
13290  */
13291
13292 /**
13293 * @class Roo.data.Record
13294  * Instances of this class encapsulate both record <em>definition</em> information, and record
13295  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13296  * to access Records cached in an {@link Roo.data.Store} object.<br>
13297  * <p>
13298  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13299  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13300  * objects.<br>
13301  * <p>
13302  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13303  * @constructor
13304  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13305  * {@link #create}. The parameters are the same.
13306  * @param {Array} data An associative Array of data values keyed by the field name.
13307  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13308  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13309  * not specified an integer id is generated.
13310  */
13311 Roo.data.Record = function(data, id){
13312     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13313     this.data = data;
13314 };
13315
13316 /**
13317  * Generate a constructor for a specific record layout.
13318  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13319  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13320  * Each field definition object may contain the following properties: <ul>
13321  * <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,
13322  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13323  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13324  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13325  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13326  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13327  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13328  * this may be omitted.</p></li>
13329  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13330  * <ul><li>auto (Default, implies no conversion)</li>
13331  * <li>string</li>
13332  * <li>int</li>
13333  * <li>float</li>
13334  * <li>boolean</li>
13335  * <li>date</li></ul></p></li>
13336  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13337  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13338  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13339  * by the Reader into an object that will be stored in the Record. It is passed the
13340  * following parameters:<ul>
13341  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13342  * </ul></p></li>
13343  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13344  * </ul>
13345  * <br>usage:<br><pre><code>
13346 var TopicRecord = Roo.data.Record.create(
13347     {name: 'title', mapping: 'topic_title'},
13348     {name: 'author', mapping: 'username'},
13349     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13350     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13351     {name: 'lastPoster', mapping: 'user2'},
13352     {name: 'excerpt', mapping: 'post_text'}
13353 );
13354
13355 var myNewRecord = new TopicRecord({
13356     title: 'Do my job please',
13357     author: 'noobie',
13358     totalPosts: 1,
13359     lastPost: new Date(),
13360     lastPoster: 'Animal',
13361     excerpt: 'No way dude!'
13362 });
13363 myStore.add(myNewRecord);
13364 </code></pre>
13365  * @method create
13366  * @static
13367  */
13368 Roo.data.Record.create = function(o){
13369     var f = function(){
13370         f.superclass.constructor.apply(this, arguments);
13371     };
13372     Roo.extend(f, Roo.data.Record);
13373     var p = f.prototype;
13374     p.fields = new Roo.util.MixedCollection(false, function(field){
13375         return field.name;
13376     });
13377     for(var i = 0, len = o.length; i < len; i++){
13378         p.fields.add(new Roo.data.Field(o[i]));
13379     }
13380     f.getField = function(name){
13381         return p.fields.get(name);  
13382     };
13383     return f;
13384 };
13385
13386 Roo.data.Record.AUTO_ID = 1000;
13387 Roo.data.Record.EDIT = 'edit';
13388 Roo.data.Record.REJECT = 'reject';
13389 Roo.data.Record.COMMIT = 'commit';
13390
13391 Roo.data.Record.prototype = {
13392     /**
13393      * Readonly flag - true if this record has been modified.
13394      * @type Boolean
13395      */
13396     dirty : false,
13397     editing : false,
13398     error: null,
13399     modified: null,
13400
13401     // private
13402     join : function(store){
13403         this.store = store;
13404     },
13405
13406     /**
13407      * Set the named field to the specified value.
13408      * @param {String} name The name of the field to set.
13409      * @param {Object} value The value to set the field to.
13410      */
13411     set : function(name, value){
13412         if(this.data[name] == value){
13413             return;
13414         }
13415         this.dirty = true;
13416         if(!this.modified){
13417             this.modified = {};
13418         }
13419         if(typeof this.modified[name] == 'undefined'){
13420             this.modified[name] = this.data[name];
13421         }
13422         this.data[name] = value;
13423         if(!this.editing && this.store){
13424             this.store.afterEdit(this);
13425         }       
13426     },
13427
13428     /**
13429      * Get the value of the named field.
13430      * @param {String} name The name of the field to get the value of.
13431      * @return {Object} The value of the field.
13432      */
13433     get : function(name){
13434         return this.data[name]; 
13435     },
13436
13437     // private
13438     beginEdit : function(){
13439         this.editing = true;
13440         this.modified = {}; 
13441     },
13442
13443     // private
13444     cancelEdit : function(){
13445         this.editing = false;
13446         delete this.modified;
13447     },
13448
13449     // private
13450     endEdit : function(){
13451         this.editing = false;
13452         if(this.dirty && this.store){
13453             this.store.afterEdit(this);
13454         }
13455     },
13456
13457     /**
13458      * Usually called by the {@link Roo.data.Store} which owns the Record.
13459      * Rejects all changes made to the Record since either creation, or the last commit operation.
13460      * Modified fields are reverted to their original values.
13461      * <p>
13462      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13463      * of reject operations.
13464      */
13465     reject : function(){
13466         var m = this.modified;
13467         for(var n in m){
13468             if(typeof m[n] != "function"){
13469                 this.data[n] = m[n];
13470             }
13471         }
13472         this.dirty = false;
13473         delete this.modified;
13474         this.editing = false;
13475         if(this.store){
13476             this.store.afterReject(this);
13477         }
13478     },
13479
13480     /**
13481      * Usually called by the {@link Roo.data.Store} which owns the Record.
13482      * Commits all changes made to the Record since either creation, or the last commit operation.
13483      * <p>
13484      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13485      * of commit operations.
13486      */
13487     commit : function(){
13488         this.dirty = false;
13489         delete this.modified;
13490         this.editing = false;
13491         if(this.store){
13492             this.store.afterCommit(this);
13493         }
13494     },
13495
13496     // private
13497     hasError : function(){
13498         return this.error != null;
13499     },
13500
13501     // private
13502     clearError : function(){
13503         this.error = null;
13504     },
13505
13506     /**
13507      * Creates a copy of this record.
13508      * @param {String} id (optional) A new record id if you don't want to use this record's id
13509      * @return {Record}
13510      */
13511     copy : function(newId) {
13512         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13513     }
13514 };/*
13515  * Based on:
13516  * Ext JS Library 1.1.1
13517  * Copyright(c) 2006-2007, Ext JS, LLC.
13518  *
13519  * Originally Released Under LGPL - original licence link has changed is not relivant.
13520  *
13521  * Fork - LGPL
13522  * <script type="text/javascript">
13523  */
13524
13525
13526
13527 /**
13528  * @class Roo.data.Store
13529  * @extends Roo.util.Observable
13530  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13531  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13532  * <p>
13533  * 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
13534  * has no knowledge of the format of the data returned by the Proxy.<br>
13535  * <p>
13536  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13537  * instances from the data object. These records are cached and made available through accessor functions.
13538  * @constructor
13539  * Creates a new Store.
13540  * @param {Object} config A config object containing the objects needed for the Store to access data,
13541  * and read the data into Records.
13542  */
13543 Roo.data.Store = function(config){
13544     this.data = new Roo.util.MixedCollection(false);
13545     this.data.getKey = function(o){
13546         return o.id;
13547     };
13548     this.baseParams = {};
13549     // private
13550     this.paramNames = {
13551         "start" : "start",
13552         "limit" : "limit",
13553         "sort" : "sort",
13554         "dir" : "dir",
13555         "multisort" : "_multisort"
13556     };
13557
13558     if(config && config.data){
13559         this.inlineData = config.data;
13560         delete config.data;
13561     }
13562
13563     Roo.apply(this, config);
13564     
13565     if(this.reader){ // reader passed
13566         this.reader = Roo.factory(this.reader, Roo.data);
13567         this.reader.xmodule = this.xmodule || false;
13568         if(!this.recordType){
13569             this.recordType = this.reader.recordType;
13570         }
13571         if(this.reader.onMetaChange){
13572             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13573         }
13574     }
13575
13576     if(this.recordType){
13577         this.fields = this.recordType.prototype.fields;
13578     }
13579     this.modified = [];
13580
13581     this.addEvents({
13582         /**
13583          * @event datachanged
13584          * Fires when the data cache has changed, and a widget which is using this Store
13585          * as a Record cache should refresh its view.
13586          * @param {Store} this
13587          */
13588         datachanged : true,
13589         /**
13590          * @event metachange
13591          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13592          * @param {Store} this
13593          * @param {Object} meta The JSON metadata
13594          */
13595         metachange : true,
13596         /**
13597          * @event add
13598          * Fires when Records have been added to the Store
13599          * @param {Store} this
13600          * @param {Roo.data.Record[]} records The array of Records added
13601          * @param {Number} index The index at which the record(s) were added
13602          */
13603         add : true,
13604         /**
13605          * @event remove
13606          * Fires when a Record has been removed from the Store
13607          * @param {Store} this
13608          * @param {Roo.data.Record} record The Record that was removed
13609          * @param {Number} index The index at which the record was removed
13610          */
13611         remove : true,
13612         /**
13613          * @event update
13614          * Fires when a Record has been updated
13615          * @param {Store} this
13616          * @param {Roo.data.Record} record The Record that was updated
13617          * @param {String} operation The update operation being performed.  Value may be one of:
13618          * <pre><code>
13619  Roo.data.Record.EDIT
13620  Roo.data.Record.REJECT
13621  Roo.data.Record.COMMIT
13622          * </code></pre>
13623          */
13624         update : true,
13625         /**
13626          * @event clear
13627          * Fires when the data cache has been cleared.
13628          * @param {Store} this
13629          */
13630         clear : true,
13631         /**
13632          * @event beforeload
13633          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13634          * the load action will be canceled.
13635          * @param {Store} this
13636          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13637          */
13638         beforeload : true,
13639         /**
13640          * @event beforeloadadd
13641          * Fires after a new set of Records has been loaded.
13642          * @param {Store} this
13643          * @param {Roo.data.Record[]} records The Records that were loaded
13644          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13645          */
13646         beforeloadadd : true,
13647         /**
13648          * @event load
13649          * Fires after a new set of Records has been loaded, before they are added to the store.
13650          * @param {Store} this
13651          * @param {Roo.data.Record[]} records The Records that were loaded
13652          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13653          * @params {Object} return from reader
13654          */
13655         load : true,
13656         /**
13657          * @event loadexception
13658          * Fires if an exception occurs in the Proxy during loading.
13659          * Called with the signature of the Proxy's "loadexception" event.
13660          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13661          * 
13662          * @param {Proxy} 
13663          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13664          * @param {Object} load options 
13665          * @param {Object} jsonData from your request (normally this contains the Exception)
13666          */
13667         loadexception : true
13668     });
13669     
13670     if(this.proxy){
13671         this.proxy = Roo.factory(this.proxy, Roo.data);
13672         this.proxy.xmodule = this.xmodule || false;
13673         this.relayEvents(this.proxy,  ["loadexception"]);
13674     }
13675     this.sortToggle = {};
13676     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13677
13678     Roo.data.Store.superclass.constructor.call(this);
13679
13680     if(this.inlineData){
13681         this.loadData(this.inlineData);
13682         delete this.inlineData;
13683     }
13684 };
13685
13686 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13687      /**
13688     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13689     * without a remote query - used by combo/forms at present.
13690     */
13691     
13692     /**
13693     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13694     */
13695     /**
13696     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13697     */
13698     /**
13699     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13700     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13701     */
13702     /**
13703     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13704     * on any HTTP request
13705     */
13706     /**
13707     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13708     */
13709     /**
13710     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13711     */
13712     multiSort: false,
13713     /**
13714     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13715     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13716     */
13717     remoteSort : false,
13718
13719     /**
13720     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13721      * loaded or when a record is removed. (defaults to false).
13722     */
13723     pruneModifiedRecords : false,
13724
13725     // private
13726     lastOptions : null,
13727
13728     /**
13729      * Add Records to the Store and fires the add event.
13730      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13731      */
13732     add : function(records){
13733         records = [].concat(records);
13734         for(var i = 0, len = records.length; i < len; i++){
13735             records[i].join(this);
13736         }
13737         var index = this.data.length;
13738         this.data.addAll(records);
13739         this.fireEvent("add", this, records, index);
13740     },
13741
13742     /**
13743      * Remove a Record from the Store and fires the remove event.
13744      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13745      */
13746     remove : function(record){
13747         var index = this.data.indexOf(record);
13748         this.data.removeAt(index);
13749  
13750         if(this.pruneModifiedRecords){
13751             this.modified.remove(record);
13752         }
13753         this.fireEvent("remove", this, record, index);
13754     },
13755
13756     /**
13757      * Remove all Records from the Store and fires the clear event.
13758      */
13759     removeAll : function(){
13760         this.data.clear();
13761         if(this.pruneModifiedRecords){
13762             this.modified = [];
13763         }
13764         this.fireEvent("clear", this);
13765     },
13766
13767     /**
13768      * Inserts Records to the Store at the given index and fires the add event.
13769      * @param {Number} index The start index at which to insert the passed Records.
13770      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13771      */
13772     insert : function(index, records){
13773         records = [].concat(records);
13774         for(var i = 0, len = records.length; i < len; i++){
13775             this.data.insert(index, records[i]);
13776             records[i].join(this);
13777         }
13778         this.fireEvent("add", this, records, index);
13779     },
13780
13781     /**
13782      * Get the index within the cache of the passed Record.
13783      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13784      * @return {Number} The index of the passed Record. Returns -1 if not found.
13785      */
13786     indexOf : function(record){
13787         return this.data.indexOf(record);
13788     },
13789
13790     /**
13791      * Get the index within the cache of the Record with the passed id.
13792      * @param {String} id The id of the Record to find.
13793      * @return {Number} The index of the Record. Returns -1 if not found.
13794      */
13795     indexOfId : function(id){
13796         return this.data.indexOfKey(id);
13797     },
13798
13799     /**
13800      * Get the Record with the specified id.
13801      * @param {String} id The id of the Record to find.
13802      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13803      */
13804     getById : function(id){
13805         return this.data.key(id);
13806     },
13807
13808     /**
13809      * Get the Record at the specified index.
13810      * @param {Number} index The index of the Record to find.
13811      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13812      */
13813     getAt : function(index){
13814         return this.data.itemAt(index);
13815     },
13816
13817     /**
13818      * Returns a range of Records between specified indices.
13819      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13820      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13821      * @return {Roo.data.Record[]} An array of Records
13822      */
13823     getRange : function(start, end){
13824         return this.data.getRange(start, end);
13825     },
13826
13827     // private
13828     storeOptions : function(o){
13829         o = Roo.apply({}, o);
13830         delete o.callback;
13831         delete o.scope;
13832         this.lastOptions = o;
13833     },
13834
13835     /**
13836      * Loads the Record cache from the configured Proxy using the configured Reader.
13837      * <p>
13838      * If using remote paging, then the first load call must specify the <em>start</em>
13839      * and <em>limit</em> properties in the options.params property to establish the initial
13840      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13841      * <p>
13842      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13843      * and this call will return before the new data has been loaded. Perform any post-processing
13844      * in a callback function, or in a "load" event handler.</strong>
13845      * <p>
13846      * @param {Object} options An object containing properties which control loading options:<ul>
13847      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13848      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13849      * passed the following arguments:<ul>
13850      * <li>r : Roo.data.Record[]</li>
13851      * <li>options: Options object from the load call</li>
13852      * <li>success: Boolean success indicator</li></ul></li>
13853      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13854      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13855      * </ul>
13856      */
13857     load : function(options){
13858         options = options || {};
13859         if(this.fireEvent("beforeload", this, options) !== false){
13860             this.storeOptions(options);
13861             var p = Roo.apply(options.params || {}, this.baseParams);
13862             // if meta was not loaded from remote source.. try requesting it.
13863             if (!this.reader.metaFromRemote) {
13864                 p._requestMeta = 1;
13865             }
13866             if(this.sortInfo && this.remoteSort){
13867                 var pn = this.paramNames;
13868                 p[pn["sort"]] = this.sortInfo.field;
13869                 p[pn["dir"]] = this.sortInfo.direction;
13870             }
13871             if (this.multiSort) {
13872                 var pn = this.paramNames;
13873                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13874             }
13875             
13876             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13877         }
13878     },
13879
13880     /**
13881      * Reloads the Record cache from the configured Proxy using the configured Reader and
13882      * the options from the last load operation performed.
13883      * @param {Object} options (optional) An object containing properties which may override the options
13884      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13885      * the most recently used options are reused).
13886      */
13887     reload : function(options){
13888         this.load(Roo.applyIf(options||{}, this.lastOptions));
13889     },
13890
13891     // private
13892     // Called as a callback by the Reader during a load operation.
13893     loadRecords : function(o, options, success){
13894         if(!o || success === false){
13895             if(success !== false){
13896                 this.fireEvent("load", this, [], options, o);
13897             }
13898             if(options.callback){
13899                 options.callback.call(options.scope || this, [], options, false);
13900             }
13901             return;
13902         }
13903         // if data returned failure - throw an exception.
13904         if (o.success === false) {
13905             // show a message if no listener is registered.
13906             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13907                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13908             }
13909             // loadmask wil be hooked into this..
13910             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13911             return;
13912         }
13913         var r = o.records, t = o.totalRecords || r.length;
13914         
13915         this.fireEvent("beforeloadadd", this, r, options, o);
13916         
13917         if(!options || options.add !== true){
13918             if(this.pruneModifiedRecords){
13919                 this.modified = [];
13920             }
13921             for(var i = 0, len = r.length; i < len; i++){
13922                 r[i].join(this);
13923             }
13924             if(this.snapshot){
13925                 this.data = this.snapshot;
13926                 delete this.snapshot;
13927             }
13928             this.data.clear();
13929             this.data.addAll(r);
13930             this.totalLength = t;
13931             this.applySort();
13932             this.fireEvent("datachanged", this);
13933         }else{
13934             this.totalLength = Math.max(t, this.data.length+r.length);
13935             this.add(r);
13936         }
13937         
13938         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13939                 
13940             var e = new Roo.data.Record({});
13941
13942             e.set(this.parent.displayField, this.parent.emptyTitle);
13943             e.set(this.parent.valueField, '');
13944
13945             this.insert(0, e);
13946         }
13947             
13948         this.fireEvent("load", this, r, options, o);
13949         if(options.callback){
13950             options.callback.call(options.scope || this, r, options, true);
13951         }
13952     },
13953
13954
13955     /**
13956      * Loads data from a passed data block. A Reader which understands the format of the data
13957      * must have been configured in the constructor.
13958      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13959      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13960      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13961      */
13962     loadData : function(o, append){
13963         var r = this.reader.readRecords(o);
13964         this.loadRecords(r, {add: append}, true);
13965     },
13966     
13967      /**
13968      * using 'cn' the nested child reader read the child array into it's child stores.
13969      * @param {Object} rec The record with a 'children array
13970      */
13971     loadDataFromChildren : function(rec)
13972     {
13973         this.loadData(this.reader.toLoadData(rec));
13974     },
13975     
13976
13977     /**
13978      * Gets the number of cached records.
13979      * <p>
13980      * <em>If using paging, this may not be the total size of the dataset. If the data object
13981      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13982      * the data set size</em>
13983      */
13984     getCount : function(){
13985         return this.data.length || 0;
13986     },
13987
13988     /**
13989      * Gets the total number of records in the dataset as returned by the server.
13990      * <p>
13991      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13992      * the dataset size</em>
13993      */
13994     getTotalCount : function(){
13995         return this.totalLength || 0;
13996     },
13997
13998     /**
13999      * Returns the sort state of the Store as an object with two properties:
14000      * <pre><code>
14001  field {String} The name of the field by which the Records are sorted
14002  direction {String} The sort order, "ASC" or "DESC"
14003      * </code></pre>
14004      */
14005     getSortState : function(){
14006         return this.sortInfo;
14007     },
14008
14009     // private
14010     applySort : function(){
14011         if(this.sortInfo && !this.remoteSort){
14012             var s = this.sortInfo, f = s.field;
14013             var st = this.fields.get(f).sortType;
14014             var fn = function(r1, r2){
14015                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14016                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14017             };
14018             this.data.sort(s.direction, fn);
14019             if(this.snapshot && this.snapshot != this.data){
14020                 this.snapshot.sort(s.direction, fn);
14021             }
14022         }
14023     },
14024
14025     /**
14026      * Sets the default sort column and order to be used by the next load operation.
14027      * @param {String} fieldName The name of the field to sort by.
14028      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14029      */
14030     setDefaultSort : function(field, dir){
14031         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14032     },
14033
14034     /**
14035      * Sort the Records.
14036      * If remote sorting is used, the sort is performed on the server, and the cache is
14037      * reloaded. If local sorting is used, the cache is sorted internally.
14038      * @param {String} fieldName The name of the field to sort by.
14039      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14040      */
14041     sort : function(fieldName, dir){
14042         var f = this.fields.get(fieldName);
14043         if(!dir){
14044             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14045             
14046             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14047                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14048             }else{
14049                 dir = f.sortDir;
14050             }
14051         }
14052         this.sortToggle[f.name] = dir;
14053         this.sortInfo = {field: f.name, direction: dir};
14054         if(!this.remoteSort){
14055             this.applySort();
14056             this.fireEvent("datachanged", this);
14057         }else{
14058             this.load(this.lastOptions);
14059         }
14060     },
14061
14062     /**
14063      * Calls the specified function for each of the Records in the cache.
14064      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14065      * Returning <em>false</em> aborts and exits the iteration.
14066      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14067      */
14068     each : function(fn, scope){
14069         this.data.each(fn, scope);
14070     },
14071
14072     /**
14073      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14074      * (e.g., during paging).
14075      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14076      */
14077     getModifiedRecords : function(){
14078         return this.modified;
14079     },
14080
14081     // private
14082     createFilterFn : function(property, value, anyMatch){
14083         if(!value.exec){ // not a regex
14084             value = String(value);
14085             if(value.length == 0){
14086                 return false;
14087             }
14088             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14089         }
14090         return function(r){
14091             return value.test(r.data[property]);
14092         };
14093     },
14094
14095     /**
14096      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14097      * @param {String} property A field on your records
14098      * @param {Number} start The record index to start at (defaults to 0)
14099      * @param {Number} end The last record index to include (defaults to length - 1)
14100      * @return {Number} The sum
14101      */
14102     sum : function(property, start, end){
14103         var rs = this.data.items, v = 0;
14104         start = start || 0;
14105         end = (end || end === 0) ? end : rs.length-1;
14106
14107         for(var i = start; i <= end; i++){
14108             v += (rs[i].data[property] || 0);
14109         }
14110         return v;
14111     },
14112
14113     /**
14114      * Filter the records by a specified property.
14115      * @param {String} field A field on your records
14116      * @param {String/RegExp} value Either a string that the field
14117      * should start with or a RegExp to test against the field
14118      * @param {Boolean} anyMatch True to match any part not just the beginning
14119      */
14120     filter : function(property, value, anyMatch){
14121         var fn = this.createFilterFn(property, value, anyMatch);
14122         return fn ? this.filterBy(fn) : this.clearFilter();
14123     },
14124
14125     /**
14126      * Filter by a function. The specified function will be called with each
14127      * record in this data source. If the function returns true the record is included,
14128      * otherwise it is filtered.
14129      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14130      * @param {Object} scope (optional) The scope of the function (defaults to this)
14131      */
14132     filterBy : function(fn, scope){
14133         this.snapshot = this.snapshot || this.data;
14134         this.data = this.queryBy(fn, scope||this);
14135         this.fireEvent("datachanged", this);
14136     },
14137
14138     /**
14139      * Query the records by a specified property.
14140      * @param {String} field A field on your records
14141      * @param {String/RegExp} value Either a string that the field
14142      * should start with or a RegExp to test against the field
14143      * @param {Boolean} anyMatch True to match any part not just the beginning
14144      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14145      */
14146     query : function(property, value, anyMatch){
14147         var fn = this.createFilterFn(property, value, anyMatch);
14148         return fn ? this.queryBy(fn) : this.data.clone();
14149     },
14150
14151     /**
14152      * Query by a function. The specified function will be called with each
14153      * record in this data source. If the function returns true the record is included
14154      * in the results.
14155      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14156      * @param {Object} scope (optional) The scope of the function (defaults to this)
14157       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14158      **/
14159     queryBy : function(fn, scope){
14160         var data = this.snapshot || this.data;
14161         return data.filterBy(fn, scope||this);
14162     },
14163
14164     /**
14165      * Collects unique values for a particular dataIndex from this store.
14166      * @param {String} dataIndex The property to collect
14167      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14168      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14169      * @return {Array} An array of the unique values
14170      **/
14171     collect : function(dataIndex, allowNull, bypassFilter){
14172         var d = (bypassFilter === true && this.snapshot) ?
14173                 this.snapshot.items : this.data.items;
14174         var v, sv, r = [], l = {};
14175         for(var i = 0, len = d.length; i < len; i++){
14176             v = d[i].data[dataIndex];
14177             sv = String(v);
14178             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14179                 l[sv] = true;
14180                 r[r.length] = v;
14181             }
14182         }
14183         return r;
14184     },
14185
14186     /**
14187      * Revert to a view of the Record cache with no filtering applied.
14188      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14189      */
14190     clearFilter : function(suppressEvent){
14191         if(this.snapshot && this.snapshot != this.data){
14192             this.data = this.snapshot;
14193             delete this.snapshot;
14194             if(suppressEvent !== true){
14195                 this.fireEvent("datachanged", this);
14196             }
14197         }
14198     },
14199
14200     // private
14201     afterEdit : function(record){
14202         if(this.modified.indexOf(record) == -1){
14203             this.modified.push(record);
14204         }
14205         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14206     },
14207     
14208     // private
14209     afterReject : function(record){
14210         this.modified.remove(record);
14211         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14212     },
14213
14214     // private
14215     afterCommit : function(record){
14216         this.modified.remove(record);
14217         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14218     },
14219
14220     /**
14221      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14222      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14223      */
14224     commitChanges : function(){
14225         var m = this.modified.slice(0);
14226         this.modified = [];
14227         for(var i = 0, len = m.length; i < len; i++){
14228             m[i].commit();
14229         }
14230     },
14231
14232     /**
14233      * Cancel outstanding changes on all changed records.
14234      */
14235     rejectChanges : function(){
14236         var m = this.modified.slice(0);
14237         this.modified = [];
14238         for(var i = 0, len = m.length; i < len; i++){
14239             m[i].reject();
14240         }
14241     },
14242
14243     onMetaChange : function(meta, rtype, o){
14244         this.recordType = rtype;
14245         this.fields = rtype.prototype.fields;
14246         delete this.snapshot;
14247         this.sortInfo = meta.sortInfo || this.sortInfo;
14248         this.modified = [];
14249         this.fireEvent('metachange', this, this.reader.meta);
14250     },
14251     
14252     moveIndex : function(data, type)
14253     {
14254         var index = this.indexOf(data);
14255         
14256         var newIndex = index + type;
14257         
14258         this.remove(data);
14259         
14260         this.insert(newIndex, data);
14261         
14262     }
14263 });/*
14264  * Based on:
14265  * Ext JS Library 1.1.1
14266  * Copyright(c) 2006-2007, Ext JS, LLC.
14267  *
14268  * Originally Released Under LGPL - original licence link has changed is not relivant.
14269  *
14270  * Fork - LGPL
14271  * <script type="text/javascript">
14272  */
14273
14274 /**
14275  * @class Roo.data.SimpleStore
14276  * @extends Roo.data.Store
14277  * Small helper class to make creating Stores from Array data easier.
14278  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14279  * @cfg {Array} fields An array of field definition objects, or field name strings.
14280  * @cfg {Object} an existing reader (eg. copied from another store)
14281  * @cfg {Array} data The multi-dimensional array of data
14282  * @constructor
14283  * @param {Object} config
14284  */
14285 Roo.data.SimpleStore = function(config)
14286 {
14287     Roo.data.SimpleStore.superclass.constructor.call(this, {
14288         isLocal : true,
14289         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14290                 id: config.id
14291             },
14292             Roo.data.Record.create(config.fields)
14293         ),
14294         proxy : new Roo.data.MemoryProxy(config.data)
14295     });
14296     this.load();
14297 };
14298 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14299  * Based on:
14300  * Ext JS Library 1.1.1
14301  * Copyright(c) 2006-2007, Ext JS, LLC.
14302  *
14303  * Originally Released Under LGPL - original licence link has changed is not relivant.
14304  *
14305  * Fork - LGPL
14306  * <script type="text/javascript">
14307  */
14308
14309 /**
14310 /**
14311  * @extends Roo.data.Store
14312  * @class Roo.data.JsonStore
14313  * Small helper class to make creating Stores for JSON data easier. <br/>
14314 <pre><code>
14315 var store = new Roo.data.JsonStore({
14316     url: 'get-images.php',
14317     root: 'images',
14318     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14319 });
14320 </code></pre>
14321  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14322  * JsonReader and HttpProxy (unless inline data is provided).</b>
14323  * @cfg {Array} fields An array of field definition objects, or field name strings.
14324  * @constructor
14325  * @param {Object} config
14326  */
14327 Roo.data.JsonStore = function(c){
14328     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14329         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14330         reader: new Roo.data.JsonReader(c, c.fields)
14331     }));
14332 };
14333 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14334  * Based on:
14335  * Ext JS Library 1.1.1
14336  * Copyright(c) 2006-2007, Ext JS, LLC.
14337  *
14338  * Originally Released Under LGPL - original licence link has changed is not relivant.
14339  *
14340  * Fork - LGPL
14341  * <script type="text/javascript">
14342  */
14343
14344  
14345 Roo.data.Field = function(config){
14346     if(typeof config == "string"){
14347         config = {name: config};
14348     }
14349     Roo.apply(this, config);
14350     
14351     if(!this.type){
14352         this.type = "auto";
14353     }
14354     
14355     var st = Roo.data.SortTypes;
14356     // named sortTypes are supported, here we look them up
14357     if(typeof this.sortType == "string"){
14358         this.sortType = st[this.sortType];
14359     }
14360     
14361     // set default sortType for strings and dates
14362     if(!this.sortType){
14363         switch(this.type){
14364             case "string":
14365                 this.sortType = st.asUCString;
14366                 break;
14367             case "date":
14368                 this.sortType = st.asDate;
14369                 break;
14370             default:
14371                 this.sortType = st.none;
14372         }
14373     }
14374
14375     // define once
14376     var stripRe = /[\$,%]/g;
14377
14378     // prebuilt conversion function for this field, instead of
14379     // switching every time we're reading a value
14380     if(!this.convert){
14381         var cv, dateFormat = this.dateFormat;
14382         switch(this.type){
14383             case "":
14384             case "auto":
14385             case undefined:
14386                 cv = function(v){ return v; };
14387                 break;
14388             case "string":
14389                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14390                 break;
14391             case "int":
14392                 cv = function(v){
14393                     return v !== undefined && v !== null && v !== '' ?
14394                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14395                     };
14396                 break;
14397             case "float":
14398                 cv = function(v){
14399                     return v !== undefined && v !== null && v !== '' ?
14400                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14401                     };
14402                 break;
14403             case "bool":
14404             case "boolean":
14405                 cv = function(v){ return v === true || v === "true" || v == 1; };
14406                 break;
14407             case "date":
14408                 cv = function(v){
14409                     if(!v){
14410                         return '';
14411                     }
14412                     if(v instanceof Date){
14413                         return v;
14414                     }
14415                     if(dateFormat){
14416                         if(dateFormat == "timestamp"){
14417                             return new Date(v*1000);
14418                         }
14419                         return Date.parseDate(v, dateFormat);
14420                     }
14421                     var parsed = Date.parse(v);
14422                     return parsed ? new Date(parsed) : null;
14423                 };
14424              break;
14425             
14426         }
14427         this.convert = cv;
14428     }
14429 };
14430
14431 Roo.data.Field.prototype = {
14432     dateFormat: null,
14433     defaultValue: "",
14434     mapping: null,
14435     sortType : null,
14436     sortDir : "ASC"
14437 };/*
14438  * Based on:
14439  * Ext JS Library 1.1.1
14440  * Copyright(c) 2006-2007, Ext JS, LLC.
14441  *
14442  * Originally Released Under LGPL - original licence link has changed is not relivant.
14443  *
14444  * Fork - LGPL
14445  * <script type="text/javascript">
14446  */
14447  
14448 // Base class for reading structured data from a data source.  This class is intended to be
14449 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14450
14451 /**
14452  * @class Roo.data.DataReader
14453  * Base class for reading structured data from a data source.  This class is intended to be
14454  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14455  */
14456
14457 Roo.data.DataReader = function(meta, recordType){
14458     
14459     this.meta = meta;
14460     
14461     this.recordType = recordType instanceof Array ? 
14462         Roo.data.Record.create(recordType) : recordType;
14463 };
14464
14465 Roo.data.DataReader.prototype = {
14466     
14467     
14468     readerType : 'Data',
14469      /**
14470      * Create an empty record
14471      * @param {Object} data (optional) - overlay some values
14472      * @return {Roo.data.Record} record created.
14473      */
14474     newRow :  function(d) {
14475         var da =  {};
14476         this.recordType.prototype.fields.each(function(c) {
14477             switch( c.type) {
14478                 case 'int' : da[c.name] = 0; break;
14479                 case 'date' : da[c.name] = new Date(); break;
14480                 case 'float' : da[c.name] = 0.0; break;
14481                 case 'boolean' : da[c.name] = false; break;
14482                 default : da[c.name] = ""; break;
14483             }
14484             
14485         });
14486         return new this.recordType(Roo.apply(da, d));
14487     }
14488     
14489     
14490 };/*
14491  * Based on:
14492  * Ext JS Library 1.1.1
14493  * Copyright(c) 2006-2007, Ext JS, LLC.
14494  *
14495  * Originally Released Under LGPL - original licence link has changed is not relivant.
14496  *
14497  * Fork - LGPL
14498  * <script type="text/javascript">
14499  */
14500
14501 /**
14502  * @class Roo.data.DataProxy
14503  * @extends Roo.data.Observable
14504  * This class is an abstract base class for implementations which provide retrieval of
14505  * unformatted data objects.<br>
14506  * <p>
14507  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14508  * (of the appropriate type which knows how to parse the data object) to provide a block of
14509  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14510  * <p>
14511  * Custom implementations must implement the load method as described in
14512  * {@link Roo.data.HttpProxy#load}.
14513  */
14514 Roo.data.DataProxy = function(){
14515     this.addEvents({
14516         /**
14517          * @event beforeload
14518          * Fires before a network request is made to retrieve a data object.
14519          * @param {Object} This DataProxy object.
14520          * @param {Object} params The params parameter to the load function.
14521          */
14522         beforeload : true,
14523         /**
14524          * @event load
14525          * Fires before the load method's callback is called.
14526          * @param {Object} This DataProxy object.
14527          * @param {Object} o The data object.
14528          * @param {Object} arg The callback argument object passed to the load function.
14529          */
14530         load : true,
14531         /**
14532          * @event loadexception
14533          * Fires if an Exception occurs during data retrieval.
14534          * @param {Object} This DataProxy object.
14535          * @param {Object} o The data object.
14536          * @param {Object} arg The callback argument object passed to the load function.
14537          * @param {Object} e The Exception.
14538          */
14539         loadexception : true
14540     });
14541     Roo.data.DataProxy.superclass.constructor.call(this);
14542 };
14543
14544 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14545
14546     /**
14547      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14548      */
14549 /*
14550  * Based on:
14551  * Ext JS Library 1.1.1
14552  * Copyright(c) 2006-2007, Ext JS, LLC.
14553  *
14554  * Originally Released Under LGPL - original licence link has changed is not relivant.
14555  *
14556  * Fork - LGPL
14557  * <script type="text/javascript">
14558  */
14559 /**
14560  * @class Roo.data.MemoryProxy
14561  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14562  * to the Reader when its load method is called.
14563  * @constructor
14564  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14565  */
14566 Roo.data.MemoryProxy = function(data){
14567     if (data.data) {
14568         data = data.data;
14569     }
14570     Roo.data.MemoryProxy.superclass.constructor.call(this);
14571     this.data = data;
14572 };
14573
14574 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14575     
14576     /**
14577      * Load data from the requested source (in this case an in-memory
14578      * data object passed to the constructor), read the data object into
14579      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14580      * process that block using the passed callback.
14581      * @param {Object} params This parameter is not used by the MemoryProxy class.
14582      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14583      * object into a block of Roo.data.Records.
14584      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14585      * The function must be passed <ul>
14586      * <li>The Record block object</li>
14587      * <li>The "arg" argument from the load function</li>
14588      * <li>A boolean success indicator</li>
14589      * </ul>
14590      * @param {Object} scope The scope in which to call the callback
14591      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14592      */
14593     load : function(params, reader, callback, scope, arg){
14594         params = params || {};
14595         var result;
14596         try {
14597             result = reader.readRecords(params.data ? params.data :this.data);
14598         }catch(e){
14599             this.fireEvent("loadexception", this, arg, null, e);
14600             callback.call(scope, null, arg, false);
14601             return;
14602         }
14603         callback.call(scope, result, arg, true);
14604     },
14605     
14606     // private
14607     update : function(params, records){
14608         
14609     }
14610 });/*
14611  * Based on:
14612  * Ext JS Library 1.1.1
14613  * Copyright(c) 2006-2007, Ext JS, LLC.
14614  *
14615  * Originally Released Under LGPL - original licence link has changed is not relivant.
14616  *
14617  * Fork - LGPL
14618  * <script type="text/javascript">
14619  */
14620 /**
14621  * @class Roo.data.HttpProxy
14622  * @extends Roo.data.DataProxy
14623  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14624  * configured to reference a certain URL.<br><br>
14625  * <p>
14626  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14627  * from which the running page was served.<br><br>
14628  * <p>
14629  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14630  * <p>
14631  * Be aware that to enable the browser to parse an XML document, the server must set
14632  * the Content-Type header in the HTTP response to "text/xml".
14633  * @constructor
14634  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14635  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14636  * will be used to make the request.
14637  */
14638 Roo.data.HttpProxy = function(conn){
14639     Roo.data.HttpProxy.superclass.constructor.call(this);
14640     // is conn a conn config or a real conn?
14641     this.conn = conn;
14642     this.useAjax = !conn || !conn.events;
14643   
14644 };
14645
14646 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14647     // thse are take from connection...
14648     
14649     /**
14650      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14651      */
14652     /**
14653      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14654      * extra parameters to each request made by this object. (defaults to undefined)
14655      */
14656     /**
14657      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14658      *  to each request made by this object. (defaults to undefined)
14659      */
14660     /**
14661      * @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)
14662      */
14663     /**
14664      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14665      */
14666      /**
14667      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14668      * @type Boolean
14669      */
14670   
14671
14672     /**
14673      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14674      * @type Boolean
14675      */
14676     /**
14677      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14678      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14679      * a finer-grained basis than the DataProxy events.
14680      */
14681     getConnection : function(){
14682         return this.useAjax ? Roo.Ajax : this.conn;
14683     },
14684
14685     /**
14686      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14687      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14688      * process that block using the passed callback.
14689      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14690      * for the request to the remote server.
14691      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14692      * object into a block of Roo.data.Records.
14693      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14694      * The function must be passed <ul>
14695      * <li>The Record block object</li>
14696      * <li>The "arg" argument from the load function</li>
14697      * <li>A boolean success indicator</li>
14698      * </ul>
14699      * @param {Object} scope The scope in which to call the callback
14700      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14701      */
14702     load : function(params, reader, callback, scope, arg){
14703         if(this.fireEvent("beforeload", this, params) !== false){
14704             var  o = {
14705                 params : params || {},
14706                 request: {
14707                     callback : callback,
14708                     scope : scope,
14709                     arg : arg
14710                 },
14711                 reader: reader,
14712                 callback : this.loadResponse,
14713                 scope: this
14714             };
14715             if(this.useAjax){
14716                 Roo.applyIf(o, this.conn);
14717                 if(this.activeRequest){
14718                     Roo.Ajax.abort(this.activeRequest);
14719                 }
14720                 this.activeRequest = Roo.Ajax.request(o);
14721             }else{
14722                 this.conn.request(o);
14723             }
14724         }else{
14725             callback.call(scope||this, null, arg, false);
14726         }
14727     },
14728
14729     // private
14730     loadResponse : function(o, success, response){
14731         delete this.activeRequest;
14732         if(!success){
14733             this.fireEvent("loadexception", this, o, response);
14734             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14735             return;
14736         }
14737         var result;
14738         try {
14739             result = o.reader.read(response);
14740         }catch(e){
14741             this.fireEvent("loadexception", this, o, response, e);
14742             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14743             return;
14744         }
14745         
14746         this.fireEvent("load", this, o, o.request.arg);
14747         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14748     },
14749
14750     // private
14751     update : function(dataSet){
14752
14753     },
14754
14755     // private
14756     updateResponse : function(dataSet){
14757
14758     }
14759 });/*
14760  * Based on:
14761  * Ext JS Library 1.1.1
14762  * Copyright(c) 2006-2007, Ext JS, LLC.
14763  *
14764  * Originally Released Under LGPL - original licence link has changed is not relivant.
14765  *
14766  * Fork - LGPL
14767  * <script type="text/javascript">
14768  */
14769
14770 /**
14771  * @class Roo.data.ScriptTagProxy
14772  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14773  * other than the originating domain of the running page.<br><br>
14774  * <p>
14775  * <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
14776  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14777  * <p>
14778  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14779  * source code that is used as the source inside a &lt;script> tag.<br><br>
14780  * <p>
14781  * In order for the browser to process the returned data, the server must wrap the data object
14782  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14783  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14784  * depending on whether the callback name was passed:
14785  * <p>
14786  * <pre><code>
14787 boolean scriptTag = false;
14788 String cb = request.getParameter("callback");
14789 if (cb != null) {
14790     scriptTag = true;
14791     response.setContentType("text/javascript");
14792 } else {
14793     response.setContentType("application/x-json");
14794 }
14795 Writer out = response.getWriter();
14796 if (scriptTag) {
14797     out.write(cb + "(");
14798 }
14799 out.print(dataBlock.toJsonString());
14800 if (scriptTag) {
14801     out.write(");");
14802 }
14803 </pre></code>
14804  *
14805  * @constructor
14806  * @param {Object} config A configuration object.
14807  */
14808 Roo.data.ScriptTagProxy = function(config){
14809     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14810     Roo.apply(this, config);
14811     this.head = document.getElementsByTagName("head")[0];
14812 };
14813
14814 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14815
14816 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14817     /**
14818      * @cfg {String} url The URL from which to request the data object.
14819      */
14820     /**
14821      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14822      */
14823     timeout : 30000,
14824     /**
14825      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14826      * the server the name of the callback function set up by the load call to process the returned data object.
14827      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14828      * javascript output which calls this named function passing the data object as its only parameter.
14829      */
14830     callbackParam : "callback",
14831     /**
14832      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14833      * name to the request.
14834      */
14835     nocache : true,
14836
14837     /**
14838      * Load data from the configured URL, read the data object into
14839      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14840      * process that block using the passed callback.
14841      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14842      * for the request to the remote server.
14843      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14844      * object into a block of Roo.data.Records.
14845      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14846      * The function must be passed <ul>
14847      * <li>The Record block object</li>
14848      * <li>The "arg" argument from the load function</li>
14849      * <li>A boolean success indicator</li>
14850      * </ul>
14851      * @param {Object} scope The scope in which to call the callback
14852      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14853      */
14854     load : function(params, reader, callback, scope, arg){
14855         if(this.fireEvent("beforeload", this, params) !== false){
14856
14857             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14858
14859             var url = this.url;
14860             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14861             if(this.nocache){
14862                 url += "&_dc=" + (new Date().getTime());
14863             }
14864             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14865             var trans = {
14866                 id : transId,
14867                 cb : "stcCallback"+transId,
14868                 scriptId : "stcScript"+transId,
14869                 params : params,
14870                 arg : arg,
14871                 url : url,
14872                 callback : callback,
14873                 scope : scope,
14874                 reader : reader
14875             };
14876             var conn = this;
14877
14878             window[trans.cb] = function(o){
14879                 conn.handleResponse(o, trans);
14880             };
14881
14882             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14883
14884             if(this.autoAbort !== false){
14885                 this.abort();
14886             }
14887
14888             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14889
14890             var script = document.createElement("script");
14891             script.setAttribute("src", url);
14892             script.setAttribute("type", "text/javascript");
14893             script.setAttribute("id", trans.scriptId);
14894             this.head.appendChild(script);
14895
14896             this.trans = trans;
14897         }else{
14898             callback.call(scope||this, null, arg, false);
14899         }
14900     },
14901
14902     // private
14903     isLoading : function(){
14904         return this.trans ? true : false;
14905     },
14906
14907     /**
14908      * Abort the current server request.
14909      */
14910     abort : function(){
14911         if(this.isLoading()){
14912             this.destroyTrans(this.trans);
14913         }
14914     },
14915
14916     // private
14917     destroyTrans : function(trans, isLoaded){
14918         this.head.removeChild(document.getElementById(trans.scriptId));
14919         clearTimeout(trans.timeoutId);
14920         if(isLoaded){
14921             window[trans.cb] = undefined;
14922             try{
14923                 delete window[trans.cb];
14924             }catch(e){}
14925         }else{
14926             // if hasn't been loaded, wait for load to remove it to prevent script error
14927             window[trans.cb] = function(){
14928                 window[trans.cb] = undefined;
14929                 try{
14930                     delete window[trans.cb];
14931                 }catch(e){}
14932             };
14933         }
14934     },
14935
14936     // private
14937     handleResponse : function(o, trans){
14938         this.trans = false;
14939         this.destroyTrans(trans, true);
14940         var result;
14941         try {
14942             result = trans.reader.readRecords(o);
14943         }catch(e){
14944             this.fireEvent("loadexception", this, o, trans.arg, e);
14945             trans.callback.call(trans.scope||window, null, trans.arg, false);
14946             return;
14947         }
14948         this.fireEvent("load", this, o, trans.arg);
14949         trans.callback.call(trans.scope||window, result, trans.arg, true);
14950     },
14951
14952     // private
14953     handleFailure : function(trans){
14954         this.trans = false;
14955         this.destroyTrans(trans, false);
14956         this.fireEvent("loadexception", this, null, trans.arg);
14957         trans.callback.call(trans.scope||window, null, trans.arg, false);
14958     }
14959 });/*
14960  * Based on:
14961  * Ext JS Library 1.1.1
14962  * Copyright(c) 2006-2007, Ext JS, LLC.
14963  *
14964  * Originally Released Under LGPL - original licence link has changed is not relivant.
14965  *
14966  * Fork - LGPL
14967  * <script type="text/javascript">
14968  */
14969
14970 /**
14971  * @class Roo.data.JsonReader
14972  * @extends Roo.data.DataReader
14973  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14974  * based on mappings in a provided Roo.data.Record constructor.
14975  * 
14976  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14977  * in the reply previously. 
14978  * 
14979  * <p>
14980  * Example code:
14981  * <pre><code>
14982 var RecordDef = Roo.data.Record.create([
14983     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14984     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14985 ]);
14986 var myReader = new Roo.data.JsonReader({
14987     totalProperty: "results",    // The property which contains the total dataset size (optional)
14988     root: "rows",                // The property which contains an Array of row objects
14989     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14990 }, RecordDef);
14991 </code></pre>
14992  * <p>
14993  * This would consume a JSON file like this:
14994  * <pre><code>
14995 { 'results': 2, 'rows': [
14996     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14997     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14998 }
14999 </code></pre>
15000  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15001  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15002  * paged from the remote server.
15003  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15004  * @cfg {String} root name of the property which contains the Array of row objects.
15005  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15006  * @cfg {Array} fields Array of field definition objects
15007  * @constructor
15008  * Create a new JsonReader
15009  * @param {Object} meta Metadata configuration options
15010  * @param {Object} recordType Either an Array of field definition objects,
15011  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15012  */
15013 Roo.data.JsonReader = function(meta, recordType){
15014     
15015     meta = meta || {};
15016     // set some defaults:
15017     Roo.applyIf(meta, {
15018         totalProperty: 'total',
15019         successProperty : 'success',
15020         root : 'data',
15021         id : 'id'
15022     });
15023     
15024     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15025 };
15026 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15027     
15028     readerType : 'Json',
15029     
15030     /**
15031      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15032      * Used by Store query builder to append _requestMeta to params.
15033      * 
15034      */
15035     metaFromRemote : false,
15036     /**
15037      * This method is only used by a DataProxy which has retrieved data from a remote server.
15038      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15039      * @return {Object} data A data block which is used by an Roo.data.Store object as
15040      * a cache of Roo.data.Records.
15041      */
15042     read : function(response){
15043         var json = response.responseText;
15044        
15045         var o = /* eval:var:o */ eval("("+json+")");
15046         if(!o) {
15047             throw {message: "JsonReader.read: Json object not found"};
15048         }
15049         
15050         if(o.metaData){
15051             
15052             delete this.ef;
15053             this.metaFromRemote = true;
15054             this.meta = o.metaData;
15055             this.recordType = Roo.data.Record.create(o.metaData.fields);
15056             this.onMetaChange(this.meta, this.recordType, o);
15057         }
15058         return this.readRecords(o);
15059     },
15060
15061     // private function a store will implement
15062     onMetaChange : function(meta, recordType, o){
15063
15064     },
15065
15066     /**
15067          * @ignore
15068          */
15069     simpleAccess: function(obj, subsc) {
15070         return obj[subsc];
15071     },
15072
15073         /**
15074          * @ignore
15075          */
15076     getJsonAccessor: function(){
15077         var re = /[\[\.]/;
15078         return function(expr) {
15079             try {
15080                 return(re.test(expr))
15081                     ? new Function("obj", "return obj." + expr)
15082                     : function(obj){
15083                         return obj[expr];
15084                     };
15085             } catch(e){}
15086             return Roo.emptyFn;
15087         };
15088     }(),
15089
15090     /**
15091      * Create a data block containing Roo.data.Records from an XML document.
15092      * @param {Object} o An object which contains an Array of row objects in the property specified
15093      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15094      * which contains the total size of the dataset.
15095      * @return {Object} data A data block which is used by an Roo.data.Store object as
15096      * a cache of Roo.data.Records.
15097      */
15098     readRecords : function(o){
15099         /**
15100          * After any data loads, the raw JSON data is available for further custom processing.
15101          * @type Object
15102          */
15103         this.o = o;
15104         var s = this.meta, Record = this.recordType,
15105             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15106
15107 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15108         if (!this.ef) {
15109             if(s.totalProperty) {
15110                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15111                 }
15112                 if(s.successProperty) {
15113                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15114                 }
15115                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15116                 if (s.id) {
15117                         var g = this.getJsonAccessor(s.id);
15118                         this.getId = function(rec) {
15119                                 var r = g(rec);  
15120                                 return (r === undefined || r === "") ? null : r;
15121                         };
15122                 } else {
15123                         this.getId = function(){return null;};
15124                 }
15125             this.ef = [];
15126             for(var jj = 0; jj < fl; jj++){
15127                 f = fi[jj];
15128                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15129                 this.ef[jj] = this.getJsonAccessor(map);
15130             }
15131         }
15132
15133         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15134         if(s.totalProperty){
15135             var vt = parseInt(this.getTotal(o), 10);
15136             if(!isNaN(vt)){
15137                 totalRecords = vt;
15138             }
15139         }
15140         if(s.successProperty){
15141             var vs = this.getSuccess(o);
15142             if(vs === false || vs === 'false'){
15143                 success = false;
15144             }
15145         }
15146         var records = [];
15147         for(var i = 0; i < c; i++){
15148                 var n = root[i];
15149             var values = {};
15150             var id = this.getId(n);
15151             for(var j = 0; j < fl; j++){
15152                 f = fi[j];
15153             var v = this.ef[j](n);
15154             if (!f.convert) {
15155                 Roo.log('missing convert for ' + f.name);
15156                 Roo.log(f);
15157                 continue;
15158             }
15159             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15160             }
15161             var record = new Record(values, id);
15162             record.json = n;
15163             records[i] = record;
15164         }
15165         return {
15166             raw : o,
15167             success : success,
15168             records : records,
15169             totalRecords : totalRecords
15170         };
15171     },
15172     // used when loading children.. @see loadDataFromChildren
15173     toLoadData: function(rec)
15174     {
15175         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15176         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15177         return { data : data, total : data.length };
15178         
15179     }
15180 });/*
15181  * Based on:
15182  * Ext JS Library 1.1.1
15183  * Copyright(c) 2006-2007, Ext JS, LLC.
15184  *
15185  * Originally Released Under LGPL - original licence link has changed is not relivant.
15186  *
15187  * Fork - LGPL
15188  * <script type="text/javascript">
15189  */
15190
15191 /**
15192  * @class Roo.data.ArrayReader
15193  * @extends Roo.data.DataReader
15194  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15195  * Each element of that Array represents a row of data fields. The
15196  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15197  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15198  * <p>
15199  * Example code:.
15200  * <pre><code>
15201 var RecordDef = Roo.data.Record.create([
15202     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15203     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15204 ]);
15205 var myReader = new Roo.data.ArrayReader({
15206     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15207 }, RecordDef);
15208 </code></pre>
15209  * <p>
15210  * This would consume an Array like this:
15211  * <pre><code>
15212 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15213   </code></pre>
15214  
15215  * @constructor
15216  * Create a new JsonReader
15217  * @param {Object} meta Metadata configuration options.
15218  * @param {Object|Array} recordType Either an Array of field definition objects
15219  * 
15220  * @cfg {Array} fields Array of field definition objects
15221  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15222  * as specified to {@link Roo.data.Record#create},
15223  * or an {@link Roo.data.Record} object
15224  *
15225  * 
15226  * created using {@link Roo.data.Record#create}.
15227  */
15228 Roo.data.ArrayReader = function(meta, recordType)
15229 {    
15230     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15231 };
15232
15233 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15234     
15235       /**
15236      * Create a data block containing Roo.data.Records from an XML document.
15237      * @param {Object} o An Array of row objects which represents the dataset.
15238      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15239      * a cache of Roo.data.Records.
15240      */
15241     readRecords : function(o)
15242     {
15243         var sid = this.meta ? this.meta.id : null;
15244         var recordType = this.recordType, fields = recordType.prototype.fields;
15245         var records = [];
15246         var root = o;
15247         for(var i = 0; i < root.length; i++){
15248             var n = root[i];
15249             var values = {};
15250             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15251             for(var j = 0, jlen = fields.length; j < jlen; j++){
15252                 var f = fields.items[j];
15253                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15254                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15255                 v = f.convert(v);
15256                 values[f.name] = v;
15257             }
15258             var record = new recordType(values, id);
15259             record.json = n;
15260             records[records.length] = record;
15261         }
15262         return {
15263             records : records,
15264             totalRecords : records.length
15265         };
15266     },
15267     // used when loading children.. @see loadDataFromChildren
15268     toLoadData: function(rec)
15269     {
15270         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15271         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15272         
15273     }
15274     
15275     
15276 });/*
15277  * - LGPL
15278  * * 
15279  */
15280
15281 /**
15282  * @class Roo.bootstrap.ComboBox
15283  * @extends Roo.bootstrap.TriggerField
15284  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15285  * @cfg {Boolean} append (true|false) default false
15286  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15287  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15288  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15289  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15290  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15291  * @cfg {Boolean} animate default true
15292  * @cfg {Boolean} emptyResultText only for touch device
15293  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15294  * @cfg {String} emptyTitle default ''
15295  * @cfg {Number} width fixed with? experimental
15296  * @constructor
15297  * Create a new ComboBox.
15298  * @param {Object} config Configuration options
15299  */
15300 Roo.bootstrap.ComboBox = function(config){
15301     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15302     this.addEvents({
15303         /**
15304          * @event expand
15305          * Fires when the dropdown list is expanded
15306         * @param {Roo.bootstrap.ComboBox} combo This combo box
15307         */
15308         'expand' : true,
15309         /**
15310          * @event collapse
15311          * Fires when the dropdown list is collapsed
15312         * @param {Roo.bootstrap.ComboBox} combo This combo box
15313         */
15314         'collapse' : true,
15315         /**
15316          * @event beforeselect
15317          * Fires before a list item is selected. Return false to cancel the selection.
15318         * @param {Roo.bootstrap.ComboBox} combo This combo box
15319         * @param {Roo.data.Record} record The data record returned from the underlying store
15320         * @param {Number} index The index of the selected item in the dropdown list
15321         */
15322         'beforeselect' : true,
15323         /**
15324          * @event select
15325          * Fires when a list item is selected
15326         * @param {Roo.bootstrap.ComboBox} combo This combo box
15327         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15328         * @param {Number} index The index of the selected item in the dropdown list
15329         */
15330         'select' : true,
15331         /**
15332          * @event beforequery
15333          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15334          * The event object passed has these properties:
15335         * @param {Roo.bootstrap.ComboBox} combo This combo box
15336         * @param {String} query The query
15337         * @param {Boolean} forceAll true to force "all" query
15338         * @param {Boolean} cancel true to cancel the query
15339         * @param {Object} e The query event object
15340         */
15341         'beforequery': true,
15342          /**
15343          * @event add
15344          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15345         * @param {Roo.bootstrap.ComboBox} combo This combo box
15346         */
15347         'add' : true,
15348         /**
15349          * @event edit
15350          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15351         * @param {Roo.bootstrap.ComboBox} combo This combo box
15352         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15353         */
15354         'edit' : true,
15355         /**
15356          * @event remove
15357          * Fires when the remove value from the combobox array
15358         * @param {Roo.bootstrap.ComboBox} combo This combo box
15359         */
15360         'remove' : true,
15361         /**
15362          * @event afterremove
15363          * Fires when the remove value from the combobox array
15364         * @param {Roo.bootstrap.ComboBox} combo This combo box
15365         */
15366         'afterremove' : true,
15367         /**
15368          * @event specialfilter
15369          * Fires when specialfilter
15370             * @param {Roo.bootstrap.ComboBox} combo This combo box
15371             */
15372         'specialfilter' : true,
15373         /**
15374          * @event tick
15375          * Fires when tick the element
15376             * @param {Roo.bootstrap.ComboBox} combo This combo box
15377             */
15378         'tick' : true,
15379         /**
15380          * @event touchviewdisplay
15381          * Fires when touch view require special display (default is using displayField)
15382             * @param {Roo.bootstrap.ComboBox} combo This combo box
15383             * @param {Object} cfg set html .
15384             */
15385         'touchviewdisplay' : true
15386         
15387     });
15388     
15389     this.item = [];
15390     this.tickItems = [];
15391     
15392     this.selectedIndex = -1;
15393     if(this.mode == 'local'){
15394         if(config.queryDelay === undefined){
15395             this.queryDelay = 10;
15396         }
15397         if(config.minChars === undefined){
15398             this.minChars = 0;
15399         }
15400     }
15401 };
15402
15403 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15404      
15405     /**
15406      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15407      * rendering into an Roo.Editor, defaults to false)
15408      */
15409     /**
15410      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15411      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15412      */
15413     /**
15414      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15415      */
15416     /**
15417      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15418      * the dropdown list (defaults to undefined, with no header element)
15419      */
15420
15421      /**
15422      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15423      */
15424      
15425      /**
15426      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15427      */
15428     listWidth: undefined,
15429     /**
15430      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15431      * mode = 'remote' or 'text' if mode = 'local')
15432      */
15433     displayField: undefined,
15434     
15435     /**
15436      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15437      * mode = 'remote' or 'value' if mode = 'local'). 
15438      * Note: use of a valueField requires the user make a selection
15439      * in order for a value to be mapped.
15440      */
15441     valueField: undefined,
15442     /**
15443      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15444      */
15445     modalTitle : '',
15446     
15447     /**
15448      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15449      * field's data value (defaults to the underlying DOM element's name)
15450      */
15451     hiddenName: undefined,
15452     /**
15453      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15454      */
15455     listClass: '',
15456     /**
15457      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15458      */
15459     selectedClass: 'active',
15460     
15461     /**
15462      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15463      */
15464     shadow:'sides',
15465     /**
15466      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15467      * anchor positions (defaults to 'tl-bl')
15468      */
15469     listAlign: 'tl-bl?',
15470     /**
15471      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15472      */
15473     maxHeight: 300,
15474     /**
15475      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15476      * query specified by the allQuery config option (defaults to 'query')
15477      */
15478     triggerAction: 'query',
15479     /**
15480      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15481      * (defaults to 4, does not apply if editable = false)
15482      */
15483     minChars : 4,
15484     /**
15485      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15486      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15487      */
15488     typeAhead: false,
15489     /**
15490      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15491      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15492      */
15493     queryDelay: 500,
15494     /**
15495      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15496      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15497      */
15498     pageSize: 0,
15499     /**
15500      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15501      * when editable = true (defaults to false)
15502      */
15503     selectOnFocus:false,
15504     /**
15505      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15506      */
15507     queryParam: 'query',
15508     /**
15509      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15510      * when mode = 'remote' (defaults to 'Loading...')
15511      */
15512     loadingText: 'Loading...',
15513     /**
15514      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15515      */
15516     resizable: false,
15517     /**
15518      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15519      */
15520     handleHeight : 8,
15521     /**
15522      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15523      * traditional select (defaults to true)
15524      */
15525     editable: true,
15526     /**
15527      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15528      */
15529     allQuery: '',
15530     /**
15531      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15532      */
15533     mode: 'remote',
15534     /**
15535      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15536      * listWidth has a higher value)
15537      */
15538     minListWidth : 70,
15539     /**
15540      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15541      * allow the user to set arbitrary text into the field (defaults to false)
15542      */
15543     forceSelection:false,
15544     /**
15545      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15546      * if typeAhead = true (defaults to 250)
15547      */
15548     typeAheadDelay : 250,
15549     /**
15550      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15551      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15552      */
15553     valueNotFoundText : undefined,
15554     /**
15555      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15556      */
15557     blockFocus : false,
15558     
15559     /**
15560      * @cfg {Boolean} disableClear Disable showing of clear button.
15561      */
15562     disableClear : false,
15563     /**
15564      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15565      */
15566     alwaysQuery : false,
15567     
15568     /**
15569      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15570      */
15571     multiple : false,
15572     
15573     /**
15574      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15575      */
15576     invalidClass : "has-warning",
15577     
15578     /**
15579      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15580      */
15581     validClass : "has-success",
15582     
15583     /**
15584      * @cfg {Boolean} specialFilter (true|false) special filter default false
15585      */
15586     specialFilter : false,
15587     
15588     /**
15589      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15590      */
15591     mobileTouchView : true,
15592     
15593     /**
15594      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15595      */
15596     useNativeIOS : false,
15597     
15598     /**
15599      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15600      */
15601     mobile_restrict_height : false,
15602     
15603     ios_options : false,
15604     
15605     //private
15606     addicon : false,
15607     editicon: false,
15608     
15609     page: 0,
15610     hasQuery: false,
15611     append: false,
15612     loadNext: false,
15613     autoFocus : true,
15614     tickable : false,
15615     btnPosition : 'right',
15616     triggerList : true,
15617     showToggleBtn : true,
15618     animate : true,
15619     emptyResultText: 'Empty',
15620     triggerText : 'Select',
15621     emptyTitle : '',
15622     width : false,
15623     
15624     // element that contains real text value.. (when hidden is used..)
15625     
15626     getAutoCreate : function()
15627     {   
15628         var cfg = false;
15629         //render
15630         /*
15631          * Render classic select for iso
15632          */
15633         
15634         if(Roo.isIOS && this.useNativeIOS){
15635             cfg = this.getAutoCreateNativeIOS();
15636             return cfg;
15637         }
15638         
15639         /*
15640          * Touch Devices
15641          */
15642         
15643         if(Roo.isTouch && this.mobileTouchView){
15644             cfg = this.getAutoCreateTouchView();
15645             return cfg;;
15646         }
15647         
15648         /*
15649          *  Normal ComboBox
15650          */
15651         if(!this.tickable){
15652             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15653             return cfg;
15654         }
15655         
15656         /*
15657          *  ComboBox with tickable selections
15658          */
15659              
15660         var align = this.labelAlign || this.parentLabelAlign();
15661         
15662         cfg = {
15663             cls : 'form-group roo-combobox-tickable' //input-group
15664         };
15665         
15666         var btn_text_select = '';
15667         var btn_text_done = '';
15668         var btn_text_cancel = '';
15669         
15670         if (this.btn_text_show) {
15671             btn_text_select = 'Select';
15672             btn_text_done = 'Done';
15673             btn_text_cancel = 'Cancel'; 
15674         }
15675         
15676         var buttons = {
15677             tag : 'div',
15678             cls : 'tickable-buttons',
15679             cn : [
15680                 {
15681                     tag : 'button',
15682                     type : 'button',
15683                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15684                     //html : this.triggerText
15685                     html: btn_text_select
15686                 },
15687                 {
15688                     tag : 'button',
15689                     type : 'button',
15690                     name : 'ok',
15691                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15692                     //html : 'Done'
15693                     html: btn_text_done
15694                 },
15695                 {
15696                     tag : 'button',
15697                     type : 'button',
15698                     name : 'cancel',
15699                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15700                     //html : 'Cancel'
15701                     html: btn_text_cancel
15702                 }
15703             ]
15704         };
15705         
15706         if(this.editable){
15707             buttons.cn.unshift({
15708                 tag: 'input',
15709                 cls: 'roo-select2-search-field-input'
15710             });
15711         }
15712         
15713         var _this = this;
15714         
15715         Roo.each(buttons.cn, function(c){
15716             if (_this.size) {
15717                 c.cls += ' btn-' + _this.size;
15718             }
15719
15720             if (_this.disabled) {
15721                 c.disabled = true;
15722             }
15723         });
15724         
15725         var box = {
15726             tag: 'div',
15727             style : 'display: contents',
15728             cn: [
15729                 {
15730                     tag: 'input',
15731                     type : 'hidden',
15732                     cls: 'form-hidden-field'
15733                 },
15734                 {
15735                     tag: 'ul',
15736                     cls: 'roo-select2-choices',
15737                     cn:[
15738                         {
15739                             tag: 'li',
15740                             cls: 'roo-select2-search-field',
15741                             cn: [
15742                                 buttons
15743                             ]
15744                         }
15745                     ]
15746                 }
15747             ]
15748         };
15749         
15750         var combobox = {
15751             cls: 'roo-select2-container input-group roo-select2-container-multi',
15752             cn: [
15753                 
15754                 box
15755 //                {
15756 //                    tag: 'ul',
15757 //                    cls: 'typeahead typeahead-long dropdown-menu',
15758 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15759 //                }
15760             ]
15761         };
15762         
15763         if(this.hasFeedback && !this.allowBlank){
15764             
15765             var feedback = {
15766                 tag: 'span',
15767                 cls: 'glyphicon form-control-feedback'
15768             };
15769
15770             combobox.cn.push(feedback);
15771         }
15772         
15773         
15774         
15775         var indicator = {
15776             tag : 'i',
15777             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15778             tooltip : 'This field is required'
15779         };
15780         if (Roo.bootstrap.version == 4) {
15781             indicator = {
15782                 tag : 'i',
15783                 style : 'display:none'
15784             };
15785         }
15786         if (align ==='left' && this.fieldLabel.length) {
15787             
15788             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15789             
15790             cfg.cn = [
15791                 indicator,
15792                 {
15793                     tag: 'label',
15794                     'for' :  id,
15795                     cls : 'control-label col-form-label',
15796                     html : this.fieldLabel
15797
15798                 },
15799                 {
15800                     cls : "", 
15801                     cn: [
15802                         combobox
15803                     ]
15804                 }
15805
15806             ];
15807             
15808             var labelCfg = cfg.cn[1];
15809             var contentCfg = cfg.cn[2];
15810             
15811
15812             if(this.indicatorpos == 'right'){
15813                 
15814                 cfg.cn = [
15815                     {
15816                         tag: 'label',
15817                         'for' :  id,
15818                         cls : 'control-label col-form-label',
15819                         cn : [
15820                             {
15821                                 tag : 'span',
15822                                 html : this.fieldLabel
15823                             },
15824                             indicator
15825                         ]
15826                     },
15827                     {
15828                         cls : "",
15829                         cn: [
15830                             combobox
15831                         ]
15832                     }
15833
15834                 ];
15835                 
15836                 
15837                 
15838                 labelCfg = cfg.cn[0];
15839                 contentCfg = cfg.cn[1];
15840             
15841             }
15842             
15843             if(this.labelWidth > 12){
15844                 labelCfg.style = "width: " + this.labelWidth + 'px';
15845             }
15846             if(this.width * 1 > 0){
15847                 contentCfg.style = "width: " + this.width + 'px';
15848             }
15849             if(this.labelWidth < 13 && this.labelmd == 0){
15850                 this.labelmd = this.labelWidth;
15851             }
15852             
15853             if(this.labellg > 0){
15854                 labelCfg.cls += ' col-lg-' + this.labellg;
15855                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15856             }
15857             
15858             if(this.labelmd > 0){
15859                 labelCfg.cls += ' col-md-' + this.labelmd;
15860                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15861             }
15862             
15863             if(this.labelsm > 0){
15864                 labelCfg.cls += ' col-sm-' + this.labelsm;
15865                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15866             }
15867             
15868             if(this.labelxs > 0){
15869                 labelCfg.cls += ' col-xs-' + this.labelxs;
15870                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15871             }
15872                 
15873                 
15874         } else if ( this.fieldLabel.length) {
15875 //                Roo.log(" label");
15876                  cfg.cn = [
15877                    indicator,
15878                     {
15879                         tag: 'label',
15880                         //cls : 'input-group-addon',
15881                         html : this.fieldLabel
15882                     },
15883                     combobox
15884                 ];
15885                 
15886                 if(this.indicatorpos == 'right'){
15887                     cfg.cn = [
15888                         {
15889                             tag: 'label',
15890                             //cls : 'input-group-addon',
15891                             html : this.fieldLabel
15892                         },
15893                         indicator,
15894                         combobox
15895                     ];
15896                     
15897                 }
15898
15899         } else {
15900             
15901 //                Roo.log(" no label && no align");
15902                 cfg = combobox
15903                      
15904                 
15905         }
15906          
15907         var settings=this;
15908         ['xs','sm','md','lg'].map(function(size){
15909             if (settings[size]) {
15910                 cfg.cls += ' col-' + size + '-' + settings[size];
15911             }
15912         });
15913         
15914         return cfg;
15915         
15916     },
15917     
15918     _initEventsCalled : false,
15919     
15920     // private
15921     initEvents: function()
15922     {   
15923         if (this._initEventsCalled) { // as we call render... prevent looping...
15924             return;
15925         }
15926         this._initEventsCalled = true;
15927         
15928         if (!this.store) {
15929             throw "can not find store for combo";
15930         }
15931         
15932         this.indicator = this.indicatorEl();
15933         
15934         this.store = Roo.factory(this.store, Roo.data);
15935         this.store.parent = this;
15936         
15937         // if we are building from html. then this element is so complex, that we can not really
15938         // use the rendered HTML.
15939         // so we have to trash and replace the previous code.
15940         if (Roo.XComponent.build_from_html) {
15941             // remove this element....
15942             var e = this.el.dom, k=0;
15943             while (e ) { e = e.previousSibling;  ++k;}
15944
15945             this.el.remove();
15946             
15947             this.el=false;
15948             this.rendered = false;
15949             
15950             this.render(this.parent().getChildContainer(true), k);
15951         }
15952         
15953         if(Roo.isIOS && this.useNativeIOS){
15954             this.initIOSView();
15955             return;
15956         }
15957         
15958         /*
15959          * Touch Devices
15960          */
15961         
15962         if(Roo.isTouch && this.mobileTouchView){
15963             this.initTouchView();
15964             return;
15965         }
15966         
15967         if(this.tickable){
15968             this.initTickableEvents();
15969             return;
15970         }
15971         
15972         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15973         
15974         if(this.hiddenName){
15975             
15976             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15977             
15978             this.hiddenField.dom.value =
15979                 this.hiddenValue !== undefined ? this.hiddenValue :
15980                 this.value !== undefined ? this.value : '';
15981
15982             // prevent input submission
15983             this.el.dom.removeAttribute('name');
15984             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15985              
15986              
15987         }
15988         //if(Roo.isGecko){
15989         //    this.el.dom.setAttribute('autocomplete', 'off');
15990         //}
15991         
15992         var cls = 'x-combo-list';
15993         
15994         //this.list = new Roo.Layer({
15995         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15996         //});
15997         
15998         var _this = this;
15999         
16000         (function(){
16001             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16002             _this.list.setWidth(lw);
16003         }).defer(100);
16004         
16005         this.list.on('mouseover', this.onViewOver, this);
16006         this.list.on('mousemove', this.onViewMove, this);
16007         this.list.on('scroll', this.onViewScroll, this);
16008         
16009         /*
16010         this.list.swallowEvent('mousewheel');
16011         this.assetHeight = 0;
16012
16013         if(this.title){
16014             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16015             this.assetHeight += this.header.getHeight();
16016         }
16017
16018         this.innerList = this.list.createChild({cls:cls+'-inner'});
16019         this.innerList.on('mouseover', this.onViewOver, this);
16020         this.innerList.on('mousemove', this.onViewMove, this);
16021         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16022         
16023         if(this.allowBlank && !this.pageSize && !this.disableClear){
16024             this.footer = this.list.createChild({cls:cls+'-ft'});
16025             this.pageTb = new Roo.Toolbar(this.footer);
16026            
16027         }
16028         if(this.pageSize){
16029             this.footer = this.list.createChild({cls:cls+'-ft'});
16030             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16031                     {pageSize: this.pageSize});
16032             
16033         }
16034         
16035         if (this.pageTb && this.allowBlank && !this.disableClear) {
16036             var _this = this;
16037             this.pageTb.add(new Roo.Toolbar.Fill(), {
16038                 cls: 'x-btn-icon x-btn-clear',
16039                 text: '&#160;',
16040                 handler: function()
16041                 {
16042                     _this.collapse();
16043                     _this.clearValue();
16044                     _this.onSelect(false, -1);
16045                 }
16046             });
16047         }
16048         if (this.footer) {
16049             this.assetHeight += this.footer.getHeight();
16050         }
16051         */
16052             
16053         if(!this.tpl){
16054             this.tpl = Roo.bootstrap.version == 4 ?
16055                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16056                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16057         }
16058
16059         this.view = new Roo.View(this.list, this.tpl, {
16060             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16061         });
16062         //this.view.wrapEl.setDisplayed(false);
16063         this.view.on('click', this.onViewClick, this);
16064         
16065         
16066         this.store.on('beforeload', this.onBeforeLoad, this);
16067         this.store.on('load', this.onLoad, this);
16068         this.store.on('loadexception', this.onLoadException, this);
16069         /*
16070         if(this.resizable){
16071             this.resizer = new Roo.Resizable(this.list,  {
16072                pinned:true, handles:'se'
16073             });
16074             this.resizer.on('resize', function(r, w, h){
16075                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16076                 this.listWidth = w;
16077                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16078                 this.restrictHeight();
16079             }, this);
16080             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16081         }
16082         */
16083         if(!this.editable){
16084             this.editable = true;
16085             this.setEditable(false);
16086         }
16087         
16088         /*
16089         
16090         if (typeof(this.events.add.listeners) != 'undefined') {
16091             
16092             this.addicon = this.wrap.createChild(
16093                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16094        
16095             this.addicon.on('click', function(e) {
16096                 this.fireEvent('add', this);
16097             }, this);
16098         }
16099         if (typeof(this.events.edit.listeners) != 'undefined') {
16100             
16101             this.editicon = this.wrap.createChild(
16102                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16103             if (this.addicon) {
16104                 this.editicon.setStyle('margin-left', '40px');
16105             }
16106             this.editicon.on('click', function(e) {
16107                 
16108                 // we fire even  if inothing is selected..
16109                 this.fireEvent('edit', this, this.lastData );
16110                 
16111             }, this);
16112         }
16113         */
16114         
16115         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16116             "up" : function(e){
16117                 this.inKeyMode = true;
16118                 this.selectPrev();
16119             },
16120
16121             "down" : function(e){
16122                 if(!this.isExpanded()){
16123                     this.onTriggerClick();
16124                 }else{
16125                     this.inKeyMode = true;
16126                     this.selectNext();
16127                 }
16128             },
16129
16130             "enter" : function(e){
16131 //                this.onViewClick();
16132                 //return true;
16133                 this.collapse();
16134                 
16135                 if(this.fireEvent("specialkey", this, e)){
16136                     this.onViewClick(false);
16137                 }
16138                 
16139                 return true;
16140             },
16141
16142             "esc" : function(e){
16143                 this.collapse();
16144             },
16145
16146             "tab" : function(e){
16147                 this.collapse();
16148                 
16149                 if(this.fireEvent("specialkey", this, e)){
16150                     this.onViewClick(false);
16151                 }
16152                 
16153                 return true;
16154             },
16155
16156             scope : this,
16157
16158             doRelay : function(foo, bar, hname){
16159                 if(hname == 'down' || this.scope.isExpanded()){
16160                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16161                 }
16162                 return true;
16163             },
16164
16165             forceKeyDown: true
16166         });
16167         
16168         
16169         this.queryDelay = Math.max(this.queryDelay || 10,
16170                 this.mode == 'local' ? 10 : 250);
16171         
16172         
16173         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16174         
16175         if(this.typeAhead){
16176             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16177         }
16178         if(this.editable !== false){
16179             this.inputEl().on("keyup", this.onKeyUp, this);
16180         }
16181         if(this.forceSelection){
16182             this.inputEl().on('blur', this.doForce, this);
16183         }
16184         
16185         if(this.multiple){
16186             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16187             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16188         }
16189     },
16190     
16191     initTickableEvents: function()
16192     {   
16193         this.createList();
16194         
16195         if(this.hiddenName){
16196             
16197             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16198             
16199             this.hiddenField.dom.value =
16200                 this.hiddenValue !== undefined ? this.hiddenValue :
16201                 this.value !== undefined ? this.value : '';
16202
16203             // prevent input submission
16204             this.el.dom.removeAttribute('name');
16205             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16206              
16207              
16208         }
16209         
16210 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16211         
16212         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16213         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16214         if(this.triggerList){
16215             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16216         }
16217          
16218         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16219         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16220         
16221         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16222         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16223         
16224         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16225         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16226         
16227         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16228         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16229         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16230         
16231         this.okBtn.hide();
16232         this.cancelBtn.hide();
16233         
16234         var _this = this;
16235         
16236         (function(){
16237             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16238             _this.list.setWidth(lw);
16239         }).defer(100);
16240         
16241         this.list.on('mouseover', this.onViewOver, this);
16242         this.list.on('mousemove', this.onViewMove, this);
16243         
16244         this.list.on('scroll', this.onViewScroll, this);
16245         
16246         if(!this.tpl){
16247             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16248                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16249         }
16250
16251         this.view = new Roo.View(this.list, this.tpl, {
16252             singleSelect:true,
16253             tickable:true,
16254             parent:this,
16255             store: this.store,
16256             selectedClass: this.selectedClass
16257         });
16258         
16259         //this.view.wrapEl.setDisplayed(false);
16260         this.view.on('click', this.onViewClick, this);
16261         
16262         
16263         
16264         this.store.on('beforeload', this.onBeforeLoad, this);
16265         this.store.on('load', this.onLoad, this);
16266         this.store.on('loadexception', this.onLoadException, this);
16267         
16268         if(this.editable){
16269             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16270                 "up" : function(e){
16271                     this.inKeyMode = true;
16272                     this.selectPrev();
16273                 },
16274
16275                 "down" : function(e){
16276                     this.inKeyMode = true;
16277                     this.selectNext();
16278                 },
16279
16280                 "enter" : function(e){
16281                     if(this.fireEvent("specialkey", this, e)){
16282                         this.onViewClick(false);
16283                     }
16284                     
16285                     return true;
16286                 },
16287
16288                 "esc" : function(e){
16289                     this.onTickableFooterButtonClick(e, false, false);
16290                 },
16291
16292                 "tab" : function(e){
16293                     this.fireEvent("specialkey", this, e);
16294                     
16295                     this.onTickableFooterButtonClick(e, false, false);
16296                     
16297                     return true;
16298                 },
16299
16300                 scope : this,
16301
16302                 doRelay : function(e, fn, key){
16303                     if(this.scope.isExpanded()){
16304                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16305                     }
16306                     return true;
16307                 },
16308
16309                 forceKeyDown: true
16310             });
16311         }
16312         
16313         this.queryDelay = Math.max(this.queryDelay || 10,
16314                 this.mode == 'local' ? 10 : 250);
16315         
16316         
16317         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16318         
16319         if(this.typeAhead){
16320             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16321         }
16322         
16323         if(this.editable !== false){
16324             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16325         }
16326         
16327         this.indicator = this.indicatorEl();
16328         
16329         if(this.indicator){
16330             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16331             this.indicator.hide();
16332         }
16333         
16334     },
16335
16336     onDestroy : function(){
16337         if(this.view){
16338             this.view.setStore(null);
16339             this.view.el.removeAllListeners();
16340             this.view.el.remove();
16341             this.view.purgeListeners();
16342         }
16343         if(this.list){
16344             this.list.dom.innerHTML  = '';
16345         }
16346         
16347         if(this.store){
16348             this.store.un('beforeload', this.onBeforeLoad, this);
16349             this.store.un('load', this.onLoad, this);
16350             this.store.un('loadexception', this.onLoadException, this);
16351         }
16352         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16353     },
16354
16355     // private
16356     fireKey : function(e){
16357         if(e.isNavKeyPress() && !this.list.isVisible()){
16358             this.fireEvent("specialkey", this, e);
16359         }
16360     },
16361
16362     // private
16363     onResize: function(w, h)
16364     {
16365         
16366         
16367 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16368 //        
16369 //        if(typeof w != 'number'){
16370 //            // we do not handle it!?!?
16371 //            return;
16372 //        }
16373 //        var tw = this.trigger.getWidth();
16374 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16375 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16376 //        var x = w - tw;
16377 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16378 //            
16379 //        //this.trigger.setStyle('left', x+'px');
16380 //        
16381 //        if(this.list && this.listWidth === undefined){
16382 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16383 //            this.list.setWidth(lw);
16384 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16385 //        }
16386         
16387     
16388         
16389     },
16390
16391     /**
16392      * Allow or prevent the user from directly editing the field text.  If false is passed,
16393      * the user will only be able to select from the items defined in the dropdown list.  This method
16394      * is the runtime equivalent of setting the 'editable' config option at config time.
16395      * @param {Boolean} value True to allow the user to directly edit the field text
16396      */
16397     setEditable : function(value){
16398         if(value == this.editable){
16399             return;
16400         }
16401         this.editable = value;
16402         if(!value){
16403             this.inputEl().dom.setAttribute('readOnly', true);
16404             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16405             this.inputEl().addClass('x-combo-noedit');
16406         }else{
16407             this.inputEl().dom.removeAttribute('readOnly');
16408             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16409             this.inputEl().removeClass('x-combo-noedit');
16410         }
16411     },
16412
16413     // private
16414     
16415     onBeforeLoad : function(combo,opts){
16416         if(!this.hasFocus){
16417             return;
16418         }
16419          if (!opts.add) {
16420             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16421          }
16422         this.restrictHeight();
16423         this.selectedIndex = -1;
16424     },
16425
16426     // private
16427     onLoad : function(){
16428         
16429         this.hasQuery = false;
16430         
16431         if(!this.hasFocus){
16432             return;
16433         }
16434         
16435         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16436             this.loading.hide();
16437         }
16438         
16439         if(this.store.getCount() > 0){
16440             
16441             this.expand();
16442             this.restrictHeight();
16443             if(this.lastQuery == this.allQuery){
16444                 if(this.editable && !this.tickable){
16445                     this.inputEl().dom.select();
16446                 }
16447                 
16448                 if(
16449                     !this.selectByValue(this.value, true) &&
16450                     this.autoFocus && 
16451                     (
16452                         !this.store.lastOptions ||
16453                         typeof(this.store.lastOptions.add) == 'undefined' || 
16454                         this.store.lastOptions.add != true
16455                     )
16456                 ){
16457                     this.select(0, true);
16458                 }
16459             }else{
16460                 if(this.autoFocus){
16461                     this.selectNext();
16462                 }
16463                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16464                     this.taTask.delay(this.typeAheadDelay);
16465                 }
16466             }
16467         }else{
16468             this.onEmptyResults();
16469         }
16470         
16471         //this.el.focus();
16472     },
16473     // private
16474     onLoadException : function()
16475     {
16476         this.hasQuery = false;
16477         
16478         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16479             this.loading.hide();
16480         }
16481         
16482         if(this.tickable && this.editable){
16483             return;
16484         }
16485         
16486         this.collapse();
16487         // only causes errors at present
16488         //Roo.log(this.store.reader.jsonData);
16489         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16490             // fixme
16491             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16492         //}
16493         
16494         
16495     },
16496     // private
16497     onTypeAhead : function(){
16498         if(this.store.getCount() > 0){
16499             var r = this.store.getAt(0);
16500             var newValue = r.data[this.displayField];
16501             var len = newValue.length;
16502             var selStart = this.getRawValue().length;
16503             
16504             if(selStart != len){
16505                 this.setRawValue(newValue);
16506                 this.selectText(selStart, newValue.length);
16507             }
16508         }
16509     },
16510
16511     // private
16512     onSelect : function(record, index){
16513         
16514         if(this.fireEvent('beforeselect', this, record, index) !== false){
16515         
16516             this.setFromData(index > -1 ? record.data : false);
16517             
16518             this.collapse();
16519             this.fireEvent('select', this, record, index);
16520         }
16521     },
16522
16523     /**
16524      * Returns the currently selected field value or empty string if no value is set.
16525      * @return {String} value The selected value
16526      */
16527     getValue : function()
16528     {
16529         if(Roo.isIOS && this.useNativeIOS){
16530             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16531         }
16532         
16533         if(this.multiple){
16534             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16535         }
16536         
16537         if(this.valueField){
16538             return typeof this.value != 'undefined' ? this.value : '';
16539         }else{
16540             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16541         }
16542     },
16543     
16544     getRawValue : function()
16545     {
16546         if(Roo.isIOS && this.useNativeIOS){
16547             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16548         }
16549         
16550         var v = this.inputEl().getValue();
16551         
16552         return v;
16553     },
16554
16555     /**
16556      * Clears any text/value currently set in the field
16557      */
16558     clearValue : function(){
16559         
16560         if(this.hiddenField){
16561             this.hiddenField.dom.value = '';
16562         }
16563         this.value = '';
16564         this.setRawValue('');
16565         this.lastSelectionText = '';
16566         this.lastData = false;
16567         
16568         var close = this.closeTriggerEl();
16569         
16570         if(close){
16571             close.hide();
16572         }
16573         
16574         this.validate();
16575         
16576     },
16577
16578     /**
16579      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16580      * will be displayed in the field.  If the value does not match the data value of an existing item,
16581      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16582      * Otherwise the field will be blank (although the value will still be set).
16583      * @param {String} value The value to match
16584      */
16585     setValue : function(v)
16586     {
16587         if(Roo.isIOS && this.useNativeIOS){
16588             this.setIOSValue(v);
16589             return;
16590         }
16591         
16592         if(this.multiple){
16593             this.syncValue();
16594             return;
16595         }
16596         
16597         var text = v;
16598         if(this.valueField){
16599             var r = this.findRecord(this.valueField, v);
16600             if(r){
16601                 text = r.data[this.displayField];
16602             }else if(this.valueNotFoundText !== undefined){
16603                 text = this.valueNotFoundText;
16604             }
16605         }
16606         this.lastSelectionText = text;
16607         if(this.hiddenField){
16608             this.hiddenField.dom.value = v;
16609         }
16610         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16611         this.value = v;
16612         
16613         var close = this.closeTriggerEl();
16614         
16615         if(close){
16616             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16617         }
16618         
16619         this.validate();
16620     },
16621     /**
16622      * @property {Object} the last set data for the element
16623      */
16624     
16625     lastData : false,
16626     /**
16627      * Sets the value of the field based on a object which is related to the record format for the store.
16628      * @param {Object} value the value to set as. or false on reset?
16629      */
16630     setFromData : function(o){
16631         
16632         if(this.multiple){
16633             this.addItem(o);
16634             return;
16635         }
16636             
16637         var dv = ''; // display value
16638         var vv = ''; // value value..
16639         this.lastData = o;
16640         if (this.displayField) {
16641             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16642         } else {
16643             // this is an error condition!!!
16644             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16645         }
16646         
16647         if(this.valueField){
16648             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16649         }
16650         
16651         var close = this.closeTriggerEl();
16652         
16653         if(close){
16654             if(dv.length || vv * 1 > 0){
16655                 close.show() ;
16656                 this.blockFocus=true;
16657             } else {
16658                 close.hide();
16659             }             
16660         }
16661         
16662         if(this.hiddenField){
16663             this.hiddenField.dom.value = vv;
16664             
16665             this.lastSelectionText = dv;
16666             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16667             this.value = vv;
16668             return;
16669         }
16670         // no hidden field.. - we store the value in 'value', but still display
16671         // display field!!!!
16672         this.lastSelectionText = dv;
16673         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16674         this.value = vv;
16675         
16676         
16677         
16678     },
16679     // private
16680     reset : function(){
16681         // overridden so that last data is reset..
16682         
16683         if(this.multiple){
16684             this.clearItem();
16685             return;
16686         }
16687         
16688         this.setValue(this.originalValue);
16689         //this.clearInvalid();
16690         this.lastData = false;
16691         if (this.view) {
16692             this.view.clearSelections();
16693         }
16694         
16695         this.validate();
16696     },
16697     // private
16698     findRecord : function(prop, value){
16699         var record;
16700         if(this.store.getCount() > 0){
16701             this.store.each(function(r){
16702                 if(r.data[prop] == value){
16703                     record = r;
16704                     return false;
16705                 }
16706                 return true;
16707             });
16708         }
16709         return record;
16710     },
16711     
16712     getName: function()
16713     {
16714         // returns hidden if it's set..
16715         if (!this.rendered) {return ''};
16716         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16717         
16718     },
16719     // private
16720     onViewMove : function(e, t){
16721         this.inKeyMode = false;
16722     },
16723
16724     // private
16725     onViewOver : function(e, t){
16726         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16727             return;
16728         }
16729         var item = this.view.findItemFromChild(t);
16730         
16731         if(item){
16732             var index = this.view.indexOf(item);
16733             this.select(index, false);
16734         }
16735     },
16736
16737     // private
16738     onViewClick : function(view, doFocus, el, e)
16739     {
16740         var index = this.view.getSelectedIndexes()[0];
16741         
16742         var r = this.store.getAt(index);
16743         
16744         if(this.tickable){
16745             
16746             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16747                 return;
16748             }
16749             
16750             var rm = false;
16751             var _this = this;
16752             
16753             Roo.each(this.tickItems, function(v,k){
16754                 
16755                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16756                     Roo.log(v);
16757                     _this.tickItems.splice(k, 1);
16758                     
16759                     if(typeof(e) == 'undefined' && view == false){
16760                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16761                     }
16762                     
16763                     rm = true;
16764                     return;
16765                 }
16766             });
16767             
16768             if(rm){
16769                 return;
16770             }
16771             
16772             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16773                 this.tickItems.push(r.data);
16774             }
16775             
16776             if(typeof(e) == 'undefined' && view == false){
16777                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16778             }
16779                     
16780             return;
16781         }
16782         
16783         if(r){
16784             this.onSelect(r, index);
16785         }
16786         if(doFocus !== false && !this.blockFocus){
16787             this.inputEl().focus();
16788         }
16789     },
16790
16791     // private
16792     restrictHeight : function(){
16793         //this.innerList.dom.style.height = '';
16794         //var inner = this.innerList.dom;
16795         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16796         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16797         //this.list.beginUpdate();
16798         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16799         this.list.alignTo(this.inputEl(), this.listAlign);
16800         this.list.alignTo(this.inputEl(), this.listAlign);
16801         //this.list.endUpdate();
16802     },
16803
16804     // private
16805     onEmptyResults : function(){
16806         
16807         if(this.tickable && this.editable){
16808             this.hasFocus = false;
16809             this.restrictHeight();
16810             return;
16811         }
16812         
16813         this.collapse();
16814     },
16815
16816     /**
16817      * Returns true if the dropdown list is expanded, else false.
16818      */
16819     isExpanded : function(){
16820         return this.list.isVisible();
16821     },
16822
16823     /**
16824      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16825      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16826      * @param {String} value The data value of the item to select
16827      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16828      * selected item if it is not currently in view (defaults to true)
16829      * @return {Boolean} True if the value matched an item in the list, else false
16830      */
16831     selectByValue : function(v, scrollIntoView){
16832         if(v !== undefined && v !== null){
16833             var r = this.findRecord(this.valueField || this.displayField, v);
16834             if(r){
16835                 this.select(this.store.indexOf(r), scrollIntoView);
16836                 return true;
16837             }
16838         }
16839         return false;
16840     },
16841
16842     /**
16843      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16844      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16845      * @param {Number} index The zero-based index of the list item to select
16846      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16847      * selected item if it is not currently in view (defaults to true)
16848      */
16849     select : function(index, scrollIntoView){
16850         this.selectedIndex = index;
16851         this.view.select(index);
16852         if(scrollIntoView !== false){
16853             var el = this.view.getNode(index);
16854             /*
16855              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16856              */
16857             if(el){
16858                 this.list.scrollChildIntoView(el, false);
16859             }
16860         }
16861     },
16862
16863     // private
16864     selectNext : function(){
16865         var ct = this.store.getCount();
16866         if(ct > 0){
16867             if(this.selectedIndex == -1){
16868                 this.select(0);
16869             }else if(this.selectedIndex < ct-1){
16870                 this.select(this.selectedIndex+1);
16871             }
16872         }
16873     },
16874
16875     // private
16876     selectPrev : function(){
16877         var ct = this.store.getCount();
16878         if(ct > 0){
16879             if(this.selectedIndex == -1){
16880                 this.select(0);
16881             }else if(this.selectedIndex != 0){
16882                 this.select(this.selectedIndex-1);
16883             }
16884         }
16885     },
16886
16887     // private
16888     onKeyUp : function(e){
16889         if(this.editable !== false && !e.isSpecialKey()){
16890             this.lastKey = e.getKey();
16891             this.dqTask.delay(this.queryDelay);
16892         }
16893     },
16894
16895     // private
16896     validateBlur : function(){
16897         return !this.list || !this.list.isVisible();   
16898     },
16899
16900     // private
16901     initQuery : function(){
16902         
16903         var v = this.getRawValue();
16904         
16905         if(this.tickable && this.editable){
16906             v = this.tickableInputEl().getValue();
16907         }
16908         
16909         this.doQuery(v);
16910     },
16911
16912     // private
16913     doForce : function(){
16914         if(this.inputEl().dom.value.length > 0){
16915             this.inputEl().dom.value =
16916                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16917              
16918         }
16919     },
16920
16921     /**
16922      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16923      * query allowing the query action to be canceled if needed.
16924      * @param {String} query The SQL query to execute
16925      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16926      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16927      * saved in the current store (defaults to false)
16928      */
16929     doQuery : function(q, forceAll){
16930         
16931         if(q === undefined || q === null){
16932             q = '';
16933         }
16934         var qe = {
16935             query: q,
16936             forceAll: forceAll,
16937             combo: this,
16938             cancel:false
16939         };
16940         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16941             return false;
16942         }
16943         q = qe.query;
16944         
16945         forceAll = qe.forceAll;
16946         if(forceAll === true || (q.length >= this.minChars)){
16947             
16948             this.hasQuery = true;
16949             
16950             if(this.lastQuery != q || this.alwaysQuery){
16951                 this.lastQuery = q;
16952                 if(this.mode == 'local'){
16953                     this.selectedIndex = -1;
16954                     if(forceAll){
16955                         this.store.clearFilter();
16956                     }else{
16957                         
16958                         if(this.specialFilter){
16959                             this.fireEvent('specialfilter', this);
16960                             this.onLoad();
16961                             return;
16962                         }
16963                         
16964                         this.store.filter(this.displayField, q);
16965                     }
16966                     
16967                     this.store.fireEvent("datachanged", this.store);
16968                     
16969                     this.onLoad();
16970                     
16971                     
16972                 }else{
16973                     
16974                     this.store.baseParams[this.queryParam] = q;
16975                     
16976                     var options = {params : this.getParams(q)};
16977                     
16978                     if(this.loadNext){
16979                         options.add = true;
16980                         options.params.start = this.page * this.pageSize;
16981                     }
16982                     
16983                     this.store.load(options);
16984                     
16985                     /*
16986                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16987                      *  we should expand the list on onLoad
16988                      *  so command out it
16989                      */
16990 //                    this.expand();
16991                 }
16992             }else{
16993                 this.selectedIndex = -1;
16994                 this.onLoad();   
16995             }
16996         }
16997         
16998         this.loadNext = false;
16999     },
17000     
17001     // private
17002     getParams : function(q){
17003         var p = {};
17004         //p[this.queryParam] = q;
17005         
17006         if(this.pageSize){
17007             p.start = 0;
17008             p.limit = this.pageSize;
17009         }
17010         return p;
17011     },
17012
17013     /**
17014      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17015      */
17016     collapse : function(){
17017         if(!this.isExpanded()){
17018             return;
17019         }
17020         
17021         this.list.hide();
17022         
17023         this.hasFocus = false;
17024         
17025         if(this.tickable){
17026             this.okBtn.hide();
17027             this.cancelBtn.hide();
17028             this.trigger.show();
17029             
17030             if(this.editable){
17031                 this.tickableInputEl().dom.value = '';
17032                 this.tickableInputEl().blur();
17033             }
17034             
17035         }
17036         
17037         Roo.get(document).un('mousedown', this.collapseIf, this);
17038         Roo.get(document).un('mousewheel', this.collapseIf, this);
17039         if (!this.editable) {
17040             Roo.get(document).un('keydown', this.listKeyPress, this);
17041         }
17042         this.fireEvent('collapse', this);
17043         
17044         this.validate();
17045     },
17046
17047     // private
17048     collapseIf : function(e){
17049         var in_combo  = e.within(this.el);
17050         var in_list =  e.within(this.list);
17051         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17052         
17053         if (in_combo || in_list || is_list) {
17054             //e.stopPropagation();
17055             return;
17056         }
17057         
17058         if(this.tickable){
17059             this.onTickableFooterButtonClick(e, false, false);
17060         }
17061
17062         this.collapse();
17063         
17064     },
17065
17066     /**
17067      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17068      */
17069     expand : function(){
17070        
17071         if(this.isExpanded() || !this.hasFocus){
17072             return;
17073         }
17074         
17075         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17076         this.list.setWidth(lw);
17077         
17078         Roo.log('expand');
17079         
17080         this.list.show();
17081         
17082         this.restrictHeight();
17083         
17084         if(this.tickable){
17085             
17086             this.tickItems = Roo.apply([], this.item);
17087             
17088             this.okBtn.show();
17089             this.cancelBtn.show();
17090             this.trigger.hide();
17091             
17092             if(this.editable){
17093                 this.tickableInputEl().focus();
17094             }
17095             
17096         }
17097         
17098         Roo.get(document).on('mousedown', this.collapseIf, this);
17099         Roo.get(document).on('mousewheel', this.collapseIf, this);
17100         if (!this.editable) {
17101             Roo.get(document).on('keydown', this.listKeyPress, this);
17102         }
17103         
17104         this.fireEvent('expand', this);
17105     },
17106
17107     // private
17108     // Implements the default empty TriggerField.onTriggerClick function
17109     onTriggerClick : function(e)
17110     {
17111         Roo.log('trigger click');
17112         
17113         if(this.disabled || !this.triggerList){
17114             return;
17115         }
17116         
17117         this.page = 0;
17118         this.loadNext = false;
17119         
17120         if(this.isExpanded()){
17121             this.collapse();
17122             if (!this.blockFocus) {
17123                 this.inputEl().focus();
17124             }
17125             
17126         }else {
17127             this.hasFocus = true;
17128             if(this.triggerAction == 'all') {
17129                 this.doQuery(this.allQuery, true);
17130             } else {
17131                 this.doQuery(this.getRawValue());
17132             }
17133             if (!this.blockFocus) {
17134                 this.inputEl().focus();
17135             }
17136         }
17137     },
17138     
17139     onTickableTriggerClick : function(e)
17140     {
17141         if(this.disabled){
17142             return;
17143         }
17144         
17145         this.page = 0;
17146         this.loadNext = false;
17147         this.hasFocus = true;
17148         
17149         if(this.triggerAction == 'all') {
17150             this.doQuery(this.allQuery, true);
17151         } else {
17152             this.doQuery(this.getRawValue());
17153         }
17154     },
17155     
17156     onSearchFieldClick : function(e)
17157     {
17158         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17159             this.onTickableFooterButtonClick(e, false, false);
17160             return;
17161         }
17162         
17163         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17164             return;
17165         }
17166         
17167         this.page = 0;
17168         this.loadNext = false;
17169         this.hasFocus = true;
17170         
17171         if(this.triggerAction == 'all') {
17172             this.doQuery(this.allQuery, true);
17173         } else {
17174             this.doQuery(this.getRawValue());
17175         }
17176     },
17177     
17178     listKeyPress : function(e)
17179     {
17180         //Roo.log('listkeypress');
17181         // scroll to first matching element based on key pres..
17182         if (e.isSpecialKey()) {
17183             return false;
17184         }
17185         var k = String.fromCharCode(e.getKey()).toUpperCase();
17186         //Roo.log(k);
17187         var match  = false;
17188         var csel = this.view.getSelectedNodes();
17189         var cselitem = false;
17190         if (csel.length) {
17191             var ix = this.view.indexOf(csel[0]);
17192             cselitem  = this.store.getAt(ix);
17193             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17194                 cselitem = false;
17195             }
17196             
17197         }
17198         
17199         this.store.each(function(v) { 
17200             if (cselitem) {
17201                 // start at existing selection.
17202                 if (cselitem.id == v.id) {
17203                     cselitem = false;
17204                 }
17205                 return true;
17206             }
17207                 
17208             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17209                 match = this.store.indexOf(v);
17210                 return false;
17211             }
17212             return true;
17213         }, this);
17214         
17215         if (match === false) {
17216             return true; // no more action?
17217         }
17218         // scroll to?
17219         this.view.select(match);
17220         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17221         sn.scrollIntoView(sn.dom.parentNode, false);
17222     },
17223     
17224     onViewScroll : function(e, t){
17225         
17226         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){
17227             return;
17228         }
17229         
17230         this.hasQuery = true;
17231         
17232         this.loading = this.list.select('.loading', true).first();
17233         
17234         if(this.loading === null){
17235             this.list.createChild({
17236                 tag: 'div',
17237                 cls: 'loading roo-select2-more-results roo-select2-active',
17238                 html: 'Loading more results...'
17239             });
17240             
17241             this.loading = this.list.select('.loading', true).first();
17242             
17243             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17244             
17245             this.loading.hide();
17246         }
17247         
17248         this.loading.show();
17249         
17250         var _combo = this;
17251         
17252         this.page++;
17253         this.loadNext = true;
17254         
17255         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17256         
17257         return;
17258     },
17259     
17260     addItem : function(o)
17261     {   
17262         var dv = ''; // display value
17263         
17264         if (this.displayField) {
17265             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17266         } else {
17267             // this is an error condition!!!
17268             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17269         }
17270         
17271         if(!dv.length){
17272             return;
17273         }
17274         
17275         var choice = this.choices.createChild({
17276             tag: 'li',
17277             cls: 'roo-select2-search-choice',
17278             cn: [
17279                 {
17280                     tag: 'div',
17281                     html: dv
17282                 },
17283                 {
17284                     tag: 'a',
17285                     href: '#',
17286                     cls: 'roo-select2-search-choice-close fa fa-times',
17287                     tabindex: '-1'
17288                 }
17289             ]
17290             
17291         }, this.searchField);
17292         
17293         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17294         
17295         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17296         
17297         this.item.push(o);
17298         
17299         this.lastData = o;
17300         
17301         this.syncValue();
17302         
17303         this.inputEl().dom.value = '';
17304         
17305         this.validate();
17306     },
17307     
17308     onRemoveItem : function(e, _self, o)
17309     {
17310         e.preventDefault();
17311         
17312         this.lastItem = Roo.apply([], this.item);
17313         
17314         var index = this.item.indexOf(o.data) * 1;
17315         
17316         if( index < 0){
17317             Roo.log('not this item?!');
17318             return;
17319         }
17320         
17321         this.item.splice(index, 1);
17322         o.item.remove();
17323         
17324         this.syncValue();
17325         
17326         this.fireEvent('remove', this, e);
17327         
17328         this.validate();
17329         
17330     },
17331     
17332     syncValue : function()
17333     {
17334         if(!this.item.length){
17335             this.clearValue();
17336             return;
17337         }
17338             
17339         var value = [];
17340         var _this = this;
17341         Roo.each(this.item, function(i){
17342             if(_this.valueField){
17343                 value.push(i[_this.valueField]);
17344                 return;
17345             }
17346
17347             value.push(i);
17348         });
17349
17350         this.value = value.join(',');
17351
17352         if(this.hiddenField){
17353             this.hiddenField.dom.value = this.value;
17354         }
17355         
17356         this.store.fireEvent("datachanged", this.store);
17357         
17358         this.validate();
17359     },
17360     
17361     clearItem : function()
17362     {
17363         if(!this.multiple){
17364             return;
17365         }
17366         
17367         this.item = [];
17368         
17369         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17370            c.remove();
17371         });
17372         
17373         this.syncValue();
17374         
17375         this.validate();
17376         
17377         if(this.tickable && !Roo.isTouch){
17378             this.view.refresh();
17379         }
17380     },
17381     
17382     inputEl: function ()
17383     {
17384         if(Roo.isIOS && this.useNativeIOS){
17385             return this.el.select('select.roo-ios-select', true).first();
17386         }
17387         
17388         if(Roo.isTouch && this.mobileTouchView){
17389             return this.el.select('input.form-control',true).first();
17390         }
17391         
17392         if(this.tickable){
17393             return this.searchField;
17394         }
17395         
17396         return this.el.select('input.form-control',true).first();
17397     },
17398     
17399     onTickableFooterButtonClick : function(e, btn, el)
17400     {
17401         e.preventDefault();
17402         
17403         this.lastItem = Roo.apply([], this.item);
17404         
17405         if(btn && btn.name == 'cancel'){
17406             this.tickItems = Roo.apply([], this.item);
17407             this.collapse();
17408             return;
17409         }
17410         
17411         this.clearItem();
17412         
17413         var _this = this;
17414         
17415         Roo.each(this.tickItems, function(o){
17416             _this.addItem(o);
17417         });
17418         
17419         this.collapse();
17420         
17421     },
17422     
17423     validate : function()
17424     {
17425         if(this.getVisibilityEl().hasClass('hidden')){
17426             return true;
17427         }
17428         
17429         var v = this.getRawValue();
17430         
17431         if(this.multiple){
17432             v = this.getValue();
17433         }
17434         
17435         if(this.disabled || this.allowBlank || v.length){
17436             this.markValid();
17437             return true;
17438         }
17439         
17440         this.markInvalid();
17441         return false;
17442     },
17443     
17444     tickableInputEl : function()
17445     {
17446         if(!this.tickable || !this.editable){
17447             return this.inputEl();
17448         }
17449         
17450         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17451     },
17452     
17453     
17454     getAutoCreateTouchView : function()
17455     {
17456         var id = Roo.id();
17457         
17458         var cfg = {
17459             cls: 'form-group' //input-group
17460         };
17461         
17462         var input =  {
17463             tag: 'input',
17464             id : id,
17465             type : this.inputType,
17466             cls : 'form-control x-combo-noedit',
17467             autocomplete: 'new-password',
17468             placeholder : this.placeholder || '',
17469             readonly : true
17470         };
17471         
17472         if (this.name) {
17473             input.name = this.name;
17474         }
17475         
17476         if (this.size) {
17477             input.cls += ' input-' + this.size;
17478         }
17479         
17480         if (this.disabled) {
17481             input.disabled = true;
17482         }
17483         
17484         var inputblock = {
17485             cls : 'roo-combobox-wrap',
17486             cn : [
17487                 input
17488             ]
17489         };
17490         
17491         if(this.before){
17492             inputblock.cls += ' input-group';
17493             
17494             inputblock.cn.unshift({
17495                 tag :'span',
17496                 cls : 'input-group-addon input-group-prepend input-group-text',
17497                 html : this.before
17498             });
17499         }
17500         
17501         if(this.removable && !this.multiple){
17502             inputblock.cls += ' roo-removable';
17503             
17504             inputblock.cn.push({
17505                 tag: 'button',
17506                 html : 'x',
17507                 cls : 'roo-combo-removable-btn close'
17508             });
17509         }
17510
17511         if(this.hasFeedback && !this.allowBlank){
17512             
17513             inputblock.cls += ' has-feedback';
17514             
17515             inputblock.cn.push({
17516                 tag: 'span',
17517                 cls: 'glyphicon form-control-feedback'
17518             });
17519             
17520         }
17521         
17522         if (this.after) {
17523             
17524             inputblock.cls += (this.before) ? '' : ' input-group';
17525             
17526             inputblock.cn.push({
17527                 tag :'span',
17528                 cls : 'input-group-addon input-group-append input-group-text',
17529                 html : this.after
17530             });
17531         }
17532
17533         
17534         var ibwrap = inputblock;
17535         
17536         if(this.multiple){
17537             ibwrap = {
17538                 tag: 'ul',
17539                 cls: 'roo-select2-choices',
17540                 cn:[
17541                     {
17542                         tag: 'li',
17543                         cls: 'roo-select2-search-field',
17544                         cn: [
17545
17546                             inputblock
17547                         ]
17548                     }
17549                 ]
17550             };
17551         
17552             
17553         }
17554         
17555         var combobox = {
17556             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17557             cn: [
17558                 {
17559                     tag: 'input',
17560                     type : 'hidden',
17561                     cls: 'form-hidden-field'
17562                 },
17563                 ibwrap
17564             ]
17565         };
17566         
17567         if(!this.multiple && this.showToggleBtn){
17568             
17569             var caret = {
17570                 cls: 'caret'
17571             };
17572             
17573             if (this.caret != false) {
17574                 caret = {
17575                      tag: 'i',
17576                      cls: 'fa fa-' + this.caret
17577                 };
17578                 
17579             }
17580             
17581             combobox.cn.push({
17582                 tag :'span',
17583                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17584                 cn : [
17585                     Roo.bootstrap.version == 3 ? caret : '',
17586                     {
17587                         tag: 'span',
17588                         cls: 'combobox-clear',
17589                         cn  : [
17590                             {
17591                                 tag : 'i',
17592                                 cls: 'icon-remove'
17593                             }
17594                         ]
17595                     }
17596                 ]
17597
17598             })
17599         }
17600         
17601         if(this.multiple){
17602             combobox.cls += ' roo-select2-container-multi';
17603         }
17604         
17605         var required =  this.allowBlank ?  {
17606                     tag : 'i',
17607                     style: 'display: none'
17608                 } : {
17609                    tag : 'i',
17610                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17611                    tooltip : 'This field is required'
17612                 };
17613         
17614         var align = this.labelAlign || this.parentLabelAlign();
17615         
17616         if (align ==='left' && this.fieldLabel.length) {
17617
17618             cfg.cn = [
17619                 required,
17620                 {
17621                     tag: 'label',
17622                     cls : 'control-label col-form-label',
17623                     html : this.fieldLabel
17624
17625                 },
17626                 {
17627                     cls : 'roo-combobox-wrap ', 
17628                     cn: [
17629                         combobox
17630                     ]
17631                 }
17632             ];
17633             
17634             var labelCfg = cfg.cn[1];
17635             var contentCfg = cfg.cn[2];
17636             
17637
17638             if(this.indicatorpos == 'right'){
17639                 cfg.cn = [
17640                     {
17641                         tag: 'label',
17642                         'for' :  id,
17643                         cls : 'control-label col-form-label',
17644                         cn : [
17645                             {
17646                                 tag : 'span',
17647                                 html : this.fieldLabel
17648                             },
17649                             required
17650                         ]
17651                     },
17652                     {
17653                         cls : "roo-combobox-wrap ",
17654                         cn: [
17655                             combobox
17656                         ]
17657                     }
17658
17659                 ];
17660                 
17661                 labelCfg = cfg.cn[0];
17662                 contentCfg = cfg.cn[1];
17663             }
17664             
17665            
17666             
17667             if(this.labelWidth > 12){
17668                 labelCfg.style = "width: " + this.labelWidth + 'px';
17669             }
17670            
17671             if(this.labelWidth < 13 && this.labelmd == 0){
17672                 this.labelmd = this.labelWidth;
17673             }
17674             
17675             if(this.labellg > 0){
17676                 labelCfg.cls += ' col-lg-' + this.labellg;
17677                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17678             }
17679             
17680             if(this.labelmd > 0){
17681                 labelCfg.cls += ' col-md-' + this.labelmd;
17682                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17683             }
17684             
17685             if(this.labelsm > 0){
17686                 labelCfg.cls += ' col-sm-' + this.labelsm;
17687                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17688             }
17689             
17690             if(this.labelxs > 0){
17691                 labelCfg.cls += ' col-xs-' + this.labelxs;
17692                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17693             }
17694                 
17695                 
17696         } else if ( this.fieldLabel.length) {
17697             cfg.cn = [
17698                required,
17699                 {
17700                     tag: 'label',
17701                     cls : 'control-label',
17702                     html : this.fieldLabel
17703
17704                 },
17705                 {
17706                     cls : '', 
17707                     cn: [
17708                         combobox
17709                     ]
17710                 }
17711             ];
17712             
17713             if(this.indicatorpos == 'right'){
17714                 cfg.cn = [
17715                     {
17716                         tag: 'label',
17717                         cls : 'control-label',
17718                         html : this.fieldLabel,
17719                         cn : [
17720                             required
17721                         ]
17722                     },
17723                     {
17724                         cls : '', 
17725                         cn: [
17726                             combobox
17727                         ]
17728                     }
17729                 ];
17730             }
17731         } else {
17732             cfg.cn = combobox;    
17733         }
17734         
17735         
17736         var settings = this;
17737         
17738         ['xs','sm','md','lg'].map(function(size){
17739             if (settings[size]) {
17740                 cfg.cls += ' col-' + size + '-' + settings[size];
17741             }
17742         });
17743         
17744         return cfg;
17745     },
17746     
17747     initTouchView : function()
17748     {
17749         this.renderTouchView();
17750         
17751         this.touchViewEl.on('scroll', function(){
17752             this.el.dom.scrollTop = 0;
17753         }, this);
17754         
17755         this.originalValue = this.getValue();
17756         
17757         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17758         
17759         this.inputEl().on("click", this.showTouchView, this);
17760         if (this.triggerEl) {
17761             this.triggerEl.on("click", this.showTouchView, this);
17762         }
17763         
17764         
17765         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17766         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17767         
17768         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17769         
17770         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17771         this.store.on('load', this.onTouchViewLoad, this);
17772         this.store.on('loadexception', this.onTouchViewLoadException, this);
17773         
17774         if(this.hiddenName){
17775             
17776             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17777             
17778             this.hiddenField.dom.value =
17779                 this.hiddenValue !== undefined ? this.hiddenValue :
17780                 this.value !== undefined ? this.value : '';
17781         
17782             this.el.dom.removeAttribute('name');
17783             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17784         }
17785         
17786         if(this.multiple){
17787             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17788             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17789         }
17790         
17791         if(this.removable && !this.multiple){
17792             var close = this.closeTriggerEl();
17793             if(close){
17794                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17795                 close.on('click', this.removeBtnClick, this, close);
17796             }
17797         }
17798         /*
17799          * fix the bug in Safari iOS8
17800          */
17801         this.inputEl().on("focus", function(e){
17802             document.activeElement.blur();
17803         }, this);
17804         
17805         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17806         
17807         return;
17808         
17809         
17810     },
17811     
17812     renderTouchView : function()
17813     {
17814         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17815         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17816         
17817         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17818         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17819         
17820         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17821         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17822         this.touchViewBodyEl.setStyle('overflow', 'auto');
17823         
17824         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17825         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17826         
17827         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17828         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17829         
17830     },
17831     
17832     showTouchView : function()
17833     {
17834         if(this.disabled){
17835             return;
17836         }
17837         
17838         this.touchViewHeaderEl.hide();
17839
17840         if(this.modalTitle.length){
17841             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17842             this.touchViewHeaderEl.show();
17843         }
17844
17845         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17846         this.touchViewEl.show();
17847
17848         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17849         
17850         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17851         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17852
17853         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17854
17855         if(this.modalTitle.length){
17856             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17857         }
17858         
17859         this.touchViewBodyEl.setHeight(bodyHeight);
17860
17861         if(this.animate){
17862             var _this = this;
17863             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17864         }else{
17865             this.touchViewEl.addClass(['in','show']);
17866         }
17867         
17868         if(this._touchViewMask){
17869             Roo.get(document.body).addClass("x-body-masked");
17870             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17871             this._touchViewMask.setStyle('z-index', 10000);
17872             this._touchViewMask.addClass('show');
17873         }
17874         
17875         this.doTouchViewQuery();
17876         
17877     },
17878     
17879     hideTouchView : function()
17880     {
17881         this.touchViewEl.removeClass(['in','show']);
17882
17883         if(this.animate){
17884             var _this = this;
17885             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17886         }else{
17887             this.touchViewEl.setStyle('display', 'none');
17888         }
17889         
17890         if(this._touchViewMask){
17891             this._touchViewMask.removeClass('show');
17892             Roo.get(document.body).removeClass("x-body-masked");
17893         }
17894     },
17895     
17896     setTouchViewValue : function()
17897     {
17898         if(this.multiple){
17899             this.clearItem();
17900         
17901             var _this = this;
17902
17903             Roo.each(this.tickItems, function(o){
17904                 this.addItem(o);
17905             }, this);
17906         }
17907         
17908         this.hideTouchView();
17909     },
17910     
17911     doTouchViewQuery : function()
17912     {
17913         var qe = {
17914             query: '',
17915             forceAll: true,
17916             combo: this,
17917             cancel:false
17918         };
17919         
17920         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17921             return false;
17922         }
17923         
17924         if(!this.alwaysQuery || this.mode == 'local'){
17925             this.onTouchViewLoad();
17926             return;
17927         }
17928         
17929         this.store.load();
17930     },
17931     
17932     onTouchViewBeforeLoad : function(combo,opts)
17933     {
17934         return;
17935     },
17936
17937     // private
17938     onTouchViewLoad : function()
17939     {
17940         if(this.store.getCount() < 1){
17941             this.onTouchViewEmptyResults();
17942             return;
17943         }
17944         
17945         this.clearTouchView();
17946         
17947         var rawValue = this.getRawValue();
17948         
17949         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17950         
17951         this.tickItems = [];
17952         
17953         this.store.data.each(function(d, rowIndex){
17954             var row = this.touchViewListGroup.createChild(template);
17955             
17956             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17957                 row.addClass(d.data.cls);
17958             }
17959             
17960             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17961                 var cfg = {
17962                     data : d.data,
17963                     html : d.data[this.displayField]
17964                 };
17965                 
17966                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17967                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17968                 }
17969             }
17970             row.removeClass('selected');
17971             if(!this.multiple && this.valueField &&
17972                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17973             {
17974                 // radio buttons..
17975                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17976                 row.addClass('selected');
17977             }
17978             
17979             if(this.multiple && this.valueField &&
17980                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17981             {
17982                 
17983                 // checkboxes...
17984                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17985                 this.tickItems.push(d.data);
17986             }
17987             
17988             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17989             
17990         }, this);
17991         
17992         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17993         
17994         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17995
17996         if(this.modalTitle.length){
17997             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17998         }
17999
18000         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18001         
18002         if(this.mobile_restrict_height && listHeight < bodyHeight){
18003             this.touchViewBodyEl.setHeight(listHeight);
18004         }
18005         
18006         var _this = this;
18007         
18008         if(firstChecked && listHeight > bodyHeight){
18009             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18010         }
18011         
18012     },
18013     
18014     onTouchViewLoadException : function()
18015     {
18016         this.hideTouchView();
18017     },
18018     
18019     onTouchViewEmptyResults : function()
18020     {
18021         this.clearTouchView();
18022         
18023         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18024         
18025         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18026         
18027     },
18028     
18029     clearTouchView : function()
18030     {
18031         this.touchViewListGroup.dom.innerHTML = '';
18032     },
18033     
18034     onTouchViewClick : function(e, el, o)
18035     {
18036         e.preventDefault();
18037         
18038         var row = o.row;
18039         var rowIndex = o.rowIndex;
18040         
18041         var r = this.store.getAt(rowIndex);
18042         
18043         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18044             
18045             if(!this.multiple){
18046                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18047                     c.dom.removeAttribute('checked');
18048                 }, this);
18049
18050                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18051
18052                 this.setFromData(r.data);
18053
18054                 var close = this.closeTriggerEl();
18055
18056                 if(close){
18057                     close.show();
18058                 }
18059
18060                 this.hideTouchView();
18061
18062                 this.fireEvent('select', this, r, rowIndex);
18063
18064                 return;
18065             }
18066
18067             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18068                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18069                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18070                 return;
18071             }
18072
18073             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18074             this.addItem(r.data);
18075             this.tickItems.push(r.data);
18076         }
18077     },
18078     
18079     getAutoCreateNativeIOS : function()
18080     {
18081         var cfg = {
18082             cls: 'form-group' //input-group,
18083         };
18084         
18085         var combobox =  {
18086             tag: 'select',
18087             cls : 'roo-ios-select'
18088         };
18089         
18090         if (this.name) {
18091             combobox.name = this.name;
18092         }
18093         
18094         if (this.disabled) {
18095             combobox.disabled = true;
18096         }
18097         
18098         var settings = this;
18099         
18100         ['xs','sm','md','lg'].map(function(size){
18101             if (settings[size]) {
18102                 cfg.cls += ' col-' + size + '-' + settings[size];
18103             }
18104         });
18105         
18106         cfg.cn = combobox;
18107         
18108         return cfg;
18109         
18110     },
18111     
18112     initIOSView : function()
18113     {
18114         this.store.on('load', this.onIOSViewLoad, this);
18115         
18116         return;
18117     },
18118     
18119     onIOSViewLoad : function()
18120     {
18121         if(this.store.getCount() < 1){
18122             return;
18123         }
18124         
18125         this.clearIOSView();
18126         
18127         if(this.allowBlank) {
18128             
18129             var default_text = '-- SELECT --';
18130             
18131             if(this.placeholder.length){
18132                 default_text = this.placeholder;
18133             }
18134             
18135             if(this.emptyTitle.length){
18136                 default_text += ' - ' + this.emptyTitle + ' -';
18137             }
18138             
18139             var opt = this.inputEl().createChild({
18140                 tag: 'option',
18141                 value : 0,
18142                 html : default_text
18143             });
18144             
18145             var o = {};
18146             o[this.valueField] = 0;
18147             o[this.displayField] = default_text;
18148             
18149             this.ios_options.push({
18150                 data : o,
18151                 el : opt
18152             });
18153             
18154         }
18155         
18156         this.store.data.each(function(d, rowIndex){
18157             
18158             var html = '';
18159             
18160             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18161                 html = d.data[this.displayField];
18162             }
18163             
18164             var value = '';
18165             
18166             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18167                 value = d.data[this.valueField];
18168             }
18169             
18170             var option = {
18171                 tag: 'option',
18172                 value : value,
18173                 html : html
18174             };
18175             
18176             if(this.value == d.data[this.valueField]){
18177                 option['selected'] = true;
18178             }
18179             
18180             var opt = this.inputEl().createChild(option);
18181             
18182             this.ios_options.push({
18183                 data : d.data,
18184                 el : opt
18185             });
18186             
18187         }, this);
18188         
18189         this.inputEl().on('change', function(){
18190            this.fireEvent('select', this);
18191         }, this);
18192         
18193     },
18194     
18195     clearIOSView: function()
18196     {
18197         this.inputEl().dom.innerHTML = '';
18198         
18199         this.ios_options = [];
18200     },
18201     
18202     setIOSValue: function(v)
18203     {
18204         this.value = v;
18205         
18206         if(!this.ios_options){
18207             return;
18208         }
18209         
18210         Roo.each(this.ios_options, function(opts){
18211            
18212            opts.el.dom.removeAttribute('selected');
18213            
18214            if(opts.data[this.valueField] != v){
18215                return;
18216            }
18217            
18218            opts.el.dom.setAttribute('selected', true);
18219            
18220         }, this);
18221     }
18222
18223     /** 
18224     * @cfg {Boolean} grow 
18225     * @hide 
18226     */
18227     /** 
18228     * @cfg {Number} growMin 
18229     * @hide 
18230     */
18231     /** 
18232     * @cfg {Number} growMax 
18233     * @hide 
18234     */
18235     /**
18236      * @hide
18237      * @method autoSize
18238      */
18239 });
18240
18241 Roo.apply(Roo.bootstrap.ComboBox,  {
18242     
18243     header : {
18244         tag: 'div',
18245         cls: 'modal-header',
18246         cn: [
18247             {
18248                 tag: 'h4',
18249                 cls: 'modal-title'
18250             }
18251         ]
18252     },
18253     
18254     body : {
18255         tag: 'div',
18256         cls: 'modal-body',
18257         cn: [
18258             {
18259                 tag: 'ul',
18260                 cls: 'list-group'
18261             }
18262         ]
18263     },
18264     
18265     listItemRadio : {
18266         tag: 'li',
18267         cls: 'list-group-item',
18268         cn: [
18269             {
18270                 tag: 'span',
18271                 cls: 'roo-combobox-list-group-item-value'
18272             },
18273             {
18274                 tag: 'div',
18275                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18276                 cn: [
18277                     {
18278                         tag: 'input',
18279                         type: 'radio'
18280                     },
18281                     {
18282                         tag: 'label'
18283                     }
18284                 ]
18285             }
18286         ]
18287     },
18288     
18289     listItemCheckbox : {
18290         tag: 'li',
18291         cls: 'list-group-item',
18292         cn: [
18293             {
18294                 tag: 'span',
18295                 cls: 'roo-combobox-list-group-item-value'
18296             },
18297             {
18298                 tag: 'div',
18299                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18300                 cn: [
18301                     {
18302                         tag: 'input',
18303                         type: 'checkbox'
18304                     },
18305                     {
18306                         tag: 'label'
18307                     }
18308                 ]
18309             }
18310         ]
18311     },
18312     
18313     emptyResult : {
18314         tag: 'div',
18315         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18316     },
18317     
18318     footer : {
18319         tag: 'div',
18320         cls: 'modal-footer',
18321         cn: [
18322             {
18323                 tag: 'div',
18324                 cls: 'row',
18325                 cn: [
18326                     {
18327                         tag: 'div',
18328                         cls: 'col-xs-6 text-left',
18329                         cn: {
18330                             tag: 'button',
18331                             cls: 'btn btn-danger roo-touch-view-cancel',
18332                             html: 'Cancel'
18333                         }
18334                     },
18335                     {
18336                         tag: 'div',
18337                         cls: 'col-xs-6 text-right',
18338                         cn: {
18339                             tag: 'button',
18340                             cls: 'btn btn-success roo-touch-view-ok',
18341                             html: 'OK'
18342                         }
18343                     }
18344                 ]
18345             }
18346         ]
18347         
18348     }
18349 });
18350
18351 Roo.apply(Roo.bootstrap.ComboBox,  {
18352     
18353     touchViewTemplate : {
18354         tag: 'div',
18355         cls: 'modal fade roo-combobox-touch-view',
18356         cn: [
18357             {
18358                 tag: 'div',
18359                 cls: 'modal-dialog',
18360                 style : 'position:fixed', // we have to fix position....
18361                 cn: [
18362                     {
18363                         tag: 'div',
18364                         cls: 'modal-content',
18365                         cn: [
18366                             Roo.bootstrap.ComboBox.header,
18367                             Roo.bootstrap.ComboBox.body,
18368                             Roo.bootstrap.ComboBox.footer
18369                         ]
18370                     }
18371                 ]
18372             }
18373         ]
18374     }
18375 });/*
18376  * Based on:
18377  * Ext JS Library 1.1.1
18378  * Copyright(c) 2006-2007, Ext JS, LLC.
18379  *
18380  * Originally Released Under LGPL - original licence link has changed is not relivant.
18381  *
18382  * Fork - LGPL
18383  * <script type="text/javascript">
18384  */
18385
18386 /**
18387  * @class Roo.View
18388  * @extends Roo.util.Observable
18389  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18390  * This class also supports single and multi selection modes. <br>
18391  * Create a data model bound view:
18392  <pre><code>
18393  var store = new Roo.data.Store(...);
18394
18395  var view = new Roo.View({
18396     el : "my-element",
18397     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18398  
18399     singleSelect: true,
18400     selectedClass: "ydataview-selected",
18401     store: store
18402  });
18403
18404  // listen for node click?
18405  view.on("click", function(vw, index, node, e){
18406  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18407  });
18408
18409  // load XML data
18410  dataModel.load("foobar.xml");
18411  </code></pre>
18412  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18413  * <br><br>
18414  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18415  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18416  * 
18417  * Note: old style constructor is still suported (container, template, config)
18418  * 
18419  * @constructor
18420  * Create a new View
18421  * @param {Object} config The config object
18422  * 
18423  */
18424 Roo.View = function(config, depreciated_tpl, depreciated_config){
18425     
18426     this.parent = false;
18427     
18428     if (typeof(depreciated_tpl) == 'undefined') {
18429         // new way.. - universal constructor.
18430         Roo.apply(this, config);
18431         this.el  = Roo.get(this.el);
18432     } else {
18433         // old format..
18434         this.el  = Roo.get(config);
18435         this.tpl = depreciated_tpl;
18436         Roo.apply(this, depreciated_config);
18437     }
18438     this.wrapEl  = this.el.wrap().wrap();
18439     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18440     
18441     
18442     if(typeof(this.tpl) == "string"){
18443         this.tpl = new Roo.Template(this.tpl);
18444     } else {
18445         // support xtype ctors..
18446         this.tpl = new Roo.factory(this.tpl, Roo);
18447     }
18448     
18449     
18450     this.tpl.compile();
18451     
18452     /** @private */
18453     this.addEvents({
18454         /**
18455          * @event beforeclick
18456          * Fires before a click is processed. Returns false to cancel the default action.
18457          * @param {Roo.View} this
18458          * @param {Number} index The index of the target node
18459          * @param {HTMLElement} node The target node
18460          * @param {Roo.EventObject} e The raw event object
18461          */
18462             "beforeclick" : true,
18463         /**
18464          * @event click
18465          * Fires when a template node is clicked.
18466          * @param {Roo.View} this
18467          * @param {Number} index The index of the target node
18468          * @param {HTMLElement} node The target node
18469          * @param {Roo.EventObject} e The raw event object
18470          */
18471             "click" : true,
18472         /**
18473          * @event dblclick
18474          * Fires when a template node is double clicked.
18475          * @param {Roo.View} this
18476          * @param {Number} index The index of the target node
18477          * @param {HTMLElement} node The target node
18478          * @param {Roo.EventObject} e The raw event object
18479          */
18480             "dblclick" : true,
18481         /**
18482          * @event contextmenu
18483          * Fires when a template node is right clicked.
18484          * @param {Roo.View} this
18485          * @param {Number} index The index of the target node
18486          * @param {HTMLElement} node The target node
18487          * @param {Roo.EventObject} e The raw event object
18488          */
18489             "contextmenu" : true,
18490         /**
18491          * @event selectionchange
18492          * Fires when the selected nodes change.
18493          * @param {Roo.View} this
18494          * @param {Array} selections Array of the selected nodes
18495          */
18496             "selectionchange" : true,
18497     
18498         /**
18499          * @event beforeselect
18500          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18501          * @param {Roo.View} this
18502          * @param {HTMLElement} node The node to be selected
18503          * @param {Array} selections Array of currently selected nodes
18504          */
18505             "beforeselect" : true,
18506         /**
18507          * @event preparedata
18508          * Fires on every row to render, to allow you to change the data.
18509          * @param {Roo.View} this
18510          * @param {Object} data to be rendered (change this)
18511          */
18512           "preparedata" : true
18513           
18514           
18515         });
18516
18517
18518
18519     this.el.on({
18520         "click": this.onClick,
18521         "dblclick": this.onDblClick,
18522         "contextmenu": this.onContextMenu,
18523         scope:this
18524     });
18525
18526     this.selections = [];
18527     this.nodes = [];
18528     this.cmp = new Roo.CompositeElementLite([]);
18529     if(this.store){
18530         this.store = Roo.factory(this.store, Roo.data);
18531         this.setStore(this.store, true);
18532     }
18533     
18534     if ( this.footer && this.footer.xtype) {
18535            
18536          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18537         
18538         this.footer.dataSource = this.store;
18539         this.footer.container = fctr;
18540         this.footer = Roo.factory(this.footer, Roo);
18541         fctr.insertFirst(this.el);
18542         
18543         // this is a bit insane - as the paging toolbar seems to detach the el..
18544 //        dom.parentNode.parentNode.parentNode
18545          // they get detached?
18546     }
18547     
18548     
18549     Roo.View.superclass.constructor.call(this);
18550     
18551     
18552 };
18553
18554 Roo.extend(Roo.View, Roo.util.Observable, {
18555     
18556      /**
18557      * @cfg {Roo.data.Store} store Data store to load data from.
18558      */
18559     store : false,
18560     
18561     /**
18562      * @cfg {String|Roo.Element} el The container element.
18563      */
18564     el : '',
18565     
18566     /**
18567      * @cfg {String|Roo.Template} tpl The template used by this View 
18568      */
18569     tpl : false,
18570     /**
18571      * @cfg {String} dataName the named area of the template to use as the data area
18572      *                          Works with domtemplates roo-name="name"
18573      */
18574     dataName: false,
18575     /**
18576      * @cfg {String} selectedClass The css class to add to selected nodes
18577      */
18578     selectedClass : "x-view-selected",
18579      /**
18580      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18581      */
18582     emptyText : "",
18583     
18584     /**
18585      * @cfg {String} text to display on mask (default Loading)
18586      */
18587     mask : false,
18588     /**
18589      * @cfg {Boolean} multiSelect Allow multiple selection
18590      */
18591     multiSelect : false,
18592     /**
18593      * @cfg {Boolean} singleSelect Allow single selection
18594      */
18595     singleSelect:  false,
18596     
18597     /**
18598      * @cfg {Boolean} toggleSelect - selecting 
18599      */
18600     toggleSelect : false,
18601     
18602     /**
18603      * @cfg {Boolean} tickable - selecting 
18604      */
18605     tickable : false,
18606     
18607     /**
18608      * Returns the element this view is bound to.
18609      * @return {Roo.Element}
18610      */
18611     getEl : function(){
18612         return this.wrapEl;
18613     },
18614     
18615     
18616
18617     /**
18618      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18619      */
18620     refresh : function(){
18621         //Roo.log('refresh');
18622         var t = this.tpl;
18623         
18624         // if we are using something like 'domtemplate', then
18625         // the what gets used is:
18626         // t.applySubtemplate(NAME, data, wrapping data..)
18627         // the outer template then get' applied with
18628         //     the store 'extra data'
18629         // and the body get's added to the
18630         //      roo-name="data" node?
18631         //      <span class='roo-tpl-{name}'></span> ?????
18632         
18633         
18634         
18635         this.clearSelections();
18636         this.el.update("");
18637         var html = [];
18638         var records = this.store.getRange();
18639         if(records.length < 1) {
18640             
18641             // is this valid??  = should it render a template??
18642             
18643             this.el.update(this.emptyText);
18644             return;
18645         }
18646         var el = this.el;
18647         if (this.dataName) {
18648             this.el.update(t.apply(this.store.meta)); //????
18649             el = this.el.child('.roo-tpl-' + this.dataName);
18650         }
18651         
18652         for(var i = 0, len = records.length; i < len; i++){
18653             var data = this.prepareData(records[i].data, i, records[i]);
18654             this.fireEvent("preparedata", this, data, i, records[i]);
18655             
18656             var d = Roo.apply({}, data);
18657             
18658             if(this.tickable){
18659                 Roo.apply(d, {'roo-id' : Roo.id()});
18660                 
18661                 var _this = this;
18662             
18663                 Roo.each(this.parent.item, function(item){
18664                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18665                         return;
18666                     }
18667                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18668                 });
18669             }
18670             
18671             html[html.length] = Roo.util.Format.trim(
18672                 this.dataName ?
18673                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18674                     t.apply(d)
18675             );
18676         }
18677         
18678         
18679         
18680         el.update(html.join(""));
18681         this.nodes = el.dom.childNodes;
18682         this.updateIndexes(0);
18683     },
18684     
18685
18686     /**
18687      * Function to override to reformat the data that is sent to
18688      * the template for each node.
18689      * DEPRICATED - use the preparedata event handler.
18690      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18691      * a JSON object for an UpdateManager bound view).
18692      */
18693     prepareData : function(data, index, record)
18694     {
18695         this.fireEvent("preparedata", this, data, index, record);
18696         return data;
18697     },
18698
18699     onUpdate : function(ds, record){
18700         // Roo.log('on update');   
18701         this.clearSelections();
18702         var index = this.store.indexOf(record);
18703         var n = this.nodes[index];
18704         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18705         n.parentNode.removeChild(n);
18706         this.updateIndexes(index, index);
18707     },
18708
18709     
18710     
18711 // --------- FIXME     
18712     onAdd : function(ds, records, index)
18713     {
18714         //Roo.log(['on Add', ds, records, index] );        
18715         this.clearSelections();
18716         if(this.nodes.length == 0){
18717             this.refresh();
18718             return;
18719         }
18720         var n = this.nodes[index];
18721         for(var i = 0, len = records.length; i < len; i++){
18722             var d = this.prepareData(records[i].data, i, records[i]);
18723             if(n){
18724                 this.tpl.insertBefore(n, d);
18725             }else{
18726                 
18727                 this.tpl.append(this.el, d);
18728             }
18729         }
18730         this.updateIndexes(index);
18731     },
18732
18733     onRemove : function(ds, record, index){
18734        // Roo.log('onRemove');
18735         this.clearSelections();
18736         var el = this.dataName  ?
18737             this.el.child('.roo-tpl-' + this.dataName) :
18738             this.el; 
18739         
18740         el.dom.removeChild(this.nodes[index]);
18741         this.updateIndexes(index);
18742     },
18743
18744     /**
18745      * Refresh an individual node.
18746      * @param {Number} index
18747      */
18748     refreshNode : function(index){
18749         this.onUpdate(this.store, this.store.getAt(index));
18750     },
18751
18752     updateIndexes : function(startIndex, endIndex){
18753         var ns = this.nodes;
18754         startIndex = startIndex || 0;
18755         endIndex = endIndex || ns.length - 1;
18756         for(var i = startIndex; i <= endIndex; i++){
18757             ns[i].nodeIndex = i;
18758         }
18759     },
18760
18761     /**
18762      * Changes the data store this view uses and refresh the view.
18763      * @param {Store} store
18764      */
18765     setStore : function(store, initial){
18766         if(!initial && this.store){
18767             this.store.un("datachanged", this.refresh);
18768             this.store.un("add", this.onAdd);
18769             this.store.un("remove", this.onRemove);
18770             this.store.un("update", this.onUpdate);
18771             this.store.un("clear", this.refresh);
18772             this.store.un("beforeload", this.onBeforeLoad);
18773             this.store.un("load", this.onLoad);
18774             this.store.un("loadexception", this.onLoad);
18775         }
18776         if(store){
18777           
18778             store.on("datachanged", this.refresh, this);
18779             store.on("add", this.onAdd, this);
18780             store.on("remove", this.onRemove, this);
18781             store.on("update", this.onUpdate, this);
18782             store.on("clear", this.refresh, this);
18783             store.on("beforeload", this.onBeforeLoad, this);
18784             store.on("load", this.onLoad, this);
18785             store.on("loadexception", this.onLoad, this);
18786         }
18787         
18788         if(store){
18789             this.refresh();
18790         }
18791     },
18792     /**
18793      * onbeforeLoad - masks the loading area.
18794      *
18795      */
18796     onBeforeLoad : function(store,opts)
18797     {
18798          //Roo.log('onBeforeLoad');   
18799         if (!opts.add) {
18800             this.el.update("");
18801         }
18802         this.el.mask(this.mask ? this.mask : "Loading" ); 
18803     },
18804     onLoad : function ()
18805     {
18806         this.el.unmask();
18807     },
18808     
18809
18810     /**
18811      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18812      * @param {HTMLElement} node
18813      * @return {HTMLElement} The template node
18814      */
18815     findItemFromChild : function(node){
18816         var el = this.dataName  ?
18817             this.el.child('.roo-tpl-' + this.dataName,true) :
18818             this.el.dom; 
18819         
18820         if(!node || node.parentNode == el){
18821                     return node;
18822             }
18823             var p = node.parentNode;
18824             while(p && p != el){
18825             if(p.parentNode == el){
18826                 return p;
18827             }
18828             p = p.parentNode;
18829         }
18830             return null;
18831     },
18832
18833     /** @ignore */
18834     onClick : function(e){
18835         var item = this.findItemFromChild(e.getTarget());
18836         if(item){
18837             var index = this.indexOf(item);
18838             if(this.onItemClick(item, index, e) !== false){
18839                 this.fireEvent("click", this, index, item, e);
18840             }
18841         }else{
18842             this.clearSelections();
18843         }
18844     },
18845
18846     /** @ignore */
18847     onContextMenu : function(e){
18848         var item = this.findItemFromChild(e.getTarget());
18849         if(item){
18850             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18851         }
18852     },
18853
18854     /** @ignore */
18855     onDblClick : function(e){
18856         var item = this.findItemFromChild(e.getTarget());
18857         if(item){
18858             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18859         }
18860     },
18861
18862     onItemClick : function(item, index, e)
18863     {
18864         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18865             return false;
18866         }
18867         if (this.toggleSelect) {
18868             var m = this.isSelected(item) ? 'unselect' : 'select';
18869             //Roo.log(m);
18870             var _t = this;
18871             _t[m](item, true, false);
18872             return true;
18873         }
18874         if(this.multiSelect || this.singleSelect){
18875             if(this.multiSelect && e.shiftKey && this.lastSelection){
18876                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18877             }else{
18878                 this.select(item, this.multiSelect && e.ctrlKey);
18879                 this.lastSelection = item;
18880             }
18881             
18882             if(!this.tickable){
18883                 e.preventDefault();
18884             }
18885             
18886         }
18887         return true;
18888     },
18889
18890     /**
18891      * Get the number of selected nodes.
18892      * @return {Number}
18893      */
18894     getSelectionCount : function(){
18895         return this.selections.length;
18896     },
18897
18898     /**
18899      * Get the currently selected nodes.
18900      * @return {Array} An array of HTMLElements
18901      */
18902     getSelectedNodes : function(){
18903         return this.selections;
18904     },
18905
18906     /**
18907      * Get the indexes of the selected nodes.
18908      * @return {Array}
18909      */
18910     getSelectedIndexes : function(){
18911         var indexes = [], s = this.selections;
18912         for(var i = 0, len = s.length; i < len; i++){
18913             indexes.push(s[i].nodeIndex);
18914         }
18915         return indexes;
18916     },
18917
18918     /**
18919      * Clear all selections
18920      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18921      */
18922     clearSelections : function(suppressEvent){
18923         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18924             this.cmp.elements = this.selections;
18925             this.cmp.removeClass(this.selectedClass);
18926             this.selections = [];
18927             if(!suppressEvent){
18928                 this.fireEvent("selectionchange", this, this.selections);
18929             }
18930         }
18931     },
18932
18933     /**
18934      * Returns true if the passed node is selected
18935      * @param {HTMLElement/Number} node The node or node index
18936      * @return {Boolean}
18937      */
18938     isSelected : function(node){
18939         var s = this.selections;
18940         if(s.length < 1){
18941             return false;
18942         }
18943         node = this.getNode(node);
18944         return s.indexOf(node) !== -1;
18945     },
18946
18947     /**
18948      * Selects nodes.
18949      * @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
18950      * @param {Boolean} keepExisting (optional) true to keep existing selections
18951      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18952      */
18953     select : function(nodeInfo, keepExisting, suppressEvent){
18954         if(nodeInfo instanceof Array){
18955             if(!keepExisting){
18956                 this.clearSelections(true);
18957             }
18958             for(var i = 0, len = nodeInfo.length; i < len; i++){
18959                 this.select(nodeInfo[i], true, true);
18960             }
18961             return;
18962         } 
18963         var node = this.getNode(nodeInfo);
18964         if(!node || this.isSelected(node)){
18965             return; // already selected.
18966         }
18967         if(!keepExisting){
18968             this.clearSelections(true);
18969         }
18970         
18971         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18972             Roo.fly(node).addClass(this.selectedClass);
18973             this.selections.push(node);
18974             if(!suppressEvent){
18975                 this.fireEvent("selectionchange", this, this.selections);
18976             }
18977         }
18978         
18979         
18980     },
18981       /**
18982      * Unselects nodes.
18983      * @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
18984      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18985      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18986      */
18987     unselect : function(nodeInfo, keepExisting, suppressEvent)
18988     {
18989         if(nodeInfo instanceof Array){
18990             Roo.each(this.selections, function(s) {
18991                 this.unselect(s, nodeInfo);
18992             }, this);
18993             return;
18994         }
18995         var node = this.getNode(nodeInfo);
18996         if(!node || !this.isSelected(node)){
18997             //Roo.log("not selected");
18998             return; // not selected.
18999         }
19000         // fireevent???
19001         var ns = [];
19002         Roo.each(this.selections, function(s) {
19003             if (s == node ) {
19004                 Roo.fly(node).removeClass(this.selectedClass);
19005
19006                 return;
19007             }
19008             ns.push(s);
19009         },this);
19010         
19011         this.selections= ns;
19012         this.fireEvent("selectionchange", this, this.selections);
19013     },
19014
19015     /**
19016      * Gets a template node.
19017      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19018      * @return {HTMLElement} The node or null if it wasn't found
19019      */
19020     getNode : function(nodeInfo){
19021         if(typeof nodeInfo == "string"){
19022             return document.getElementById(nodeInfo);
19023         }else if(typeof nodeInfo == "number"){
19024             return this.nodes[nodeInfo];
19025         }
19026         return nodeInfo;
19027     },
19028
19029     /**
19030      * Gets a range template nodes.
19031      * @param {Number} startIndex
19032      * @param {Number} endIndex
19033      * @return {Array} An array of nodes
19034      */
19035     getNodes : function(start, end){
19036         var ns = this.nodes;
19037         start = start || 0;
19038         end = typeof end == "undefined" ? ns.length - 1 : end;
19039         var nodes = [];
19040         if(start <= end){
19041             for(var i = start; i <= end; i++){
19042                 nodes.push(ns[i]);
19043             }
19044         } else{
19045             for(var i = start; i >= end; i--){
19046                 nodes.push(ns[i]);
19047             }
19048         }
19049         return nodes;
19050     },
19051
19052     /**
19053      * Finds the index of the passed node
19054      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19055      * @return {Number} The index of the node or -1
19056      */
19057     indexOf : function(node){
19058         node = this.getNode(node);
19059         if(typeof node.nodeIndex == "number"){
19060             return node.nodeIndex;
19061         }
19062         var ns = this.nodes;
19063         for(var i = 0, len = ns.length; i < len; i++){
19064             if(ns[i] == node){
19065                 return i;
19066             }
19067         }
19068         return -1;
19069     }
19070 });
19071 /*
19072  * - LGPL
19073  *
19074  * based on jquery fullcalendar
19075  * 
19076  */
19077
19078 Roo.bootstrap = Roo.bootstrap || {};
19079 /**
19080  * @class Roo.bootstrap.Calendar
19081  * @extends Roo.bootstrap.Component
19082  * Bootstrap Calendar class
19083  * @cfg {Boolean} loadMask (true|false) default false
19084  * @cfg {Object} header generate the user specific header of the calendar, default false
19085
19086  * @constructor
19087  * Create a new Container
19088  * @param {Object} config The config object
19089  */
19090
19091
19092
19093 Roo.bootstrap.Calendar = function(config){
19094     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19095      this.addEvents({
19096         /**
19097              * @event select
19098              * Fires when a date is selected
19099              * @param {DatePicker} this
19100              * @param {Date} date The selected date
19101              */
19102         'select': true,
19103         /**
19104              * @event monthchange
19105              * Fires when the displayed month changes 
19106              * @param {DatePicker} this
19107              * @param {Date} date The selected month
19108              */
19109         'monthchange': true,
19110         /**
19111              * @event evententer
19112              * Fires when mouse over an event
19113              * @param {Calendar} this
19114              * @param {event} Event
19115              */
19116         'evententer': true,
19117         /**
19118              * @event eventleave
19119              * Fires when the mouse leaves an
19120              * @param {Calendar} this
19121              * @param {event}
19122              */
19123         'eventleave': true,
19124         /**
19125              * @event eventclick
19126              * Fires when the mouse click an
19127              * @param {Calendar} this
19128              * @param {event}
19129              */
19130         'eventclick': true
19131         
19132     });
19133
19134 };
19135
19136 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19137     
19138      /**
19139      * @cfg {Number} startDay
19140      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19141      */
19142     startDay : 0,
19143     
19144     loadMask : false,
19145     
19146     header : false,
19147       
19148     getAutoCreate : function(){
19149         
19150         
19151         var fc_button = function(name, corner, style, content ) {
19152             return Roo.apply({},{
19153                 tag : 'span',
19154                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19155                          (corner.length ?
19156                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19157                             ''
19158                         ),
19159                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19160                 unselectable: 'on'
19161             });
19162         };
19163         
19164         var header = {};
19165         
19166         if(!this.header){
19167             header = {
19168                 tag : 'table',
19169                 cls : 'fc-header',
19170                 style : 'width:100%',
19171                 cn : [
19172                     {
19173                         tag: 'tr',
19174                         cn : [
19175                             {
19176                                 tag : 'td',
19177                                 cls : 'fc-header-left',
19178                                 cn : [
19179                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19180                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19181                                     { tag: 'span', cls: 'fc-header-space' },
19182                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19183
19184
19185                                 ]
19186                             },
19187
19188                             {
19189                                 tag : 'td',
19190                                 cls : 'fc-header-center',
19191                                 cn : [
19192                                     {
19193                                         tag: 'span',
19194                                         cls: 'fc-header-title',
19195                                         cn : {
19196                                             tag: 'H2',
19197                                             html : 'month / year'
19198                                         }
19199                                     }
19200
19201                                 ]
19202                             },
19203                             {
19204                                 tag : 'td',
19205                                 cls : 'fc-header-right',
19206                                 cn : [
19207                               /*      fc_button('month', 'left', '', 'month' ),
19208                                     fc_button('week', '', '', 'week' ),
19209                                     fc_button('day', 'right', '', 'day' )
19210                                 */    
19211
19212                                 ]
19213                             }
19214
19215                         ]
19216                     }
19217                 ]
19218             };
19219         }
19220         
19221         header = this.header;
19222         
19223        
19224         var cal_heads = function() {
19225             var ret = [];
19226             // fixme - handle this.
19227             
19228             for (var i =0; i < Date.dayNames.length; i++) {
19229                 var d = Date.dayNames[i];
19230                 ret.push({
19231                     tag: 'th',
19232                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19233                     html : d.substring(0,3)
19234                 });
19235                 
19236             }
19237             ret[0].cls += ' fc-first';
19238             ret[6].cls += ' fc-last';
19239             return ret;
19240         };
19241         var cal_cell = function(n) {
19242             return  {
19243                 tag: 'td',
19244                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19245                 cn : [
19246                     {
19247                         cn : [
19248                             {
19249                                 cls: 'fc-day-number',
19250                                 html: 'D'
19251                             },
19252                             {
19253                                 cls: 'fc-day-content',
19254                              
19255                                 cn : [
19256                                      {
19257                                         style: 'position: relative;' // height: 17px;
19258                                     }
19259                                 ]
19260                             }
19261                             
19262                             
19263                         ]
19264                     }
19265                 ]
19266                 
19267             }
19268         };
19269         var cal_rows = function() {
19270             
19271             var ret = [];
19272             for (var r = 0; r < 6; r++) {
19273                 var row= {
19274                     tag : 'tr',
19275                     cls : 'fc-week',
19276                     cn : []
19277                 };
19278                 
19279                 for (var i =0; i < Date.dayNames.length; i++) {
19280                     var d = Date.dayNames[i];
19281                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19282
19283                 }
19284                 row.cn[0].cls+=' fc-first';
19285                 row.cn[0].cn[0].style = 'min-height:90px';
19286                 row.cn[6].cls+=' fc-last';
19287                 ret.push(row);
19288                 
19289             }
19290             ret[0].cls += ' fc-first';
19291             ret[4].cls += ' fc-prev-last';
19292             ret[5].cls += ' fc-last';
19293             return ret;
19294             
19295         };
19296         
19297         var cal_table = {
19298             tag: 'table',
19299             cls: 'fc-border-separate',
19300             style : 'width:100%',
19301             cellspacing  : 0,
19302             cn : [
19303                 { 
19304                     tag: 'thead',
19305                     cn : [
19306                         { 
19307                             tag: 'tr',
19308                             cls : 'fc-first fc-last',
19309                             cn : cal_heads()
19310                         }
19311                     ]
19312                 },
19313                 { 
19314                     tag: 'tbody',
19315                     cn : cal_rows()
19316                 }
19317                   
19318             ]
19319         };
19320          
19321          var cfg = {
19322             cls : 'fc fc-ltr',
19323             cn : [
19324                 header,
19325                 {
19326                     cls : 'fc-content',
19327                     style : "position: relative;",
19328                     cn : [
19329                         {
19330                             cls : 'fc-view fc-view-month fc-grid',
19331                             style : 'position: relative',
19332                             unselectable : 'on',
19333                             cn : [
19334                                 {
19335                                     cls : 'fc-event-container',
19336                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19337                                 },
19338                                 cal_table
19339                             ]
19340                         }
19341                     ]
19342     
19343                 }
19344            ] 
19345             
19346         };
19347         
19348          
19349         
19350         return cfg;
19351     },
19352     
19353     
19354     initEvents : function()
19355     {
19356         if(!this.store){
19357             throw "can not find store for calendar";
19358         }
19359         
19360         var mark = {
19361             tag: "div",
19362             cls:"x-dlg-mask",
19363             style: "text-align:center",
19364             cn: [
19365                 {
19366                     tag: "div",
19367                     style: "background-color:white;width:50%;margin:250 auto",
19368                     cn: [
19369                         {
19370                             tag: "img",
19371                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19372                         },
19373                         {
19374                             tag: "span",
19375                             html: "Loading"
19376                         }
19377                         
19378                     ]
19379                 }
19380             ]
19381         };
19382         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19383         
19384         var size = this.el.select('.fc-content', true).first().getSize();
19385         this.maskEl.setSize(size.width, size.height);
19386         this.maskEl.enableDisplayMode("block");
19387         if(!this.loadMask){
19388             this.maskEl.hide();
19389         }
19390         
19391         this.store = Roo.factory(this.store, Roo.data);
19392         this.store.on('load', this.onLoad, this);
19393         this.store.on('beforeload', this.onBeforeLoad, this);
19394         
19395         this.resize();
19396         
19397         this.cells = this.el.select('.fc-day',true);
19398         //Roo.log(this.cells);
19399         this.textNodes = this.el.query('.fc-day-number');
19400         this.cells.addClassOnOver('fc-state-hover');
19401         
19402         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19403         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19404         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19405         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19406         
19407         this.on('monthchange', this.onMonthChange, this);
19408         
19409         this.update(new Date().clearTime());
19410     },
19411     
19412     resize : function() {
19413         var sz  = this.el.getSize();
19414         
19415         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19416         this.el.select('.fc-day-content div',true).setHeight(34);
19417     },
19418     
19419     
19420     // private
19421     showPrevMonth : function(e){
19422         this.update(this.activeDate.add("mo", -1));
19423     },
19424     showToday : function(e){
19425         this.update(new Date().clearTime());
19426     },
19427     // private
19428     showNextMonth : function(e){
19429         this.update(this.activeDate.add("mo", 1));
19430     },
19431
19432     // private
19433     showPrevYear : function(){
19434         this.update(this.activeDate.add("y", -1));
19435     },
19436
19437     // private
19438     showNextYear : function(){
19439         this.update(this.activeDate.add("y", 1));
19440     },
19441
19442     
19443    // private
19444     update : function(date)
19445     {
19446         var vd = this.activeDate;
19447         this.activeDate = date;
19448 //        if(vd && this.el){
19449 //            var t = date.getTime();
19450 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19451 //                Roo.log('using add remove');
19452 //                
19453 //                this.fireEvent('monthchange', this, date);
19454 //                
19455 //                this.cells.removeClass("fc-state-highlight");
19456 //                this.cells.each(function(c){
19457 //                   if(c.dateValue == t){
19458 //                       c.addClass("fc-state-highlight");
19459 //                       setTimeout(function(){
19460 //                            try{c.dom.firstChild.focus();}catch(e){}
19461 //                       }, 50);
19462 //                       return false;
19463 //                   }
19464 //                   return true;
19465 //                });
19466 //                return;
19467 //            }
19468 //        }
19469         
19470         var days = date.getDaysInMonth();
19471         
19472         var firstOfMonth = date.getFirstDateOfMonth();
19473         var startingPos = firstOfMonth.getDay()-this.startDay;
19474         
19475         if(startingPos < this.startDay){
19476             startingPos += 7;
19477         }
19478         
19479         var pm = date.add(Date.MONTH, -1);
19480         var prevStart = pm.getDaysInMonth()-startingPos;
19481 //        
19482         this.cells = this.el.select('.fc-day',true);
19483         this.textNodes = this.el.query('.fc-day-number');
19484         this.cells.addClassOnOver('fc-state-hover');
19485         
19486         var cells = this.cells.elements;
19487         var textEls = this.textNodes;
19488         
19489         Roo.each(cells, function(cell){
19490             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19491         });
19492         
19493         days += startingPos;
19494
19495         // convert everything to numbers so it's fast
19496         var day = 86400000;
19497         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19498         //Roo.log(d);
19499         //Roo.log(pm);
19500         //Roo.log(prevStart);
19501         
19502         var today = new Date().clearTime().getTime();
19503         var sel = date.clearTime().getTime();
19504         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19505         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19506         var ddMatch = this.disabledDatesRE;
19507         var ddText = this.disabledDatesText;
19508         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19509         var ddaysText = this.disabledDaysText;
19510         var format = this.format;
19511         
19512         var setCellClass = function(cal, cell){
19513             cell.row = 0;
19514             cell.events = [];
19515             cell.more = [];
19516             //Roo.log('set Cell Class');
19517             cell.title = "";
19518             var t = d.getTime();
19519             
19520             //Roo.log(d);
19521             
19522             cell.dateValue = t;
19523             if(t == today){
19524                 cell.className += " fc-today";
19525                 cell.className += " fc-state-highlight";
19526                 cell.title = cal.todayText;
19527             }
19528             if(t == sel){
19529                 // disable highlight in other month..
19530                 //cell.className += " fc-state-highlight";
19531                 
19532             }
19533             // disabling
19534             if(t < min) {
19535                 cell.className = " fc-state-disabled";
19536                 cell.title = cal.minText;
19537                 return;
19538             }
19539             if(t > max) {
19540                 cell.className = " fc-state-disabled";
19541                 cell.title = cal.maxText;
19542                 return;
19543             }
19544             if(ddays){
19545                 if(ddays.indexOf(d.getDay()) != -1){
19546                     cell.title = ddaysText;
19547                     cell.className = " fc-state-disabled";
19548                 }
19549             }
19550             if(ddMatch && format){
19551                 var fvalue = d.dateFormat(format);
19552                 if(ddMatch.test(fvalue)){
19553                     cell.title = ddText.replace("%0", fvalue);
19554                     cell.className = " fc-state-disabled";
19555                 }
19556             }
19557             
19558             if (!cell.initialClassName) {
19559                 cell.initialClassName = cell.dom.className;
19560             }
19561             
19562             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19563         };
19564
19565         var i = 0;
19566         
19567         for(; i < startingPos; i++) {
19568             textEls[i].innerHTML = (++prevStart);
19569             d.setDate(d.getDate()+1);
19570             
19571             cells[i].className = "fc-past fc-other-month";
19572             setCellClass(this, cells[i]);
19573         }
19574         
19575         var intDay = 0;
19576         
19577         for(; i < days; i++){
19578             intDay = i - startingPos + 1;
19579             textEls[i].innerHTML = (intDay);
19580             d.setDate(d.getDate()+1);
19581             
19582             cells[i].className = ''; // "x-date-active";
19583             setCellClass(this, cells[i]);
19584         }
19585         var extraDays = 0;
19586         
19587         for(; i < 42; i++) {
19588             textEls[i].innerHTML = (++extraDays);
19589             d.setDate(d.getDate()+1);
19590             
19591             cells[i].className = "fc-future fc-other-month";
19592             setCellClass(this, cells[i]);
19593         }
19594         
19595         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19596         
19597         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19598         
19599         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19600         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19601         
19602         if(totalRows != 6){
19603             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19604             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19605         }
19606         
19607         this.fireEvent('monthchange', this, date);
19608         
19609         
19610         /*
19611         if(!this.internalRender){
19612             var main = this.el.dom.firstChild;
19613             var w = main.offsetWidth;
19614             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19615             Roo.fly(main).setWidth(w);
19616             this.internalRender = true;
19617             // opera does not respect the auto grow header center column
19618             // then, after it gets a width opera refuses to recalculate
19619             // without a second pass
19620             if(Roo.isOpera && !this.secondPass){
19621                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19622                 this.secondPass = true;
19623                 this.update.defer(10, this, [date]);
19624             }
19625         }
19626         */
19627         
19628     },
19629     
19630     findCell : function(dt) {
19631         dt = dt.clearTime().getTime();
19632         var ret = false;
19633         this.cells.each(function(c){
19634             //Roo.log("check " +c.dateValue + '?=' + dt);
19635             if(c.dateValue == dt){
19636                 ret = c;
19637                 return false;
19638             }
19639             return true;
19640         });
19641         
19642         return ret;
19643     },
19644     
19645     findCells : function(ev) {
19646         var s = ev.start.clone().clearTime().getTime();
19647        // Roo.log(s);
19648         var e= ev.end.clone().clearTime().getTime();
19649        // Roo.log(e);
19650         var ret = [];
19651         this.cells.each(function(c){
19652              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19653             
19654             if(c.dateValue > e){
19655                 return ;
19656             }
19657             if(c.dateValue < s){
19658                 return ;
19659             }
19660             ret.push(c);
19661         });
19662         
19663         return ret;    
19664     },
19665     
19666 //    findBestRow: function(cells)
19667 //    {
19668 //        var ret = 0;
19669 //        
19670 //        for (var i =0 ; i < cells.length;i++) {
19671 //            ret  = Math.max(cells[i].rows || 0,ret);
19672 //        }
19673 //        return ret;
19674 //        
19675 //    },
19676     
19677     
19678     addItem : function(ev)
19679     {
19680         // look for vertical location slot in
19681         var cells = this.findCells(ev);
19682         
19683 //        ev.row = this.findBestRow(cells);
19684         
19685         // work out the location.
19686         
19687         var crow = false;
19688         var rows = [];
19689         for(var i =0; i < cells.length; i++) {
19690             
19691             cells[i].row = cells[0].row;
19692             
19693             if(i == 0){
19694                 cells[i].row = cells[i].row + 1;
19695             }
19696             
19697             if (!crow) {
19698                 crow = {
19699                     start : cells[i],
19700                     end :  cells[i]
19701                 };
19702                 continue;
19703             }
19704             if (crow.start.getY() == cells[i].getY()) {
19705                 // on same row.
19706                 crow.end = cells[i];
19707                 continue;
19708             }
19709             // different row.
19710             rows.push(crow);
19711             crow = {
19712                 start: cells[i],
19713                 end : cells[i]
19714             };
19715             
19716         }
19717         
19718         rows.push(crow);
19719         ev.els = [];
19720         ev.rows = rows;
19721         ev.cells = cells;
19722         
19723         cells[0].events.push(ev);
19724         
19725         this.calevents.push(ev);
19726     },
19727     
19728     clearEvents: function() {
19729         
19730         if(!this.calevents){
19731             return;
19732         }
19733         
19734         Roo.each(this.cells.elements, function(c){
19735             c.row = 0;
19736             c.events = [];
19737             c.more = [];
19738         });
19739         
19740         Roo.each(this.calevents, function(e) {
19741             Roo.each(e.els, function(el) {
19742                 el.un('mouseenter' ,this.onEventEnter, this);
19743                 el.un('mouseleave' ,this.onEventLeave, this);
19744                 el.remove();
19745             },this);
19746         },this);
19747         
19748         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19749             e.remove();
19750         });
19751         
19752     },
19753     
19754     renderEvents: function()
19755     {   
19756         var _this = this;
19757         
19758         this.cells.each(function(c) {
19759             
19760             if(c.row < 5){
19761                 return;
19762             }
19763             
19764             var ev = c.events;
19765             
19766             var r = 4;
19767             if(c.row != c.events.length){
19768                 r = 4 - (4 - (c.row - c.events.length));
19769             }
19770             
19771             c.events = ev.slice(0, r);
19772             c.more = ev.slice(r);
19773             
19774             if(c.more.length && c.more.length == 1){
19775                 c.events.push(c.more.pop());
19776             }
19777             
19778             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19779             
19780         });
19781             
19782         this.cells.each(function(c) {
19783             
19784             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19785             
19786             
19787             for (var e = 0; e < c.events.length; e++){
19788                 var ev = c.events[e];
19789                 var rows = ev.rows;
19790                 
19791                 for(var i = 0; i < rows.length; i++) {
19792                 
19793                     // how many rows should it span..
19794
19795                     var  cfg = {
19796                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19797                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19798
19799                         unselectable : "on",
19800                         cn : [
19801                             {
19802                                 cls: 'fc-event-inner',
19803                                 cn : [
19804     //                                {
19805     //                                  tag:'span',
19806     //                                  cls: 'fc-event-time',
19807     //                                  html : cells.length > 1 ? '' : ev.time
19808     //                                },
19809                                     {
19810                                       tag:'span',
19811                                       cls: 'fc-event-title',
19812                                       html : String.format('{0}', ev.title)
19813                                     }
19814
19815
19816                                 ]
19817                             },
19818                             {
19819                                 cls: 'ui-resizable-handle ui-resizable-e',
19820                                 html : '&nbsp;&nbsp;&nbsp'
19821                             }
19822
19823                         ]
19824                     };
19825
19826                     if (i == 0) {
19827                         cfg.cls += ' fc-event-start';
19828                     }
19829                     if ((i+1) == rows.length) {
19830                         cfg.cls += ' fc-event-end';
19831                     }
19832
19833                     var ctr = _this.el.select('.fc-event-container',true).first();
19834                     var cg = ctr.createChild(cfg);
19835
19836                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19837                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19838
19839                     var r = (c.more.length) ? 1 : 0;
19840                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19841                     cg.setWidth(ebox.right - sbox.x -2);
19842
19843                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19844                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19845                     cg.on('click', _this.onEventClick, _this, ev);
19846
19847                     ev.els.push(cg);
19848                     
19849                 }
19850                 
19851             }
19852             
19853             
19854             if(c.more.length){
19855                 var  cfg = {
19856                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19857                     style : 'position: absolute',
19858                     unselectable : "on",
19859                     cn : [
19860                         {
19861                             cls: 'fc-event-inner',
19862                             cn : [
19863                                 {
19864                                   tag:'span',
19865                                   cls: 'fc-event-title',
19866                                   html : 'More'
19867                                 }
19868
19869
19870                             ]
19871                         },
19872                         {
19873                             cls: 'ui-resizable-handle ui-resizable-e',
19874                             html : '&nbsp;&nbsp;&nbsp'
19875                         }
19876
19877                     ]
19878                 };
19879
19880                 var ctr = _this.el.select('.fc-event-container',true).first();
19881                 var cg = ctr.createChild(cfg);
19882
19883                 var sbox = c.select('.fc-day-content',true).first().getBox();
19884                 var ebox = c.select('.fc-day-content',true).first().getBox();
19885                 //Roo.log(cg);
19886                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19887                 cg.setWidth(ebox.right - sbox.x -2);
19888
19889                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19890                 
19891             }
19892             
19893         });
19894         
19895         
19896         
19897     },
19898     
19899     onEventEnter: function (e, el,event,d) {
19900         this.fireEvent('evententer', this, el, event);
19901     },
19902     
19903     onEventLeave: function (e, el,event,d) {
19904         this.fireEvent('eventleave', this, el, event);
19905     },
19906     
19907     onEventClick: function (e, el,event,d) {
19908         this.fireEvent('eventclick', this, el, event);
19909     },
19910     
19911     onMonthChange: function () {
19912         this.store.load();
19913     },
19914     
19915     onMoreEventClick: function(e, el, more)
19916     {
19917         var _this = this;
19918         
19919         this.calpopover.placement = 'right';
19920         this.calpopover.setTitle('More');
19921         
19922         this.calpopover.setContent('');
19923         
19924         var ctr = this.calpopover.el.select('.popover-content', true).first();
19925         
19926         Roo.each(more, function(m){
19927             var cfg = {
19928                 cls : 'fc-event-hori fc-event-draggable',
19929                 html : m.title
19930             };
19931             var cg = ctr.createChild(cfg);
19932             
19933             cg.on('click', _this.onEventClick, _this, m);
19934         });
19935         
19936         this.calpopover.show(el);
19937         
19938         
19939     },
19940     
19941     onLoad: function () 
19942     {   
19943         this.calevents = [];
19944         var cal = this;
19945         
19946         if(this.store.getCount() > 0){
19947             this.store.data.each(function(d){
19948                cal.addItem({
19949                     id : d.data.id,
19950                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19951                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19952                     time : d.data.start_time,
19953                     title : d.data.title,
19954                     description : d.data.description,
19955                     venue : d.data.venue
19956                 });
19957             });
19958         }
19959         
19960         this.renderEvents();
19961         
19962         if(this.calevents.length && this.loadMask){
19963             this.maskEl.hide();
19964         }
19965     },
19966     
19967     onBeforeLoad: function()
19968     {
19969         this.clearEvents();
19970         if(this.loadMask){
19971             this.maskEl.show();
19972         }
19973     }
19974 });
19975
19976  
19977  /*
19978  * - LGPL
19979  *
19980  * element
19981  * 
19982  */
19983
19984 /**
19985  * @class Roo.bootstrap.Popover
19986  * @extends Roo.bootstrap.Component
19987  * Bootstrap Popover class
19988  * @cfg {String} html contents of the popover   (or false to use children..)
19989  * @cfg {String} title of popover (or false to hide)
19990  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19991  * @cfg {String} trigger click || hover (or false to trigger manually)
19992  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19993  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19994  *      - if false and it has a 'parent' then it will be automatically added to that element
19995  *      - if string - Roo.get  will be called 
19996  * @cfg {Number} delay - delay before showing
19997  
19998  * @constructor
19999  * Create a new Popover
20000  * @param {Object} config The config object
20001  */
20002
20003 Roo.bootstrap.Popover = function(config){
20004     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20005     
20006     this.addEvents({
20007         // raw events
20008          /**
20009          * @event show
20010          * After the popover show
20011          * 
20012          * @param {Roo.bootstrap.Popover} this
20013          */
20014         "show" : true,
20015         /**
20016          * @event hide
20017          * After the popover hide
20018          * 
20019          * @param {Roo.bootstrap.Popover} this
20020          */
20021         "hide" : true
20022     });
20023 };
20024
20025 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20026     
20027     title: false,
20028     html: false,
20029     
20030     placement : 'right',
20031     trigger : 'hover', // hover
20032     modal : false,
20033     delay : 0,
20034     
20035     over: false,
20036     
20037     can_build_overlaid : false,
20038     
20039     maskEl : false, // the mask element
20040     headerEl : false,
20041     contentEl : false,
20042     alignEl : false, // when show is called with an element - this get's stored.
20043     
20044     getChildContainer : function()
20045     {
20046         return this.contentEl;
20047         
20048     },
20049     getPopoverHeader : function()
20050     {
20051         this.title = true; // flag not to hide it..
20052         this.headerEl.addClass('p-0');
20053         return this.headerEl
20054     },
20055     
20056     
20057     getAutoCreate : function(){
20058          
20059         var cfg = {
20060            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20061            style: 'display:block',
20062            cn : [
20063                 {
20064                     cls : 'arrow'
20065                 },
20066                 {
20067                     cls : 'popover-inner ',
20068                     cn : [
20069                         {
20070                             tag: 'h3',
20071                             cls: 'popover-title popover-header',
20072                             html : this.title === false ? '' : this.title
20073                         },
20074                         {
20075                             cls : 'popover-content popover-body '  + (this.cls || ''),
20076                             html : this.html || ''
20077                         }
20078                     ]
20079                     
20080                 }
20081            ]
20082         };
20083         
20084         return cfg;
20085     },
20086     /**
20087      * @param {string} the title
20088      */
20089     setTitle: function(str)
20090     {
20091         this.title = str;
20092         if (this.el) {
20093             this.headerEl.dom.innerHTML = str;
20094         }
20095         
20096     },
20097     /**
20098      * @param {string} the body content
20099      */
20100     setContent: function(str)
20101     {
20102         this.html = str;
20103         if (this.contentEl) {
20104             this.contentEl.dom.innerHTML = str;
20105         }
20106         
20107     },
20108     // as it get's added to the bottom of the page.
20109     onRender : function(ct, position)
20110     {
20111         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20112         
20113         
20114         
20115         if(!this.el){
20116             var cfg = Roo.apply({},  this.getAutoCreate());
20117             cfg.id = Roo.id();
20118             
20119             if (this.cls) {
20120                 cfg.cls += ' ' + this.cls;
20121             }
20122             if (this.style) {
20123                 cfg.style = this.style;
20124             }
20125             //Roo.log("adding to ");
20126             this.el = Roo.get(document.body).createChild(cfg, position);
20127 //            Roo.log(this.el);
20128         }
20129         
20130         this.contentEl = this.el.select('.popover-content',true).first();
20131         this.headerEl =  this.el.select('.popover-title',true).first();
20132         
20133         var nitems = [];
20134         if(typeof(this.items) != 'undefined'){
20135             var items = this.items;
20136             delete this.items;
20137
20138             for(var i =0;i < items.length;i++) {
20139                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20140             }
20141         }
20142
20143         this.items = nitems;
20144         
20145         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20146         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20147         
20148         
20149         
20150         this.initEvents();
20151     },
20152     
20153     resizeMask : function()
20154     {
20155         this.maskEl.setSize(
20156             Roo.lib.Dom.getViewWidth(true),
20157             Roo.lib.Dom.getViewHeight(true)
20158         );
20159     },
20160     
20161     initEvents : function()
20162     {
20163         
20164         if (!this.modal) { 
20165             Roo.bootstrap.Popover.register(this);
20166         }
20167          
20168         this.arrowEl = this.el.select('.arrow',true).first();
20169         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20170         this.el.enableDisplayMode('block');
20171         this.el.hide();
20172  
20173         
20174         if (this.over === false && !this.parent()) {
20175             return; 
20176         }
20177         if (this.triggers === false) {
20178             return;
20179         }
20180          
20181         // support parent
20182         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20183         var triggers = this.trigger ? this.trigger.split(' ') : [];
20184         Roo.each(triggers, function(trigger) {
20185         
20186             if (trigger == 'click') {
20187                 on_el.on('click', this.toggle, this);
20188             } else if (trigger != 'manual') {
20189                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20190                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20191       
20192                 on_el.on(eventIn  ,this.enter, this);
20193                 on_el.on(eventOut, this.leave, this);
20194             }
20195         }, this);
20196     },
20197     
20198     
20199     // private
20200     timeout : null,
20201     hoverState : null,
20202     
20203     toggle : function () {
20204         this.hoverState == 'in' ? this.leave() : this.enter();
20205     },
20206     
20207     enter : function () {
20208         
20209         clearTimeout(this.timeout);
20210     
20211         this.hoverState = 'in';
20212     
20213         if (!this.delay || !this.delay.show) {
20214             this.show();
20215             return;
20216         }
20217         var _t = this;
20218         this.timeout = setTimeout(function () {
20219             if (_t.hoverState == 'in') {
20220                 _t.show();
20221             }
20222         }, this.delay.show)
20223     },
20224     
20225     leave : function() {
20226         clearTimeout(this.timeout);
20227     
20228         this.hoverState = 'out';
20229     
20230         if (!this.delay || !this.delay.hide) {
20231             this.hide();
20232             return;
20233         }
20234         var _t = this;
20235         this.timeout = setTimeout(function () {
20236             if (_t.hoverState == 'out') {
20237                 _t.hide();
20238             }
20239         }, this.delay.hide)
20240     },
20241     /**
20242      * Show the popover
20243      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20244      * @param {string} (left|right|top|bottom) position
20245      */
20246     show : function (on_el, placement)
20247     {
20248         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20249         on_el = on_el || false; // default to false
20250          
20251         if (!on_el) {
20252             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20253                 on_el = this.parent().el;
20254             } else if (this.over) {
20255                 on_el = Roo.get(this.over);
20256             }
20257             
20258         }
20259         
20260         this.alignEl = Roo.get( on_el );
20261
20262         if (!this.el) {
20263             this.render(document.body);
20264         }
20265         
20266         
20267          
20268         
20269         if (this.title === false) {
20270             this.headerEl.hide();
20271         }
20272         
20273        
20274         this.el.show();
20275         this.el.dom.style.display = 'block';
20276          
20277  
20278         if (this.alignEl) {
20279             this.updatePosition(this.placement, true);
20280              
20281         } else {
20282             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20283             var es = this.el.getSize();
20284             var x = Roo.lib.Dom.getViewWidth()/2;
20285             var y = Roo.lib.Dom.getViewHeight()/2;
20286             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20287             
20288         }
20289
20290         
20291         //var arrow = this.el.select('.arrow',true).first();
20292         //arrow.set(align[2], 
20293         
20294         this.el.addClass('in');
20295         
20296          
20297         
20298         this.hoverState = 'in';
20299         
20300         if (this.modal) {
20301             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20302             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20303             this.maskEl.dom.style.display = 'block';
20304             this.maskEl.addClass('show');
20305         }
20306         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20307  
20308         this.fireEvent('show', this);
20309         
20310     },
20311     /**
20312      * fire this manually after loading a grid in the table for example
20313      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20314      * @param {Boolean} try and move it if we cant get right position.
20315      */
20316     updatePosition : function(placement, try_move)
20317     {
20318         // allow for calling with no parameters
20319         placement = placement   ? placement :  this.placement;
20320         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20321         
20322         this.el.removeClass([
20323             'fade','top','bottom', 'left', 'right','in',
20324             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20325         ]);
20326         this.el.addClass(placement + ' bs-popover-' + placement);
20327         
20328         if (!this.alignEl ) {
20329             return false;
20330         }
20331         
20332         switch (placement) {
20333             case 'right':
20334                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20335                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20336                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20337                     //normal display... or moved up/down.
20338                     this.el.setXY(offset);
20339                     var xy = this.alignEl.getAnchorXY('tr', false);
20340                     xy[0]+=2;xy[1]+=5;
20341                     this.arrowEl.setXY(xy);
20342                     return true;
20343                 }
20344                 // continue through...
20345                 return this.updatePosition('left', false);
20346                 
20347             
20348             case 'left':
20349                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20350                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20351                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20352                     //normal display... or moved up/down.
20353                     this.el.setXY(offset);
20354                     var xy = this.alignEl.getAnchorXY('tl', false);
20355                     xy[0]-=10;xy[1]+=5; // << fix me
20356                     this.arrowEl.setXY(xy);
20357                     return true;
20358                 }
20359                 // call self...
20360                 return this.updatePosition('right', false);
20361             
20362             case 'top':
20363                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20364                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[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('t', false);
20369                     xy[1]-=10; // << fix me
20370                     this.arrowEl.setXY(xy);
20371                     return true;
20372                 }
20373                 // fall through
20374                return this.updatePosition('bottom', false);
20375             
20376             case 'bottom':
20377                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20378                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20379                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20380                     //normal display... or moved up/down.
20381                     this.el.setXY(offset);
20382                     var xy = this.alignEl.getAnchorXY('b', false);
20383                      xy[1]+=2; // << fix me
20384                     this.arrowEl.setXY(xy);
20385                     return true;
20386                 }
20387                 // fall through
20388                 return this.updatePosition('top', false);
20389                 
20390             
20391         }
20392         
20393         
20394         return false;
20395     },
20396     
20397     hide : function()
20398     {
20399         this.el.setXY([0,0]);
20400         this.el.removeClass('in');
20401         this.el.hide();
20402         this.hoverState = null;
20403         this.maskEl.hide(); // always..
20404         this.fireEvent('hide', this);
20405     }
20406     
20407 });
20408
20409
20410 Roo.apply(Roo.bootstrap.Popover, {
20411
20412     alignment : {
20413         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20414         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20415         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20416         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20417     },
20418     
20419     zIndex : 20001,
20420
20421     clickHander : false,
20422     
20423     
20424
20425     onMouseDown : function(e)
20426     {
20427         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20428             /// what is nothing is showing..
20429             this.hideAll();
20430         }
20431          
20432     },
20433     
20434     
20435     popups : [],
20436     
20437     register : function(popup)
20438     {
20439         if (!Roo.bootstrap.Popover.clickHandler) {
20440             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20441         }
20442         // hide other popups.
20443         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20444         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20445         this.hideAll(); //<< why?
20446         //this.popups.push(popup);
20447     },
20448     hideAll : function()
20449     {
20450         this.popups.forEach(function(p) {
20451             p.hide();
20452         });
20453     },
20454     onShow : function() {
20455         Roo.bootstrap.Popover.popups.push(this);
20456     },
20457     onHide : function() {
20458         Roo.bootstrap.Popover.popups.remove(this);
20459     } 
20460
20461 });/*
20462  * - LGPL
20463  *
20464  * Card header - holder for the card header elements.
20465  * 
20466  */
20467
20468 /**
20469  * @class Roo.bootstrap.PopoverNav
20470  * @extends Roo.bootstrap.NavGroup
20471  * Bootstrap Popover header navigation class
20472  * @constructor
20473  * Create a new Popover Header Navigation 
20474  * @param {Object} config The config object
20475  */
20476
20477 Roo.bootstrap.PopoverNav = function(config){
20478     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20479 };
20480
20481 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20482     
20483     
20484     container_method : 'getPopoverHeader' 
20485     
20486      
20487     
20488     
20489    
20490 });
20491
20492  
20493
20494  /*
20495  * - LGPL
20496  *
20497  * Progress
20498  * 
20499  */
20500
20501 /**
20502  * @class Roo.bootstrap.Progress
20503  * @extends Roo.bootstrap.Component
20504  * Bootstrap Progress class
20505  * @cfg {Boolean} striped striped of the progress bar
20506  * @cfg {Boolean} active animated of the progress bar
20507  * 
20508  * 
20509  * @constructor
20510  * Create a new Progress
20511  * @param {Object} config The config object
20512  */
20513
20514 Roo.bootstrap.Progress = function(config){
20515     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20516 };
20517
20518 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20519     
20520     striped : false,
20521     active: false,
20522     
20523     getAutoCreate : function(){
20524         var cfg = {
20525             tag: 'div',
20526             cls: 'progress'
20527         };
20528         
20529         
20530         if(this.striped){
20531             cfg.cls += ' progress-striped';
20532         }
20533       
20534         if(this.active){
20535             cfg.cls += ' active';
20536         }
20537         
20538         
20539         return cfg;
20540     }
20541    
20542 });
20543
20544  
20545
20546  /*
20547  * - LGPL
20548  *
20549  * ProgressBar
20550  * 
20551  */
20552
20553 /**
20554  * @class Roo.bootstrap.ProgressBar
20555  * @extends Roo.bootstrap.Component
20556  * Bootstrap ProgressBar class
20557  * @cfg {Number} aria_valuenow aria-value now
20558  * @cfg {Number} aria_valuemin aria-value min
20559  * @cfg {Number} aria_valuemax aria-value max
20560  * @cfg {String} label label for the progress bar
20561  * @cfg {String} panel (success | info | warning | danger )
20562  * @cfg {String} role role of the progress bar
20563  * @cfg {String} sr_only text
20564  * 
20565  * 
20566  * @constructor
20567  * Create a new ProgressBar
20568  * @param {Object} config The config object
20569  */
20570
20571 Roo.bootstrap.ProgressBar = function(config){
20572     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20573 };
20574
20575 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20576     
20577     aria_valuenow : 0,
20578     aria_valuemin : 0,
20579     aria_valuemax : 100,
20580     label : false,
20581     panel : false,
20582     role : false,
20583     sr_only: false,
20584     
20585     getAutoCreate : function()
20586     {
20587         
20588         var cfg = {
20589             tag: 'div',
20590             cls: 'progress-bar',
20591             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20592         };
20593         
20594         if(this.sr_only){
20595             cfg.cn = {
20596                 tag: 'span',
20597                 cls: 'sr-only',
20598                 html: this.sr_only
20599             }
20600         }
20601         
20602         if(this.role){
20603             cfg.role = this.role;
20604         }
20605         
20606         if(this.aria_valuenow){
20607             cfg['aria-valuenow'] = this.aria_valuenow;
20608         }
20609         
20610         if(this.aria_valuemin){
20611             cfg['aria-valuemin'] = this.aria_valuemin;
20612         }
20613         
20614         if(this.aria_valuemax){
20615             cfg['aria-valuemax'] = this.aria_valuemax;
20616         }
20617         
20618         if(this.label && !this.sr_only){
20619             cfg.html = this.label;
20620         }
20621         
20622         if(this.panel){
20623             cfg.cls += ' progress-bar-' + this.panel;
20624         }
20625         
20626         return cfg;
20627     },
20628     
20629     update : function(aria_valuenow)
20630     {
20631         this.aria_valuenow = aria_valuenow;
20632         
20633         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20634     }
20635    
20636 });
20637
20638  
20639
20640  /*
20641  * - LGPL
20642  *
20643  * column
20644  * 
20645  */
20646
20647 /**
20648  * @class Roo.bootstrap.TabGroup
20649  * @extends Roo.bootstrap.Column
20650  * Bootstrap Column class
20651  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20652  * @cfg {Boolean} carousel true to make the group behave like a carousel
20653  * @cfg {Boolean} bullets show bullets for the panels
20654  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20655  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20656  * @cfg {Boolean} showarrow (true|false) show arrow default true
20657  * 
20658  * @constructor
20659  * Create a new TabGroup
20660  * @param {Object} config The config object
20661  */
20662
20663 Roo.bootstrap.TabGroup = function(config){
20664     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20665     if (!this.navId) {
20666         this.navId = Roo.id();
20667     }
20668     this.tabs = [];
20669     Roo.bootstrap.TabGroup.register(this);
20670     
20671 };
20672
20673 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20674     
20675     carousel : false,
20676     transition : false,
20677     bullets : 0,
20678     timer : 0,
20679     autoslide : false,
20680     slideFn : false,
20681     slideOnTouch : false,
20682     showarrow : true,
20683     
20684     getAutoCreate : function()
20685     {
20686         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20687         
20688         cfg.cls += ' tab-content';
20689         
20690         if (this.carousel) {
20691             cfg.cls += ' carousel slide';
20692             
20693             cfg.cn = [{
20694                cls : 'carousel-inner',
20695                cn : []
20696             }];
20697         
20698             if(this.bullets  && !Roo.isTouch){
20699                 
20700                 var bullets = {
20701                     cls : 'carousel-bullets',
20702                     cn : []
20703                 };
20704                
20705                 if(this.bullets_cls){
20706                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20707                 }
20708                 
20709                 bullets.cn.push({
20710                     cls : 'clear'
20711                 });
20712                 
20713                 cfg.cn[0].cn.push(bullets);
20714             }
20715             
20716             if(this.showarrow){
20717                 cfg.cn[0].cn.push({
20718                     tag : 'div',
20719                     class : 'carousel-arrow',
20720                     cn : [
20721                         {
20722                             tag : 'div',
20723                             class : 'carousel-prev',
20724                             cn : [
20725                                 {
20726                                     tag : 'i',
20727                                     class : 'fa fa-chevron-left'
20728                                 }
20729                             ]
20730                         },
20731                         {
20732                             tag : 'div',
20733                             class : 'carousel-next',
20734                             cn : [
20735                                 {
20736                                     tag : 'i',
20737                                     class : 'fa fa-chevron-right'
20738                                 }
20739                             ]
20740                         }
20741                     ]
20742                 });
20743             }
20744             
20745         }
20746         
20747         return cfg;
20748     },
20749     
20750     initEvents:  function()
20751     {
20752 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20753 //            this.el.on("touchstart", this.onTouchStart, this);
20754 //        }
20755         
20756         if(this.autoslide){
20757             var _this = this;
20758             
20759             this.slideFn = window.setInterval(function() {
20760                 _this.showPanelNext();
20761             }, this.timer);
20762         }
20763         
20764         if(this.showarrow){
20765             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20766             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20767         }
20768         
20769         
20770     },
20771     
20772 //    onTouchStart : function(e, el, o)
20773 //    {
20774 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20775 //            return;
20776 //        }
20777 //        
20778 //        this.showPanelNext();
20779 //    },
20780     
20781     
20782     getChildContainer : function()
20783     {
20784         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20785     },
20786     
20787     /**
20788     * register a Navigation item
20789     * @param {Roo.bootstrap.NavItem} the navitem to add
20790     */
20791     register : function(item)
20792     {
20793         this.tabs.push( item);
20794         item.navId = this.navId; // not really needed..
20795         this.addBullet();
20796     
20797     },
20798     
20799     getActivePanel : function()
20800     {
20801         var r = false;
20802         Roo.each(this.tabs, function(t) {
20803             if (t.active) {
20804                 r = t;
20805                 return false;
20806             }
20807             return null;
20808         });
20809         return r;
20810         
20811     },
20812     getPanelByName : function(n)
20813     {
20814         var r = false;
20815         Roo.each(this.tabs, function(t) {
20816             if (t.tabId == n) {
20817                 r = t;
20818                 return false;
20819             }
20820             return null;
20821         });
20822         return r;
20823     },
20824     indexOfPanel : function(p)
20825     {
20826         var r = false;
20827         Roo.each(this.tabs, function(t,i) {
20828             if (t.tabId == p.tabId) {
20829                 r = i;
20830                 return false;
20831             }
20832             return null;
20833         });
20834         return r;
20835     },
20836     /**
20837      * show a specific panel
20838      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20839      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20840      */
20841     showPanel : function (pan)
20842     {
20843         if(this.transition || typeof(pan) == 'undefined'){
20844             Roo.log("waiting for the transitionend");
20845             return false;
20846         }
20847         
20848         if (typeof(pan) == 'number') {
20849             pan = this.tabs[pan];
20850         }
20851         
20852         if (typeof(pan) == 'string') {
20853             pan = this.getPanelByName(pan);
20854         }
20855         
20856         var cur = this.getActivePanel();
20857         
20858         if(!pan || !cur){
20859             Roo.log('pan or acitve pan is undefined');
20860             return false;
20861         }
20862         
20863         if (pan.tabId == this.getActivePanel().tabId) {
20864             return true;
20865         }
20866         
20867         if (false === cur.fireEvent('beforedeactivate')) {
20868             return false;
20869         }
20870         
20871         if(this.bullets > 0 && !Roo.isTouch){
20872             this.setActiveBullet(this.indexOfPanel(pan));
20873         }
20874         
20875         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20876             
20877             //class="carousel-item carousel-item-next carousel-item-left"
20878             
20879             this.transition = true;
20880             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20881             var lr = dir == 'next' ? 'left' : 'right';
20882             pan.el.addClass(dir); // or prev
20883             pan.el.addClass('carousel-item-' + dir); // or prev
20884             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20885             cur.el.addClass(lr); // or right
20886             pan.el.addClass(lr);
20887             cur.el.addClass('carousel-item-' +lr); // or right
20888             pan.el.addClass('carousel-item-' +lr);
20889             
20890             
20891             var _this = this;
20892             cur.el.on('transitionend', function() {
20893                 Roo.log("trans end?");
20894                 
20895                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20896                 pan.setActive(true);
20897                 
20898                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20899                 cur.setActive(false);
20900                 
20901                 _this.transition = false;
20902                 
20903             }, this, { single:  true } );
20904             
20905             return true;
20906         }
20907         
20908         cur.setActive(false);
20909         pan.setActive(true);
20910         
20911         return true;
20912         
20913     },
20914     showPanelNext : function()
20915     {
20916         var i = this.indexOfPanel(this.getActivePanel());
20917         
20918         if (i >= this.tabs.length - 1 && !this.autoslide) {
20919             return;
20920         }
20921         
20922         if (i >= this.tabs.length - 1 && this.autoslide) {
20923             i = -1;
20924         }
20925         
20926         this.showPanel(this.tabs[i+1]);
20927     },
20928     
20929     showPanelPrev : function()
20930     {
20931         var i = this.indexOfPanel(this.getActivePanel());
20932         
20933         if (i  < 1 && !this.autoslide) {
20934             return;
20935         }
20936         
20937         if (i < 1 && this.autoslide) {
20938             i = this.tabs.length;
20939         }
20940         
20941         this.showPanel(this.tabs[i-1]);
20942     },
20943     
20944     
20945     addBullet: function()
20946     {
20947         if(!this.bullets || Roo.isTouch){
20948             return;
20949         }
20950         var ctr = this.el.select('.carousel-bullets',true).first();
20951         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20952         var bullet = ctr.createChild({
20953             cls : 'bullet bullet-' + i
20954         },ctr.dom.lastChild);
20955         
20956         
20957         var _this = this;
20958         
20959         bullet.on('click', (function(e, el, o, ii, t){
20960
20961             e.preventDefault();
20962
20963             this.showPanel(ii);
20964
20965             if(this.autoslide && this.slideFn){
20966                 clearInterval(this.slideFn);
20967                 this.slideFn = window.setInterval(function() {
20968                     _this.showPanelNext();
20969                 }, this.timer);
20970             }
20971
20972         }).createDelegate(this, [i, bullet], true));
20973                 
20974         
20975     },
20976      
20977     setActiveBullet : function(i)
20978     {
20979         if(Roo.isTouch){
20980             return;
20981         }
20982         
20983         Roo.each(this.el.select('.bullet', true).elements, function(el){
20984             el.removeClass('selected');
20985         });
20986
20987         var bullet = this.el.select('.bullet-' + i, true).first();
20988         
20989         if(!bullet){
20990             return;
20991         }
20992         
20993         bullet.addClass('selected');
20994     }
20995     
20996     
20997   
20998 });
20999
21000  
21001
21002  
21003  
21004 Roo.apply(Roo.bootstrap.TabGroup, {
21005     
21006     groups: {},
21007      /**
21008     * register a Navigation Group
21009     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21010     */
21011     register : function(navgrp)
21012     {
21013         this.groups[navgrp.navId] = navgrp;
21014         
21015     },
21016     /**
21017     * fetch a Navigation Group based on the navigation ID
21018     * if one does not exist , it will get created.
21019     * @param {string} the navgroup to add
21020     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21021     */
21022     get: function(navId) {
21023         if (typeof(this.groups[navId]) == 'undefined') {
21024             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21025         }
21026         return this.groups[navId] ;
21027     }
21028     
21029     
21030     
21031 });
21032
21033  /*
21034  * - LGPL
21035  *
21036  * TabPanel
21037  * 
21038  */
21039
21040 /**
21041  * @class Roo.bootstrap.TabPanel
21042  * @extends Roo.bootstrap.Component
21043  * Bootstrap TabPanel class
21044  * @cfg {Boolean} active panel active
21045  * @cfg {String} html panel content
21046  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21047  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21048  * @cfg {String} href click to link..
21049  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21050  * 
21051  * 
21052  * @constructor
21053  * Create a new TabPanel
21054  * @param {Object} config The config object
21055  */
21056
21057 Roo.bootstrap.TabPanel = function(config){
21058     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21059     this.addEvents({
21060         /**
21061              * @event changed
21062              * Fires when the active status changes
21063              * @param {Roo.bootstrap.TabPanel} this
21064              * @param {Boolean} state the new state
21065             
21066          */
21067         'changed': true,
21068         /**
21069              * @event beforedeactivate
21070              * Fires before a tab is de-activated - can be used to do validation on a form.
21071              * @param {Roo.bootstrap.TabPanel} this
21072              * @return {Boolean} false if there is an error
21073             
21074          */
21075         'beforedeactivate': true
21076      });
21077     
21078     this.tabId = this.tabId || Roo.id();
21079   
21080 };
21081
21082 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21083     
21084     active: false,
21085     html: false,
21086     tabId: false,
21087     navId : false,
21088     href : '',
21089     touchSlide : false,
21090     getAutoCreate : function(){
21091         
21092         
21093         var cfg = {
21094             tag: 'div',
21095             // item is needed for carousel - not sure if it has any effect otherwise
21096             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21097             html: this.html || ''
21098         };
21099         
21100         if(this.active){
21101             cfg.cls += ' active';
21102         }
21103         
21104         if(this.tabId){
21105             cfg.tabId = this.tabId;
21106         }
21107         
21108         
21109         
21110         return cfg;
21111     },
21112     
21113     initEvents:  function()
21114     {
21115         var p = this.parent();
21116         
21117         this.navId = this.navId || p.navId;
21118         
21119         if (typeof(this.navId) != 'undefined') {
21120             // not really needed.. but just in case.. parent should be a NavGroup.
21121             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21122             
21123             tg.register(this);
21124             
21125             var i = tg.tabs.length - 1;
21126             
21127             if(this.active && tg.bullets > 0 && i < tg.bullets){
21128                 tg.setActiveBullet(i);
21129             }
21130         }
21131         
21132         this.el.on('click', this.onClick, this);
21133         
21134         if(Roo.isTouch && this.touchSlide){
21135             this.el.on("touchstart", this.onTouchStart, this);
21136             this.el.on("touchmove", this.onTouchMove, this);
21137             this.el.on("touchend", this.onTouchEnd, this);
21138         }
21139         
21140     },
21141     
21142     onRender : function(ct, position)
21143     {
21144         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21145     },
21146     
21147     setActive : function(state)
21148     {
21149         Roo.log("panel - set active " + this.tabId + "=" + state);
21150         
21151         this.active = state;
21152         if (!state) {
21153             this.el.removeClass('active');
21154             
21155         } else  if (!this.el.hasClass('active')) {
21156             this.el.addClass('active');
21157         }
21158         
21159         this.fireEvent('changed', this, state);
21160     },
21161     
21162     onClick : function(e)
21163     {
21164         e.preventDefault();
21165         
21166         if(!this.href.length){
21167             return;
21168         }
21169         
21170         window.location.href = this.href;
21171     },
21172     
21173     startX : 0,
21174     startY : 0,
21175     endX : 0,
21176     endY : 0,
21177     swiping : false,
21178     
21179     onTouchStart : function(e)
21180     {
21181         this.swiping = false;
21182         
21183         this.startX = e.browserEvent.touches[0].clientX;
21184         this.startY = e.browserEvent.touches[0].clientY;
21185     },
21186     
21187     onTouchMove : function(e)
21188     {
21189         this.swiping = true;
21190         
21191         this.endX = e.browserEvent.touches[0].clientX;
21192         this.endY = e.browserEvent.touches[0].clientY;
21193     },
21194     
21195     onTouchEnd : function(e)
21196     {
21197         if(!this.swiping){
21198             this.onClick(e);
21199             return;
21200         }
21201         
21202         var tabGroup = this.parent();
21203         
21204         if(this.endX > this.startX){ // swiping right
21205             tabGroup.showPanelPrev();
21206             return;
21207         }
21208         
21209         if(this.startX > this.endX){ // swiping left
21210             tabGroup.showPanelNext();
21211             return;
21212         }
21213     }
21214     
21215     
21216 });
21217  
21218
21219  
21220
21221  /*
21222  * - LGPL
21223  *
21224  * DateField
21225  * 
21226  */
21227
21228 /**
21229  * @class Roo.bootstrap.DateField
21230  * @extends Roo.bootstrap.Input
21231  * Bootstrap DateField class
21232  * @cfg {Number} weekStart default 0
21233  * @cfg {String} viewMode default empty, (months|years)
21234  * @cfg {String} minViewMode default empty, (months|years)
21235  * @cfg {Number} startDate default -Infinity
21236  * @cfg {Number} endDate default Infinity
21237  * @cfg {Boolean} todayHighlight default false
21238  * @cfg {Boolean} todayBtn default false
21239  * @cfg {Boolean} calendarWeeks default false
21240  * @cfg {Object} daysOfWeekDisabled default empty
21241  * @cfg {Boolean} singleMode default false (true | false)
21242  * 
21243  * @cfg {Boolean} keyboardNavigation default true
21244  * @cfg {String} language default en
21245  * 
21246  * @constructor
21247  * Create a new DateField
21248  * @param {Object} config The config object
21249  */
21250
21251 Roo.bootstrap.DateField = function(config){
21252     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21253      this.addEvents({
21254             /**
21255              * @event show
21256              * Fires when this field show.
21257              * @param {Roo.bootstrap.DateField} this
21258              * @param {Mixed} date The date value
21259              */
21260             show : true,
21261             /**
21262              * @event show
21263              * Fires when this field hide.
21264              * @param {Roo.bootstrap.DateField} this
21265              * @param {Mixed} date The date value
21266              */
21267             hide : true,
21268             /**
21269              * @event select
21270              * Fires when select a date.
21271              * @param {Roo.bootstrap.DateField} this
21272              * @param {Mixed} date The date value
21273              */
21274             select : true,
21275             /**
21276              * @event beforeselect
21277              * Fires when before select a date.
21278              * @param {Roo.bootstrap.DateField} this
21279              * @param {Mixed} date The date value
21280              */
21281             beforeselect : true
21282         });
21283 };
21284
21285 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21286     
21287     /**
21288      * @cfg {String} format
21289      * The default date format string which can be overriden for localization support.  The format must be
21290      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21291      */
21292     format : "m/d/y",
21293     /**
21294      * @cfg {String} altFormats
21295      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21296      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21297      */
21298     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21299     
21300     weekStart : 0,
21301     
21302     viewMode : '',
21303     
21304     minViewMode : '',
21305     
21306     todayHighlight : false,
21307     
21308     todayBtn: false,
21309     
21310     language: 'en',
21311     
21312     keyboardNavigation: true,
21313     
21314     calendarWeeks: false,
21315     
21316     startDate: -Infinity,
21317     
21318     endDate: Infinity,
21319     
21320     daysOfWeekDisabled: [],
21321     
21322     _events: [],
21323     
21324     singleMode : false,
21325     
21326     UTCDate: function()
21327     {
21328         return new Date(Date.UTC.apply(Date, arguments));
21329     },
21330     
21331     UTCToday: function()
21332     {
21333         var today = new Date();
21334         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21335     },
21336     
21337     getDate: function() {
21338             var d = this.getUTCDate();
21339             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21340     },
21341     
21342     getUTCDate: function() {
21343             return this.date;
21344     },
21345     
21346     setDate: function(d) {
21347             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21348     },
21349     
21350     setUTCDate: function(d) {
21351             this.date = d;
21352             this.setValue(this.formatDate(this.date));
21353     },
21354         
21355     onRender: function(ct, position)
21356     {
21357         
21358         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21359         
21360         this.language = this.language || 'en';
21361         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21362         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21363         
21364         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21365         this.format = this.format || 'm/d/y';
21366         this.isInline = false;
21367         this.isInput = true;
21368         this.component = this.el.select('.add-on', true).first() || false;
21369         this.component = (this.component && this.component.length === 0) ? false : this.component;
21370         this.hasInput = this.component && this.inputEl().length;
21371         
21372         if (typeof(this.minViewMode === 'string')) {
21373             switch (this.minViewMode) {
21374                 case 'months':
21375                     this.minViewMode = 1;
21376                     break;
21377                 case 'years':
21378                     this.minViewMode = 2;
21379                     break;
21380                 default:
21381                     this.minViewMode = 0;
21382                     break;
21383             }
21384         }
21385         
21386         if (typeof(this.viewMode === 'string')) {
21387             switch (this.viewMode) {
21388                 case 'months':
21389                     this.viewMode = 1;
21390                     break;
21391                 case 'years':
21392                     this.viewMode = 2;
21393                     break;
21394                 default:
21395                     this.viewMode = 0;
21396                     break;
21397             }
21398         }
21399                 
21400         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21401         
21402 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21403         
21404         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21405         
21406         this.picker().on('mousedown', this.onMousedown, this);
21407         this.picker().on('click', this.onClick, this);
21408         
21409         this.picker().addClass('datepicker-dropdown');
21410         
21411         this.startViewMode = this.viewMode;
21412         
21413         if(this.singleMode){
21414             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21415                 v.setVisibilityMode(Roo.Element.DISPLAY);
21416                 v.hide();
21417             });
21418             
21419             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21420                 v.setStyle('width', '189px');
21421             });
21422         }
21423         
21424         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21425             if(!this.calendarWeeks){
21426                 v.remove();
21427                 return;
21428             }
21429             
21430             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21431             v.attr('colspan', function(i, val){
21432                 return parseInt(val) + 1;
21433             });
21434         });
21435                         
21436         
21437         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21438         
21439         this.setStartDate(this.startDate);
21440         this.setEndDate(this.endDate);
21441         
21442         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21443         
21444         this.fillDow();
21445         this.fillMonths();
21446         this.update();
21447         this.showMode();
21448         
21449         if(this.isInline) {
21450             this.showPopup();
21451         }
21452     },
21453     
21454     picker : function()
21455     {
21456         return this.pickerEl;
21457 //        return this.el.select('.datepicker', true).first();
21458     },
21459     
21460     fillDow: function()
21461     {
21462         var dowCnt = this.weekStart;
21463         
21464         var dow = {
21465             tag: 'tr',
21466             cn: [
21467                 
21468             ]
21469         };
21470         
21471         if(this.calendarWeeks){
21472             dow.cn.push({
21473                 tag: 'th',
21474                 cls: 'cw',
21475                 html: '&nbsp;'
21476             })
21477         }
21478         
21479         while (dowCnt < this.weekStart + 7) {
21480             dow.cn.push({
21481                 tag: 'th',
21482                 cls: 'dow',
21483                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21484             });
21485         }
21486         
21487         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21488     },
21489     
21490     fillMonths: function()
21491     {    
21492         var i = 0;
21493         var months = this.picker().select('>.datepicker-months td', true).first();
21494         
21495         months.dom.innerHTML = '';
21496         
21497         while (i < 12) {
21498             var month = {
21499                 tag: 'span',
21500                 cls: 'month',
21501                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21502             };
21503             
21504             months.createChild(month);
21505         }
21506         
21507     },
21508     
21509     update: function()
21510     {
21511         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;
21512         
21513         if (this.date < this.startDate) {
21514             this.viewDate = new Date(this.startDate);
21515         } else if (this.date > this.endDate) {
21516             this.viewDate = new Date(this.endDate);
21517         } else {
21518             this.viewDate = new Date(this.date);
21519         }
21520         
21521         this.fill();
21522     },
21523     
21524     fill: function() 
21525     {
21526         var d = new Date(this.viewDate),
21527                 year = d.getUTCFullYear(),
21528                 month = d.getUTCMonth(),
21529                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21530                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21531                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21532                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21533                 currentDate = this.date && this.date.valueOf(),
21534                 today = this.UTCToday();
21535         
21536         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21537         
21538 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21539         
21540 //        this.picker.select('>tfoot th.today').
21541 //                                              .text(dates[this.language].today)
21542 //                                              .toggle(this.todayBtn !== false);
21543     
21544         this.updateNavArrows();
21545         this.fillMonths();
21546                                                 
21547         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21548         
21549         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21550          
21551         prevMonth.setUTCDate(day);
21552         
21553         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21554         
21555         var nextMonth = new Date(prevMonth);
21556         
21557         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21558         
21559         nextMonth = nextMonth.valueOf();
21560         
21561         var fillMonths = false;
21562         
21563         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21564         
21565         while(prevMonth.valueOf() <= nextMonth) {
21566             var clsName = '';
21567             
21568             if (prevMonth.getUTCDay() === this.weekStart) {
21569                 if(fillMonths){
21570                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21571                 }
21572                     
21573                 fillMonths = {
21574                     tag: 'tr',
21575                     cn: []
21576                 };
21577                 
21578                 if(this.calendarWeeks){
21579                     // ISO 8601: First week contains first thursday.
21580                     // ISO also states week starts on Monday, but we can be more abstract here.
21581                     var
21582                     // Start of current week: based on weekstart/current date
21583                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21584                     // Thursday of this week
21585                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21586                     // First Thursday of year, year from thursday
21587                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21588                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21589                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21590                     
21591                     fillMonths.cn.push({
21592                         tag: 'td',
21593                         cls: 'cw',
21594                         html: calWeek
21595                     });
21596                 }
21597             }
21598             
21599             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21600                 clsName += ' old';
21601             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21602                 clsName += ' new';
21603             }
21604             if (this.todayHighlight &&
21605                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21606                 prevMonth.getUTCMonth() == today.getMonth() &&
21607                 prevMonth.getUTCDate() == today.getDate()) {
21608                 clsName += ' today';
21609             }
21610             
21611             if (currentDate && prevMonth.valueOf() === currentDate) {
21612                 clsName += ' active';
21613             }
21614             
21615             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21616                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21617                     clsName += ' disabled';
21618             }
21619             
21620             fillMonths.cn.push({
21621                 tag: 'td',
21622                 cls: 'day ' + clsName,
21623                 html: prevMonth.getDate()
21624             });
21625             
21626             prevMonth.setDate(prevMonth.getDate()+1);
21627         }
21628           
21629         var currentYear = this.date && this.date.getUTCFullYear();
21630         var currentMonth = this.date && this.date.getUTCMonth();
21631         
21632         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21633         
21634         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21635             v.removeClass('active');
21636             
21637             if(currentYear === year && k === currentMonth){
21638                 v.addClass('active');
21639             }
21640             
21641             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21642                 v.addClass('disabled');
21643             }
21644             
21645         });
21646         
21647         
21648         year = parseInt(year/10, 10) * 10;
21649         
21650         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21651         
21652         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21653         
21654         year -= 1;
21655         for (var i = -1; i < 11; i++) {
21656             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21657                 tag: 'span',
21658                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21659                 html: year
21660             });
21661             
21662             year += 1;
21663         }
21664     },
21665     
21666     showMode: function(dir) 
21667     {
21668         if (dir) {
21669             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21670         }
21671         
21672         Roo.each(this.picker().select('>div',true).elements, function(v){
21673             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21674             v.hide();
21675         });
21676         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21677     },
21678     
21679     place: function()
21680     {
21681         if(this.isInline) {
21682             return;
21683         }
21684         
21685         this.picker().removeClass(['bottom', 'top']);
21686         
21687         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21688             /*
21689              * place to the top of element!
21690              *
21691              */
21692             
21693             this.picker().addClass('top');
21694             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21695             
21696             return;
21697         }
21698         
21699         this.picker().addClass('bottom');
21700         
21701         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21702     },
21703     
21704     parseDate : function(value)
21705     {
21706         if(!value || value instanceof Date){
21707             return value;
21708         }
21709         var v = Date.parseDate(value, this.format);
21710         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21711             v = Date.parseDate(value, 'Y-m-d');
21712         }
21713         if(!v && this.altFormats){
21714             if(!this.altFormatsArray){
21715                 this.altFormatsArray = this.altFormats.split("|");
21716             }
21717             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21718                 v = Date.parseDate(value, this.altFormatsArray[i]);
21719             }
21720         }
21721         return v;
21722     },
21723     
21724     formatDate : function(date, fmt)
21725     {   
21726         return (!date || !(date instanceof Date)) ?
21727         date : date.dateFormat(fmt || this.format);
21728     },
21729     
21730     onFocus : function()
21731     {
21732         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21733         this.showPopup();
21734     },
21735     
21736     onBlur : function()
21737     {
21738         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21739         
21740         var d = this.inputEl().getValue();
21741         
21742         this.setValue(d);
21743                 
21744         this.hidePopup();
21745     },
21746     
21747     showPopup : function()
21748     {
21749         this.picker().show();
21750         this.update();
21751         this.place();
21752         
21753         this.fireEvent('showpopup', this, this.date);
21754     },
21755     
21756     hidePopup : function()
21757     {
21758         if(this.isInline) {
21759             return;
21760         }
21761         this.picker().hide();
21762         this.viewMode = this.startViewMode;
21763         this.showMode();
21764         
21765         this.fireEvent('hidepopup', this, this.date);
21766         
21767     },
21768     
21769     onMousedown: function(e)
21770     {
21771         e.stopPropagation();
21772         e.preventDefault();
21773     },
21774     
21775     keyup: function(e)
21776     {
21777         Roo.bootstrap.DateField.superclass.keyup.call(this);
21778         this.update();
21779     },
21780
21781     setValue: function(v)
21782     {
21783         if(this.fireEvent('beforeselect', this, v) !== false){
21784             var d = new Date(this.parseDate(v) ).clearTime();
21785         
21786             if(isNaN(d.getTime())){
21787                 this.date = this.viewDate = '';
21788                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21789                 return;
21790             }
21791
21792             v = this.formatDate(d);
21793
21794             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21795
21796             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21797
21798             this.update();
21799
21800             this.fireEvent('select', this, this.date);
21801         }
21802     },
21803     
21804     getValue: function()
21805     {
21806         return this.formatDate(this.date);
21807     },
21808     
21809     fireKey: function(e)
21810     {
21811         if (!this.picker().isVisible()){
21812             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21813                 this.showPopup();
21814             }
21815             return;
21816         }
21817         
21818         var dateChanged = false,
21819         dir, day, month,
21820         newDate, newViewDate;
21821         
21822         switch(e.keyCode){
21823             case 27: // escape
21824                 this.hidePopup();
21825                 e.preventDefault();
21826                 break;
21827             case 37: // left
21828             case 39: // right
21829                 if (!this.keyboardNavigation) {
21830                     break;
21831                 }
21832                 dir = e.keyCode == 37 ? -1 : 1;
21833                 
21834                 if (e.ctrlKey){
21835                     newDate = this.moveYear(this.date, dir);
21836                     newViewDate = this.moveYear(this.viewDate, dir);
21837                 } else if (e.shiftKey){
21838                     newDate = this.moveMonth(this.date, dir);
21839                     newViewDate = this.moveMonth(this.viewDate, dir);
21840                 } else {
21841                     newDate = new Date(this.date);
21842                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21843                     newViewDate = new Date(this.viewDate);
21844                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21845                 }
21846                 if (this.dateWithinRange(newDate)){
21847                     this.date = newDate;
21848                     this.viewDate = newViewDate;
21849                     this.setValue(this.formatDate(this.date));
21850 //                    this.update();
21851                     e.preventDefault();
21852                     dateChanged = true;
21853                 }
21854                 break;
21855             case 38: // up
21856             case 40: // down
21857                 if (!this.keyboardNavigation) {
21858                     break;
21859                 }
21860                 dir = e.keyCode == 38 ? -1 : 1;
21861                 if (e.ctrlKey){
21862                     newDate = this.moveYear(this.date, dir);
21863                     newViewDate = this.moveYear(this.viewDate, dir);
21864                 } else if (e.shiftKey){
21865                     newDate = this.moveMonth(this.date, dir);
21866                     newViewDate = this.moveMonth(this.viewDate, dir);
21867                 } else {
21868                     newDate = new Date(this.date);
21869                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21870                     newViewDate = new Date(this.viewDate);
21871                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21872                 }
21873                 if (this.dateWithinRange(newDate)){
21874                     this.date = newDate;
21875                     this.viewDate = newViewDate;
21876                     this.setValue(this.formatDate(this.date));
21877 //                    this.update();
21878                     e.preventDefault();
21879                     dateChanged = true;
21880                 }
21881                 break;
21882             case 13: // enter
21883                 this.setValue(this.formatDate(this.date));
21884                 this.hidePopup();
21885                 e.preventDefault();
21886                 break;
21887             case 9: // tab
21888                 this.setValue(this.formatDate(this.date));
21889                 this.hidePopup();
21890                 break;
21891             case 16: // shift
21892             case 17: // ctrl
21893             case 18: // alt
21894                 break;
21895             default :
21896                 this.hidePopup();
21897                 
21898         }
21899     },
21900     
21901     
21902     onClick: function(e) 
21903     {
21904         e.stopPropagation();
21905         e.preventDefault();
21906         
21907         var target = e.getTarget();
21908         
21909         if(target.nodeName.toLowerCase() === 'i'){
21910             target = Roo.get(target).dom.parentNode;
21911         }
21912         
21913         var nodeName = target.nodeName;
21914         var className = target.className;
21915         var html = target.innerHTML;
21916         //Roo.log(nodeName);
21917         
21918         switch(nodeName.toLowerCase()) {
21919             case 'th':
21920                 switch(className) {
21921                     case 'switch':
21922                         this.showMode(1);
21923                         break;
21924                     case 'prev':
21925                     case 'next':
21926                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21927                         switch(this.viewMode){
21928                                 case 0:
21929                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21930                                         break;
21931                                 case 1:
21932                                 case 2:
21933                                         this.viewDate = this.moveYear(this.viewDate, dir);
21934                                         break;
21935                         }
21936                         this.fill();
21937                         break;
21938                     case 'today':
21939                         var date = new Date();
21940                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21941 //                        this.fill()
21942                         this.setValue(this.formatDate(this.date));
21943                         
21944                         this.hidePopup();
21945                         break;
21946                 }
21947                 break;
21948             case 'span':
21949                 if (className.indexOf('disabled') < 0) {
21950                     this.viewDate.setUTCDate(1);
21951                     if (className.indexOf('month') > -1) {
21952                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21953                     } else {
21954                         var year = parseInt(html, 10) || 0;
21955                         this.viewDate.setUTCFullYear(year);
21956                         
21957                     }
21958                     
21959                     if(this.singleMode){
21960                         this.setValue(this.formatDate(this.viewDate));
21961                         this.hidePopup();
21962                         return;
21963                     }
21964                     
21965                     this.showMode(-1);
21966                     this.fill();
21967                 }
21968                 break;
21969                 
21970             case 'td':
21971                 //Roo.log(className);
21972                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21973                     var day = parseInt(html, 10) || 1;
21974                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21975                         month = (this.viewDate || new Date()).getUTCMonth();
21976
21977                     if (className.indexOf('old') > -1) {
21978                         if(month === 0 ){
21979                             month = 11;
21980                             year -= 1;
21981                         }else{
21982                             month -= 1;
21983                         }
21984                     } else if (className.indexOf('new') > -1) {
21985                         if (month == 11) {
21986                             month = 0;
21987                             year += 1;
21988                         } else {
21989                             month += 1;
21990                         }
21991                     }
21992                     //Roo.log([year,month,day]);
21993                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21994                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21995 //                    this.fill();
21996                     //Roo.log(this.formatDate(this.date));
21997                     this.setValue(this.formatDate(this.date));
21998                     this.hidePopup();
21999                 }
22000                 break;
22001         }
22002     },
22003     
22004     setStartDate: function(startDate)
22005     {
22006         this.startDate = startDate || -Infinity;
22007         if (this.startDate !== -Infinity) {
22008             this.startDate = this.parseDate(this.startDate);
22009         }
22010         this.update();
22011         this.updateNavArrows();
22012     },
22013
22014     setEndDate: function(endDate)
22015     {
22016         this.endDate = endDate || Infinity;
22017         if (this.endDate !== Infinity) {
22018             this.endDate = this.parseDate(this.endDate);
22019         }
22020         this.update();
22021         this.updateNavArrows();
22022     },
22023     
22024     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22025     {
22026         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22027         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22028             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22029         }
22030         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22031             return parseInt(d, 10);
22032         });
22033         this.update();
22034         this.updateNavArrows();
22035     },
22036     
22037     updateNavArrows: function() 
22038     {
22039         if(this.singleMode){
22040             return;
22041         }
22042         
22043         var d = new Date(this.viewDate),
22044         year = d.getUTCFullYear(),
22045         month = d.getUTCMonth();
22046         
22047         Roo.each(this.picker().select('.prev', true).elements, function(v){
22048             v.show();
22049             switch (this.viewMode) {
22050                 case 0:
22051
22052                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22053                         v.hide();
22054                     }
22055                     break;
22056                 case 1:
22057                 case 2:
22058                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22059                         v.hide();
22060                     }
22061                     break;
22062             }
22063         });
22064         
22065         Roo.each(this.picker().select('.next', true).elements, function(v){
22066             v.show();
22067             switch (this.viewMode) {
22068                 case 0:
22069
22070                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22071                         v.hide();
22072                     }
22073                     break;
22074                 case 1:
22075                 case 2:
22076                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22077                         v.hide();
22078                     }
22079                     break;
22080             }
22081         })
22082     },
22083     
22084     moveMonth: function(date, dir)
22085     {
22086         if (!dir) {
22087             return date;
22088         }
22089         var new_date = new Date(date.valueOf()),
22090         day = new_date.getUTCDate(),
22091         month = new_date.getUTCMonth(),
22092         mag = Math.abs(dir),
22093         new_month, test;
22094         dir = dir > 0 ? 1 : -1;
22095         if (mag == 1){
22096             test = dir == -1
22097             // If going back one month, make sure month is not current month
22098             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22099             ? function(){
22100                 return new_date.getUTCMonth() == month;
22101             }
22102             // If going forward one month, make sure month is as expected
22103             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22104             : function(){
22105                 return new_date.getUTCMonth() != new_month;
22106             };
22107             new_month = month + dir;
22108             new_date.setUTCMonth(new_month);
22109             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22110             if (new_month < 0 || new_month > 11) {
22111                 new_month = (new_month + 12) % 12;
22112             }
22113         } else {
22114             // For magnitudes >1, move one month at a time...
22115             for (var i=0; i<mag; i++) {
22116                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22117                 new_date = this.moveMonth(new_date, dir);
22118             }
22119             // ...then reset the day, keeping it in the new month
22120             new_month = new_date.getUTCMonth();
22121             new_date.setUTCDate(day);
22122             test = function(){
22123                 return new_month != new_date.getUTCMonth();
22124             };
22125         }
22126         // Common date-resetting loop -- if date is beyond end of month, make it
22127         // end of month
22128         while (test()){
22129             new_date.setUTCDate(--day);
22130             new_date.setUTCMonth(new_month);
22131         }
22132         return new_date;
22133     },
22134
22135     moveYear: function(date, dir)
22136     {
22137         return this.moveMonth(date, dir*12);
22138     },
22139
22140     dateWithinRange: function(date)
22141     {
22142         return date >= this.startDate && date <= this.endDate;
22143     },
22144
22145     
22146     remove: function() 
22147     {
22148         this.picker().remove();
22149     },
22150     
22151     validateValue : function(value)
22152     {
22153         if(this.getVisibilityEl().hasClass('hidden')){
22154             return true;
22155         }
22156         
22157         if(value.length < 1)  {
22158             if(this.allowBlank){
22159                 return true;
22160             }
22161             return false;
22162         }
22163         
22164         if(value.length < this.minLength){
22165             return false;
22166         }
22167         if(value.length > this.maxLength){
22168             return false;
22169         }
22170         if(this.vtype){
22171             var vt = Roo.form.VTypes;
22172             if(!vt[this.vtype](value, this)){
22173                 return false;
22174             }
22175         }
22176         if(typeof this.validator == "function"){
22177             var msg = this.validator(value);
22178             if(msg !== true){
22179                 return false;
22180             }
22181         }
22182         
22183         if(this.regex && !this.regex.test(value)){
22184             return false;
22185         }
22186         
22187         if(typeof(this.parseDate(value)) == 'undefined'){
22188             return false;
22189         }
22190         
22191         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22192             return false;
22193         }      
22194         
22195         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22196             return false;
22197         } 
22198         
22199         
22200         return true;
22201     },
22202     
22203     reset : function()
22204     {
22205         this.date = this.viewDate = '';
22206         
22207         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22208     }
22209    
22210 });
22211
22212 Roo.apply(Roo.bootstrap.DateField,  {
22213     
22214     head : {
22215         tag: 'thead',
22216         cn: [
22217         {
22218             tag: 'tr',
22219             cn: [
22220             {
22221                 tag: 'th',
22222                 cls: 'prev',
22223                 html: '<i class="fa fa-arrow-left"/>'
22224             },
22225             {
22226                 tag: 'th',
22227                 cls: 'switch',
22228                 colspan: '5'
22229             },
22230             {
22231                 tag: 'th',
22232                 cls: 'next',
22233                 html: '<i class="fa fa-arrow-right"/>'
22234             }
22235
22236             ]
22237         }
22238         ]
22239     },
22240     
22241     content : {
22242         tag: 'tbody',
22243         cn: [
22244         {
22245             tag: 'tr',
22246             cn: [
22247             {
22248                 tag: 'td',
22249                 colspan: '7'
22250             }
22251             ]
22252         }
22253         ]
22254     },
22255     
22256     footer : {
22257         tag: 'tfoot',
22258         cn: [
22259         {
22260             tag: 'tr',
22261             cn: [
22262             {
22263                 tag: 'th',
22264                 colspan: '7',
22265                 cls: 'today'
22266             }
22267                     
22268             ]
22269         }
22270         ]
22271     },
22272     
22273     dates:{
22274         en: {
22275             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22276             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22277             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22278             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22279             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22280             today: "Today"
22281         }
22282     },
22283     
22284     modes: [
22285     {
22286         clsName: 'days',
22287         navFnc: 'Month',
22288         navStep: 1
22289     },
22290     {
22291         clsName: 'months',
22292         navFnc: 'FullYear',
22293         navStep: 1
22294     },
22295     {
22296         clsName: 'years',
22297         navFnc: 'FullYear',
22298         navStep: 10
22299     }]
22300 });
22301
22302 Roo.apply(Roo.bootstrap.DateField,  {
22303   
22304     template : {
22305         tag: 'div',
22306         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22307         cn: [
22308         {
22309             tag: 'div',
22310             cls: 'datepicker-days',
22311             cn: [
22312             {
22313                 tag: 'table',
22314                 cls: 'table-condensed',
22315                 cn:[
22316                 Roo.bootstrap.DateField.head,
22317                 {
22318                     tag: 'tbody'
22319                 },
22320                 Roo.bootstrap.DateField.footer
22321                 ]
22322             }
22323             ]
22324         },
22325         {
22326             tag: 'div',
22327             cls: 'datepicker-months',
22328             cn: [
22329             {
22330                 tag: 'table',
22331                 cls: 'table-condensed',
22332                 cn:[
22333                 Roo.bootstrap.DateField.head,
22334                 Roo.bootstrap.DateField.content,
22335                 Roo.bootstrap.DateField.footer
22336                 ]
22337             }
22338             ]
22339         },
22340         {
22341             tag: 'div',
22342             cls: 'datepicker-years',
22343             cn: [
22344             {
22345                 tag: 'table',
22346                 cls: 'table-condensed',
22347                 cn:[
22348                 Roo.bootstrap.DateField.head,
22349                 Roo.bootstrap.DateField.content,
22350                 Roo.bootstrap.DateField.footer
22351                 ]
22352             }
22353             ]
22354         }
22355         ]
22356     }
22357 });
22358
22359  
22360
22361  /*
22362  * - LGPL
22363  *
22364  * TimeField
22365  * 
22366  */
22367
22368 /**
22369  * @class Roo.bootstrap.TimeField
22370  * @extends Roo.bootstrap.Input
22371  * Bootstrap DateField class
22372  * 
22373  * 
22374  * @constructor
22375  * Create a new TimeField
22376  * @param {Object} config The config object
22377  */
22378
22379 Roo.bootstrap.TimeField = function(config){
22380     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22381     this.addEvents({
22382             /**
22383              * @event show
22384              * Fires when this field show.
22385              * @param {Roo.bootstrap.DateField} thisthis
22386              * @param {Mixed} date The date value
22387              */
22388             show : true,
22389             /**
22390              * @event show
22391              * Fires when this field hide.
22392              * @param {Roo.bootstrap.DateField} this
22393              * @param {Mixed} date The date value
22394              */
22395             hide : true,
22396             /**
22397              * @event select
22398              * Fires when select a date.
22399              * @param {Roo.bootstrap.DateField} this
22400              * @param {Mixed} date The date value
22401              */
22402             select : true
22403         });
22404 };
22405
22406 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22407     
22408     /**
22409      * @cfg {String} format
22410      * The default time format string which can be overriden for localization support.  The format must be
22411      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22412      */
22413     format : "H:i",
22414
22415     getAutoCreate : function()
22416     {
22417         this.after = '<i class="fa far fa-clock"></i>';
22418         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22419         
22420          
22421     },
22422     onRender: function(ct, position)
22423     {
22424         
22425         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22426                 
22427         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22428         
22429         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22430         
22431         this.pop = this.picker().select('>.datepicker-time',true).first();
22432         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22433         
22434         this.picker().on('mousedown', this.onMousedown, this);
22435         this.picker().on('click', this.onClick, this);
22436         
22437         this.picker().addClass('datepicker-dropdown');
22438     
22439         this.fillTime();
22440         this.update();
22441             
22442         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22443         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22444         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22445         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22446         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22447         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22448
22449     },
22450     
22451     fireKey: function(e){
22452         if (!this.picker().isVisible()){
22453             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22454                 this.show();
22455             }
22456             return;
22457         }
22458
22459         e.preventDefault();
22460         
22461         switch(e.keyCode){
22462             case 27: // escape
22463                 this.hide();
22464                 break;
22465             case 37: // left
22466             case 39: // right
22467                 this.onTogglePeriod();
22468                 break;
22469             case 38: // up
22470                 this.onIncrementMinutes();
22471                 break;
22472             case 40: // down
22473                 this.onDecrementMinutes();
22474                 break;
22475             case 13: // enter
22476             case 9: // tab
22477                 this.setTime();
22478                 break;
22479         }
22480     },
22481     
22482     onClick: function(e) {
22483         e.stopPropagation();
22484         e.preventDefault();
22485     },
22486     
22487     picker : function()
22488     {
22489         return this.pickerEl;
22490     },
22491     
22492     fillTime: function()
22493     {    
22494         var time = this.pop.select('tbody', true).first();
22495         
22496         time.dom.innerHTML = '';
22497         
22498         time.createChild({
22499             tag: 'tr',
22500             cn: [
22501                 {
22502                     tag: 'td',
22503                     cn: [
22504                         {
22505                             tag: 'a',
22506                             href: '#',
22507                             cls: 'btn',
22508                             cn: [
22509                                 {
22510                                     tag: 'i',
22511                                     cls: 'hours-up fa fas fa-chevron-up'
22512                                 }
22513                             ]
22514                         } 
22515                     ]
22516                 },
22517                 {
22518                     tag: 'td',
22519                     cls: 'separator'
22520                 },
22521                 {
22522                     tag: 'td',
22523                     cn: [
22524                         {
22525                             tag: 'a',
22526                             href: '#',
22527                             cls: 'btn',
22528                             cn: [
22529                                 {
22530                                     tag: 'i',
22531                                     cls: 'minutes-up fa fas fa-chevron-up'
22532                                 }
22533                             ]
22534                         }
22535                     ]
22536                 },
22537                 {
22538                     tag: 'td',
22539                     cls: 'separator'
22540                 }
22541             ]
22542         });
22543         
22544         time.createChild({
22545             tag: 'tr',
22546             cn: [
22547                 {
22548                     tag: 'td',
22549                     cn: [
22550                         {
22551                             tag: 'span',
22552                             cls: 'timepicker-hour',
22553                             html: '00'
22554                         }  
22555                     ]
22556                 },
22557                 {
22558                     tag: 'td',
22559                     cls: 'separator',
22560                     html: ':'
22561                 },
22562                 {
22563                     tag: 'td',
22564                     cn: [
22565                         {
22566                             tag: 'span',
22567                             cls: 'timepicker-minute',
22568                             html: '00'
22569                         }  
22570                     ]
22571                 },
22572                 {
22573                     tag: 'td',
22574                     cls: 'separator'
22575                 },
22576                 {
22577                     tag: 'td',
22578                     cn: [
22579                         {
22580                             tag: 'button',
22581                             type: 'button',
22582                             cls: 'btn btn-primary period',
22583                             html: 'AM'
22584                             
22585                         }
22586                     ]
22587                 }
22588             ]
22589         });
22590         
22591         time.createChild({
22592             tag: 'tr',
22593             cn: [
22594                 {
22595                     tag: 'td',
22596                     cn: [
22597                         {
22598                             tag: 'a',
22599                             href: '#',
22600                             cls: 'btn',
22601                             cn: [
22602                                 {
22603                                     tag: 'span',
22604                                     cls: 'hours-down fa fas fa-chevron-down'
22605                                 }
22606                             ]
22607                         }
22608                     ]
22609                 },
22610                 {
22611                     tag: 'td',
22612                     cls: 'separator'
22613                 },
22614                 {
22615                     tag: 'td',
22616                     cn: [
22617                         {
22618                             tag: 'a',
22619                             href: '#',
22620                             cls: 'btn',
22621                             cn: [
22622                                 {
22623                                     tag: 'span',
22624                                     cls: 'minutes-down fa fas fa-chevron-down'
22625                                 }
22626                             ]
22627                         }
22628                     ]
22629                 },
22630                 {
22631                     tag: 'td',
22632                     cls: 'separator'
22633                 }
22634             ]
22635         });
22636         
22637     },
22638     
22639     update: function()
22640     {
22641         
22642         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22643         
22644         this.fill();
22645     },
22646     
22647     fill: function() 
22648     {
22649         var hours = this.time.getHours();
22650         var minutes = this.time.getMinutes();
22651         var period = 'AM';
22652         
22653         if(hours > 11){
22654             period = 'PM';
22655         }
22656         
22657         if(hours == 0){
22658             hours = 12;
22659         }
22660         
22661         
22662         if(hours > 12){
22663             hours = hours - 12;
22664         }
22665         
22666         if(hours < 10){
22667             hours = '0' + hours;
22668         }
22669         
22670         if(minutes < 10){
22671             minutes = '0' + minutes;
22672         }
22673         
22674         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22675         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22676         this.pop.select('button', true).first().dom.innerHTML = period;
22677         
22678     },
22679     
22680     place: function()
22681     {   
22682         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22683         
22684         var cls = ['bottom'];
22685         
22686         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22687             cls.pop();
22688             cls.push('top');
22689         }
22690         
22691         cls.push('right');
22692         
22693         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22694             cls.pop();
22695             cls.push('left');
22696         }
22697         //this.picker().setXY(20000,20000);
22698         this.picker().addClass(cls.join('-'));
22699         
22700         var _this = this;
22701         
22702         Roo.each(cls, function(c){
22703             if(c == 'bottom'){
22704                 (function() {
22705                  //  
22706                 }).defer(200);
22707                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22708                 //_this.picker().setTop(_this.inputEl().getHeight());
22709                 return;
22710             }
22711             if(c == 'top'){
22712                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22713                 
22714                 //_this.picker().setTop(0 - _this.picker().getHeight());
22715                 return;
22716             }
22717             /*
22718             if(c == 'left'){
22719                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22720                 return;
22721             }
22722             if(c == 'right'){
22723                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22724                 return;
22725             }
22726             */
22727         });
22728         
22729     },
22730   
22731     onFocus : function()
22732     {
22733         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22734         this.show();
22735     },
22736     
22737     onBlur : function()
22738     {
22739         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22740         this.hide();
22741     },
22742     
22743     show : function()
22744     {
22745         this.picker().show();
22746         this.pop.show();
22747         this.update();
22748         this.place();
22749         
22750         this.fireEvent('show', this, this.date);
22751     },
22752     
22753     hide : function()
22754     {
22755         this.picker().hide();
22756         this.pop.hide();
22757         
22758         this.fireEvent('hide', this, this.date);
22759     },
22760     
22761     setTime : function()
22762     {
22763         this.hide();
22764         this.setValue(this.time.format(this.format));
22765         
22766         this.fireEvent('select', this, this.date);
22767         
22768         
22769     },
22770     
22771     onMousedown: function(e){
22772         e.stopPropagation();
22773         e.preventDefault();
22774     },
22775     
22776     onIncrementHours: function()
22777     {
22778         Roo.log('onIncrementHours');
22779         this.time = this.time.add(Date.HOUR, 1);
22780         this.update();
22781         
22782     },
22783     
22784     onDecrementHours: function()
22785     {
22786         Roo.log('onDecrementHours');
22787         this.time = this.time.add(Date.HOUR, -1);
22788         this.update();
22789     },
22790     
22791     onIncrementMinutes: function()
22792     {
22793         Roo.log('onIncrementMinutes');
22794         this.time = this.time.add(Date.MINUTE, 1);
22795         this.update();
22796     },
22797     
22798     onDecrementMinutes: function()
22799     {
22800         Roo.log('onDecrementMinutes');
22801         this.time = this.time.add(Date.MINUTE, -1);
22802         this.update();
22803     },
22804     
22805     onTogglePeriod: function()
22806     {
22807         Roo.log('onTogglePeriod');
22808         this.time = this.time.add(Date.HOUR, 12);
22809         this.update();
22810     }
22811     
22812    
22813 });
22814  
22815
22816 Roo.apply(Roo.bootstrap.TimeField,  {
22817   
22818     template : {
22819         tag: 'div',
22820         cls: 'datepicker dropdown-menu',
22821         cn: [
22822             {
22823                 tag: 'div',
22824                 cls: 'datepicker-time',
22825                 cn: [
22826                 {
22827                     tag: 'table',
22828                     cls: 'table-condensed',
22829                     cn:[
22830                         {
22831                             tag: 'tbody',
22832                             cn: [
22833                                 {
22834                                     tag: 'tr',
22835                                     cn: [
22836                                     {
22837                                         tag: 'td',
22838                                         colspan: '7'
22839                                     }
22840                                     ]
22841                                 }
22842                             ]
22843                         },
22844                         {
22845                             tag: 'tfoot',
22846                             cn: [
22847                                 {
22848                                     tag: 'tr',
22849                                     cn: [
22850                                     {
22851                                         tag: 'th',
22852                                         colspan: '7',
22853                                         cls: '',
22854                                         cn: [
22855                                             {
22856                                                 tag: 'button',
22857                                                 cls: 'btn btn-info ok',
22858                                                 html: 'OK'
22859                                             }
22860                                         ]
22861                                     }
22862                     
22863                                     ]
22864                                 }
22865                             ]
22866                         }
22867                     ]
22868                 }
22869                 ]
22870             }
22871         ]
22872     }
22873 });
22874
22875  
22876
22877  /*
22878  * - LGPL
22879  *
22880  * MonthField
22881  * 
22882  */
22883
22884 /**
22885  * @class Roo.bootstrap.MonthField
22886  * @extends Roo.bootstrap.Input
22887  * Bootstrap MonthField class
22888  * 
22889  * @cfg {String} language default en
22890  * 
22891  * @constructor
22892  * Create a new MonthField
22893  * @param {Object} config The config object
22894  */
22895
22896 Roo.bootstrap.MonthField = function(config){
22897     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22898     
22899     this.addEvents({
22900         /**
22901          * @event show
22902          * Fires when this field show.
22903          * @param {Roo.bootstrap.MonthField} this
22904          * @param {Mixed} date The date value
22905          */
22906         show : true,
22907         /**
22908          * @event show
22909          * Fires when this field hide.
22910          * @param {Roo.bootstrap.MonthField} this
22911          * @param {Mixed} date The date value
22912          */
22913         hide : true,
22914         /**
22915          * @event select
22916          * Fires when select a date.
22917          * @param {Roo.bootstrap.MonthField} this
22918          * @param {String} oldvalue The old value
22919          * @param {String} newvalue The new value
22920          */
22921         select : true
22922     });
22923 };
22924
22925 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22926     
22927     onRender: function(ct, position)
22928     {
22929         
22930         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22931         
22932         this.language = this.language || 'en';
22933         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22934         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22935         
22936         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22937         this.isInline = false;
22938         this.isInput = true;
22939         this.component = this.el.select('.add-on', true).first() || false;
22940         this.component = (this.component && this.component.length === 0) ? false : this.component;
22941         this.hasInput = this.component && this.inputEL().length;
22942         
22943         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22944         
22945         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22946         
22947         this.picker().on('mousedown', this.onMousedown, this);
22948         this.picker().on('click', this.onClick, this);
22949         
22950         this.picker().addClass('datepicker-dropdown');
22951         
22952         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22953             v.setStyle('width', '189px');
22954         });
22955         
22956         this.fillMonths();
22957         
22958         this.update();
22959         
22960         if(this.isInline) {
22961             this.show();
22962         }
22963         
22964     },
22965     
22966     setValue: function(v, suppressEvent)
22967     {   
22968         var o = this.getValue();
22969         
22970         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22971         
22972         this.update();
22973
22974         if(suppressEvent !== true){
22975             this.fireEvent('select', this, o, v);
22976         }
22977         
22978     },
22979     
22980     getValue: function()
22981     {
22982         return this.value;
22983     },
22984     
22985     onClick: function(e) 
22986     {
22987         e.stopPropagation();
22988         e.preventDefault();
22989         
22990         var target = e.getTarget();
22991         
22992         if(target.nodeName.toLowerCase() === 'i'){
22993             target = Roo.get(target).dom.parentNode;
22994         }
22995         
22996         var nodeName = target.nodeName;
22997         var className = target.className;
22998         var html = target.innerHTML;
22999         
23000         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23001             return;
23002         }
23003         
23004         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23005         
23006         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23007         
23008         this.hide();
23009                         
23010     },
23011     
23012     picker : function()
23013     {
23014         return this.pickerEl;
23015     },
23016     
23017     fillMonths: function()
23018     {    
23019         var i = 0;
23020         var months = this.picker().select('>.datepicker-months td', true).first();
23021         
23022         months.dom.innerHTML = '';
23023         
23024         while (i < 12) {
23025             var month = {
23026                 tag: 'span',
23027                 cls: 'month',
23028                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23029             };
23030             
23031             months.createChild(month);
23032         }
23033         
23034     },
23035     
23036     update: function()
23037     {
23038         var _this = this;
23039         
23040         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23041             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23042         }
23043         
23044         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23045             e.removeClass('active');
23046             
23047             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23048                 e.addClass('active');
23049             }
23050         })
23051     },
23052     
23053     place: function()
23054     {
23055         if(this.isInline) {
23056             return;
23057         }
23058         
23059         this.picker().removeClass(['bottom', 'top']);
23060         
23061         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23062             /*
23063              * place to the top of element!
23064              *
23065              */
23066             
23067             this.picker().addClass('top');
23068             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23069             
23070             return;
23071         }
23072         
23073         this.picker().addClass('bottom');
23074         
23075         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23076     },
23077     
23078     onFocus : function()
23079     {
23080         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23081         this.show();
23082     },
23083     
23084     onBlur : function()
23085     {
23086         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23087         
23088         var d = this.inputEl().getValue();
23089         
23090         this.setValue(d);
23091                 
23092         this.hide();
23093     },
23094     
23095     show : function()
23096     {
23097         this.picker().show();
23098         this.picker().select('>.datepicker-months', true).first().show();
23099         this.update();
23100         this.place();
23101         
23102         this.fireEvent('show', this, this.date);
23103     },
23104     
23105     hide : function()
23106     {
23107         if(this.isInline) {
23108             return;
23109         }
23110         this.picker().hide();
23111         this.fireEvent('hide', this, this.date);
23112         
23113     },
23114     
23115     onMousedown: function(e)
23116     {
23117         e.stopPropagation();
23118         e.preventDefault();
23119     },
23120     
23121     keyup: function(e)
23122     {
23123         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23124         this.update();
23125     },
23126
23127     fireKey: function(e)
23128     {
23129         if (!this.picker().isVisible()){
23130             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23131                 this.show();
23132             }
23133             return;
23134         }
23135         
23136         var dir;
23137         
23138         switch(e.keyCode){
23139             case 27: // escape
23140                 this.hide();
23141                 e.preventDefault();
23142                 break;
23143             case 37: // left
23144             case 39: // right
23145                 dir = e.keyCode == 37 ? -1 : 1;
23146                 
23147                 this.vIndex = this.vIndex + dir;
23148                 
23149                 if(this.vIndex < 0){
23150                     this.vIndex = 0;
23151                 }
23152                 
23153                 if(this.vIndex > 11){
23154                     this.vIndex = 11;
23155                 }
23156                 
23157                 if(isNaN(this.vIndex)){
23158                     this.vIndex = 0;
23159                 }
23160                 
23161                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23162                 
23163                 break;
23164             case 38: // up
23165             case 40: // down
23166                 
23167                 dir = e.keyCode == 38 ? -1 : 1;
23168                 
23169                 this.vIndex = this.vIndex + dir * 4;
23170                 
23171                 if(this.vIndex < 0){
23172                     this.vIndex = 0;
23173                 }
23174                 
23175                 if(this.vIndex > 11){
23176                     this.vIndex = 11;
23177                 }
23178                 
23179                 if(isNaN(this.vIndex)){
23180                     this.vIndex = 0;
23181                 }
23182                 
23183                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23184                 break;
23185                 
23186             case 13: // enter
23187                 
23188                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23189                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23190                 }
23191                 
23192                 this.hide();
23193                 e.preventDefault();
23194                 break;
23195             case 9: // tab
23196                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23197                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23198                 }
23199                 this.hide();
23200                 break;
23201             case 16: // shift
23202             case 17: // ctrl
23203             case 18: // alt
23204                 break;
23205             default :
23206                 this.hide();
23207                 
23208         }
23209     },
23210     
23211     remove: function() 
23212     {
23213         this.picker().remove();
23214     }
23215    
23216 });
23217
23218 Roo.apply(Roo.bootstrap.MonthField,  {
23219     
23220     content : {
23221         tag: 'tbody',
23222         cn: [
23223         {
23224             tag: 'tr',
23225             cn: [
23226             {
23227                 tag: 'td',
23228                 colspan: '7'
23229             }
23230             ]
23231         }
23232         ]
23233     },
23234     
23235     dates:{
23236         en: {
23237             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23238             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23239         }
23240     }
23241 });
23242
23243 Roo.apply(Roo.bootstrap.MonthField,  {
23244   
23245     template : {
23246         tag: 'div',
23247         cls: 'datepicker dropdown-menu roo-dynamic',
23248         cn: [
23249             {
23250                 tag: 'div',
23251                 cls: 'datepicker-months',
23252                 cn: [
23253                 {
23254                     tag: 'table',
23255                     cls: 'table-condensed',
23256                     cn:[
23257                         Roo.bootstrap.DateField.content
23258                     ]
23259                 }
23260                 ]
23261             }
23262         ]
23263     }
23264 });
23265
23266  
23267
23268  
23269  /*
23270  * - LGPL
23271  *
23272  * CheckBox
23273  * 
23274  */
23275
23276 /**
23277  * @class Roo.bootstrap.CheckBox
23278  * @extends Roo.bootstrap.Input
23279  * Bootstrap CheckBox class
23280  * 
23281  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23282  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23283  * @cfg {String} boxLabel The text that appears beside the checkbox
23284  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23285  * @cfg {Boolean} checked initnal the element
23286  * @cfg {Boolean} inline inline the element (default false)
23287  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23288  * @cfg {String} tooltip label tooltip
23289  * 
23290  * @constructor
23291  * Create a new CheckBox
23292  * @param {Object} config The config object
23293  */
23294
23295 Roo.bootstrap.CheckBox = function(config){
23296     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23297    
23298     this.addEvents({
23299         /**
23300         * @event check
23301         * Fires when the element is checked or unchecked.
23302         * @param {Roo.bootstrap.CheckBox} this This input
23303         * @param {Boolean} checked The new checked value
23304         */
23305        check : true,
23306        /**
23307         * @event click
23308         * Fires when the element is click.
23309         * @param {Roo.bootstrap.CheckBox} this This input
23310         */
23311        click : true
23312     });
23313     
23314 };
23315
23316 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23317   
23318     inputType: 'checkbox',
23319     inputValue: 1,
23320     valueOff: 0,
23321     boxLabel: false,
23322     checked: false,
23323     weight : false,
23324     inline: false,
23325     tooltip : '',
23326     
23327     // checkbox success does not make any sense really.. 
23328     invalidClass : "",
23329     validClass : "",
23330     
23331     
23332     getAutoCreate : function()
23333     {
23334         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23335         
23336         var id = Roo.id();
23337         
23338         var cfg = {};
23339         
23340         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23341         
23342         if(this.inline){
23343             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23344         }
23345         
23346         var input =  {
23347             tag: 'input',
23348             id : id,
23349             type : this.inputType,
23350             value : this.inputValue,
23351             cls : 'roo-' + this.inputType, //'form-box',
23352             placeholder : this.placeholder || ''
23353             
23354         };
23355         
23356         if(this.inputType != 'radio'){
23357             var hidden =  {
23358                 tag: 'input',
23359                 type : 'hidden',
23360                 cls : 'roo-hidden-value',
23361                 value : this.checked ? this.inputValue : this.valueOff
23362             };
23363         }
23364         
23365             
23366         if (this.weight) { // Validity check?
23367             cfg.cls += " " + this.inputType + "-" + this.weight;
23368         }
23369         
23370         if (this.disabled) {
23371             input.disabled=true;
23372         }
23373         
23374         if(this.checked){
23375             input.checked = this.checked;
23376         }
23377         
23378         if (this.name) {
23379             
23380             input.name = this.name;
23381             
23382             if(this.inputType != 'radio'){
23383                 hidden.name = this.name;
23384                 input.name = '_hidden_' + this.name;
23385             }
23386         }
23387         
23388         if (this.size) {
23389             input.cls += ' input-' + this.size;
23390         }
23391         
23392         var settings=this;
23393         
23394         ['xs','sm','md','lg'].map(function(size){
23395             if (settings[size]) {
23396                 cfg.cls += ' col-' + size + '-' + settings[size];
23397             }
23398         });
23399         
23400         var inputblock = input;
23401          
23402         if (this.before || this.after) {
23403             
23404             inputblock = {
23405                 cls : 'input-group',
23406                 cn :  [] 
23407             };
23408             
23409             if (this.before) {
23410                 inputblock.cn.push({
23411                     tag :'span',
23412                     cls : 'input-group-addon',
23413                     html : this.before
23414                 });
23415             }
23416             
23417             inputblock.cn.push(input);
23418             
23419             if(this.inputType != 'radio'){
23420                 inputblock.cn.push(hidden);
23421             }
23422             
23423             if (this.after) {
23424                 inputblock.cn.push({
23425                     tag :'span',
23426                     cls : 'input-group-addon',
23427                     html : this.after
23428                 });
23429             }
23430             
23431         }
23432         var boxLabelCfg = false;
23433         
23434         if(this.boxLabel){
23435            
23436             boxLabelCfg = {
23437                 tag: 'label',
23438                 //'for': id, // box label is handled by onclick - so no for...
23439                 cls: 'box-label',
23440                 html: this.boxLabel
23441             };
23442             if(this.tooltip){
23443                 boxLabelCfg.tooltip = this.tooltip;
23444             }
23445              
23446         }
23447         
23448         
23449         if (align ==='left' && this.fieldLabel.length) {
23450 //                Roo.log("left and has label");
23451             cfg.cn = [
23452                 {
23453                     tag: 'label',
23454                     'for' :  id,
23455                     cls : 'control-label',
23456                     html : this.fieldLabel
23457                 },
23458                 {
23459                     cls : "", 
23460                     cn: [
23461                         inputblock
23462                     ]
23463                 }
23464             ];
23465             
23466             if (boxLabelCfg) {
23467                 cfg.cn[1].cn.push(boxLabelCfg);
23468             }
23469             
23470             if(this.labelWidth > 12){
23471                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23472             }
23473             
23474             if(this.labelWidth < 13 && this.labelmd == 0){
23475                 this.labelmd = this.labelWidth;
23476             }
23477             
23478             if(this.labellg > 0){
23479                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23480                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23481             }
23482             
23483             if(this.labelmd > 0){
23484                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23485                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23486             }
23487             
23488             if(this.labelsm > 0){
23489                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23490                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23491             }
23492             
23493             if(this.labelxs > 0){
23494                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23495                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23496             }
23497             
23498         } else if ( this.fieldLabel.length) {
23499 //                Roo.log(" label");
23500                 cfg.cn = [
23501                    
23502                     {
23503                         tag: this.boxLabel ? 'span' : 'label',
23504                         'for': id,
23505                         cls: 'control-label box-input-label',
23506                         //cls : 'input-group-addon',
23507                         html : this.fieldLabel
23508                     },
23509                     
23510                     inputblock
23511                     
23512                 ];
23513                 if (boxLabelCfg) {
23514                     cfg.cn.push(boxLabelCfg);
23515                 }
23516
23517         } else {
23518             
23519 //                Roo.log(" no label && no align");
23520                 cfg.cn = [  inputblock ] ;
23521                 if (boxLabelCfg) {
23522                     cfg.cn.push(boxLabelCfg);
23523                 }
23524
23525                 
23526         }
23527         
23528        
23529         
23530         if(this.inputType != 'radio'){
23531             cfg.cn.push(hidden);
23532         }
23533         
23534         return cfg;
23535         
23536     },
23537     
23538     /**
23539      * return the real input element.
23540      */
23541     inputEl: function ()
23542     {
23543         return this.el.select('input.roo-' + this.inputType,true).first();
23544     },
23545     hiddenEl: function ()
23546     {
23547         return this.el.select('input.roo-hidden-value',true).first();
23548     },
23549     
23550     labelEl: function()
23551     {
23552         return this.el.select('label.control-label',true).first();
23553     },
23554     /* depricated... */
23555     
23556     label: function()
23557     {
23558         return this.labelEl();
23559     },
23560     
23561     boxLabelEl: function()
23562     {
23563         return this.el.select('label.box-label',true).first();
23564     },
23565     
23566     initEvents : function()
23567     {
23568 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23569         
23570         this.inputEl().on('click', this.onClick,  this);
23571         
23572         if (this.boxLabel) { 
23573             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23574         }
23575         
23576         this.startValue = this.getValue();
23577         
23578         if(this.groupId){
23579             Roo.bootstrap.CheckBox.register(this);
23580         }
23581     },
23582     
23583     onClick : function(e)
23584     {   
23585         if(this.fireEvent('click', this, e) !== false){
23586             this.setChecked(!this.checked);
23587         }
23588         
23589     },
23590     
23591     setChecked : function(state,suppressEvent)
23592     {
23593         this.startValue = this.getValue();
23594
23595         if(this.inputType == 'radio'){
23596             
23597             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23598                 e.dom.checked = false;
23599             });
23600             
23601             this.inputEl().dom.checked = true;
23602             
23603             this.inputEl().dom.value = this.inputValue;
23604             
23605             if(suppressEvent !== true){
23606                 this.fireEvent('check', this, true);
23607             }
23608             
23609             this.validate();
23610             
23611             return;
23612         }
23613         
23614         this.checked = state;
23615         
23616         this.inputEl().dom.checked = state;
23617         
23618         
23619         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23620         
23621         if(suppressEvent !== true){
23622             this.fireEvent('check', this, state);
23623         }
23624         
23625         this.validate();
23626     },
23627     
23628     getValue : function()
23629     {
23630         if(this.inputType == 'radio'){
23631             return this.getGroupValue();
23632         }
23633         
23634         return this.hiddenEl().dom.value;
23635         
23636     },
23637     
23638     getGroupValue : function()
23639     {
23640         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23641             return '';
23642         }
23643         
23644         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23645     },
23646     
23647     setValue : function(v,suppressEvent)
23648     {
23649         if(this.inputType == 'radio'){
23650             this.setGroupValue(v, suppressEvent);
23651             return;
23652         }
23653         
23654         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23655         
23656         this.validate();
23657     },
23658     
23659     setGroupValue : function(v, suppressEvent)
23660     {
23661         this.startValue = this.getValue();
23662         
23663         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23664             e.dom.checked = false;
23665             
23666             if(e.dom.value == v){
23667                 e.dom.checked = true;
23668             }
23669         });
23670         
23671         if(suppressEvent !== true){
23672             this.fireEvent('check', this, true);
23673         }
23674
23675         this.validate();
23676         
23677         return;
23678     },
23679     
23680     validate : function()
23681     {
23682         if(this.getVisibilityEl().hasClass('hidden')){
23683             return true;
23684         }
23685         
23686         if(
23687                 this.disabled || 
23688                 (this.inputType == 'radio' && this.validateRadio()) ||
23689                 (this.inputType == 'checkbox' && this.validateCheckbox())
23690         ){
23691             this.markValid();
23692             return true;
23693         }
23694         
23695         this.markInvalid();
23696         return false;
23697     },
23698     
23699     validateRadio : function()
23700     {
23701         if(this.getVisibilityEl().hasClass('hidden')){
23702             return true;
23703         }
23704         
23705         if(this.allowBlank){
23706             return true;
23707         }
23708         
23709         var valid = false;
23710         
23711         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23712             if(!e.dom.checked){
23713                 return;
23714             }
23715             
23716             valid = true;
23717             
23718             return false;
23719         });
23720         
23721         return valid;
23722     },
23723     
23724     validateCheckbox : function()
23725     {
23726         if(!this.groupId){
23727             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23728             //return (this.getValue() == this.inputValue) ? true : false;
23729         }
23730         
23731         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23732         
23733         if(!group){
23734             return false;
23735         }
23736         
23737         var r = false;
23738         
23739         for(var i in group){
23740             if(group[i].el.isVisible(true)){
23741                 r = false;
23742                 break;
23743             }
23744             
23745             r = true;
23746         }
23747         
23748         for(var i in group){
23749             if(r){
23750                 break;
23751             }
23752             
23753             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23754         }
23755         
23756         return r;
23757     },
23758     
23759     /**
23760      * Mark this field as valid
23761      */
23762     markValid : function()
23763     {
23764         var _this = this;
23765         
23766         this.fireEvent('valid', this);
23767         
23768         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23769         
23770         if(this.groupId){
23771             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23772         }
23773         
23774         if(label){
23775             label.markValid();
23776         }
23777
23778         if(this.inputType == 'radio'){
23779             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23780                 var fg = e.findParent('.form-group', false, true);
23781                 if (Roo.bootstrap.version == 3) {
23782                     fg.removeClass([_this.invalidClass, _this.validClass]);
23783                     fg.addClass(_this.validClass);
23784                 } else {
23785                     fg.removeClass(['is-valid', 'is-invalid']);
23786                     fg.addClass('is-valid');
23787                 }
23788             });
23789             
23790             return;
23791         }
23792
23793         if(!this.groupId){
23794             var fg = this.el.findParent('.form-group', false, true);
23795             if (Roo.bootstrap.version == 3) {
23796                 fg.removeClass([this.invalidClass, this.validClass]);
23797                 fg.addClass(this.validClass);
23798             } else {
23799                 fg.removeClass(['is-valid', 'is-invalid']);
23800                 fg.addClass('is-valid');
23801             }
23802             return;
23803         }
23804         
23805         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23806         
23807         if(!group){
23808             return;
23809         }
23810         
23811         for(var i in group){
23812             var fg = group[i].el.findParent('.form-group', false, true);
23813             if (Roo.bootstrap.version == 3) {
23814                 fg.removeClass([this.invalidClass, this.validClass]);
23815                 fg.addClass(this.validClass);
23816             } else {
23817                 fg.removeClass(['is-valid', 'is-invalid']);
23818                 fg.addClass('is-valid');
23819             }
23820         }
23821     },
23822     
23823      /**
23824      * Mark this field as invalid
23825      * @param {String} msg The validation message
23826      */
23827     markInvalid : function(msg)
23828     {
23829         if(this.allowBlank){
23830             return;
23831         }
23832         
23833         var _this = this;
23834         
23835         this.fireEvent('invalid', this, msg);
23836         
23837         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23838         
23839         if(this.groupId){
23840             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23841         }
23842         
23843         if(label){
23844             label.markInvalid();
23845         }
23846             
23847         if(this.inputType == 'radio'){
23848             
23849             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23850                 var fg = e.findParent('.form-group', false, true);
23851                 if (Roo.bootstrap.version == 3) {
23852                     fg.removeClass([_this.invalidClass, _this.validClass]);
23853                     fg.addClass(_this.invalidClass);
23854                 } else {
23855                     fg.removeClass(['is-invalid', 'is-valid']);
23856                     fg.addClass('is-invalid');
23857                 }
23858             });
23859             
23860             return;
23861         }
23862         
23863         if(!this.groupId){
23864             var fg = this.el.findParent('.form-group', false, true);
23865             if (Roo.bootstrap.version == 3) {
23866                 fg.removeClass([_this.invalidClass, _this.validClass]);
23867                 fg.addClass(_this.invalidClass);
23868             } else {
23869                 fg.removeClass(['is-invalid', 'is-valid']);
23870                 fg.addClass('is-invalid');
23871             }
23872             return;
23873         }
23874         
23875         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23876         
23877         if(!group){
23878             return;
23879         }
23880         
23881         for(var i in group){
23882             var fg = group[i].el.findParent('.form-group', false, true);
23883             if (Roo.bootstrap.version == 3) {
23884                 fg.removeClass([_this.invalidClass, _this.validClass]);
23885                 fg.addClass(_this.invalidClass);
23886             } else {
23887                 fg.removeClass(['is-invalid', 'is-valid']);
23888                 fg.addClass('is-invalid');
23889             }
23890         }
23891         
23892     },
23893     
23894     clearInvalid : function()
23895     {
23896         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23897         
23898         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23899         
23900         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23901         
23902         if (label && label.iconEl) {
23903             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23904             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23905         }
23906     },
23907     
23908     disable : function()
23909     {
23910         if(this.inputType != 'radio'){
23911             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23912             return;
23913         }
23914         
23915         var _this = this;
23916         
23917         if(this.rendered){
23918             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23919                 _this.getActionEl().addClass(this.disabledClass);
23920                 e.dom.disabled = true;
23921             });
23922         }
23923         
23924         this.disabled = true;
23925         this.fireEvent("disable", this);
23926         return this;
23927     },
23928
23929     enable : function()
23930     {
23931         if(this.inputType != 'radio'){
23932             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23933             return;
23934         }
23935         
23936         var _this = this;
23937         
23938         if(this.rendered){
23939             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23940                 _this.getActionEl().removeClass(this.disabledClass);
23941                 e.dom.disabled = false;
23942             });
23943         }
23944         
23945         this.disabled = false;
23946         this.fireEvent("enable", this);
23947         return this;
23948     },
23949     
23950     setBoxLabel : function(v)
23951     {
23952         this.boxLabel = v;
23953         
23954         if(this.rendered){
23955             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23956         }
23957     }
23958
23959 });
23960
23961 Roo.apply(Roo.bootstrap.CheckBox, {
23962     
23963     groups: {},
23964     
23965      /**
23966     * register a CheckBox Group
23967     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23968     */
23969     register : function(checkbox)
23970     {
23971         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23972             this.groups[checkbox.groupId] = {};
23973         }
23974         
23975         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23976             return;
23977         }
23978         
23979         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23980         
23981     },
23982     /**
23983     * fetch a CheckBox Group based on the group ID
23984     * @param {string} the group ID
23985     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23986     */
23987     get: function(groupId) {
23988         if (typeof(this.groups[groupId]) == 'undefined') {
23989             return false;
23990         }
23991         
23992         return this.groups[groupId] ;
23993     }
23994     
23995     
23996 });
23997 /*
23998  * - LGPL
23999  *
24000  * RadioItem
24001  * 
24002  */
24003
24004 /**
24005  * @class Roo.bootstrap.Radio
24006  * @extends Roo.bootstrap.Component
24007  * Bootstrap Radio class
24008  * @cfg {String} boxLabel - the label associated
24009  * @cfg {String} value - the value of radio
24010  * 
24011  * @constructor
24012  * Create a new Radio
24013  * @param {Object} config The config object
24014  */
24015 Roo.bootstrap.Radio = function(config){
24016     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24017     
24018 };
24019
24020 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24021     
24022     boxLabel : '',
24023     
24024     value : '',
24025     
24026     getAutoCreate : function()
24027     {
24028         var cfg = {
24029             tag : 'div',
24030             cls : 'form-group radio',
24031             cn : [
24032                 {
24033                     tag : 'label',
24034                     cls : 'box-label',
24035                     html : this.boxLabel
24036                 }
24037             ]
24038         };
24039         
24040         return cfg;
24041     },
24042     
24043     initEvents : function() 
24044     {
24045         this.parent().register(this);
24046         
24047         this.el.on('click', this.onClick, this);
24048         
24049     },
24050     
24051     onClick : function(e)
24052     {
24053         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24054             this.setChecked(true);
24055         }
24056     },
24057     
24058     setChecked : function(state, suppressEvent)
24059     {
24060         this.parent().setValue(this.value, suppressEvent);
24061         
24062     },
24063     
24064     setBoxLabel : function(v)
24065     {
24066         this.boxLabel = v;
24067         
24068         if(this.rendered){
24069             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24070         }
24071     }
24072     
24073 });
24074  
24075
24076  /*
24077  * - LGPL
24078  *
24079  * Input
24080  * 
24081  */
24082
24083 /**
24084  * @class Roo.bootstrap.SecurePass
24085  * @extends Roo.bootstrap.Input
24086  * Bootstrap SecurePass class
24087  *
24088  * 
24089  * @constructor
24090  * Create a new SecurePass
24091  * @param {Object} config The config object
24092  */
24093  
24094 Roo.bootstrap.SecurePass = function (config) {
24095     // these go here, so the translation tool can replace them..
24096     this.errors = {
24097         PwdEmpty: "Please type a password, and then retype it to confirm.",
24098         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24099         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24100         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24101         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24102         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24103         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24104         TooWeak: "Your password is Too Weak."
24105     },
24106     this.meterLabel = "Password strength:";
24107     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24108     this.meterClass = [
24109         "roo-password-meter-tooweak", 
24110         "roo-password-meter-weak", 
24111         "roo-password-meter-medium", 
24112         "roo-password-meter-strong", 
24113         "roo-password-meter-grey"
24114     ];
24115     
24116     this.errors = {};
24117     
24118     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24119 }
24120
24121 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24122     /**
24123      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24124      * {
24125      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24126      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24127      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24128      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24129      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24130      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24131      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24132      * })
24133      */
24134     // private
24135     
24136     meterWidth: 300,
24137     errorMsg :'',    
24138     errors: false,
24139     imageRoot: '/',
24140     /**
24141      * @cfg {String/Object} Label for the strength meter (defaults to
24142      * 'Password strength:')
24143      */
24144     // private
24145     meterLabel: '',
24146     /**
24147      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24148      * ['Weak', 'Medium', 'Strong'])
24149      */
24150     // private    
24151     pwdStrengths: false,    
24152     // private
24153     strength: 0,
24154     // private
24155     _lastPwd: null,
24156     // private
24157     kCapitalLetter: 0,
24158     kSmallLetter: 1,
24159     kDigit: 2,
24160     kPunctuation: 3,
24161     
24162     insecure: false,
24163     // private
24164     initEvents: function ()
24165     {
24166         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24167
24168         if (this.el.is('input[type=password]') && Roo.isSafari) {
24169             this.el.on('keydown', this.SafariOnKeyDown, this);
24170         }
24171
24172         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24173     },
24174     // private
24175     onRender: function (ct, position)
24176     {
24177         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24178         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24179         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24180
24181         this.trigger.createChild({
24182                    cn: [
24183                     {
24184                     //id: 'PwdMeter',
24185                     tag: 'div',
24186                     cls: 'roo-password-meter-grey col-xs-12',
24187                     style: {
24188                         //width: 0,
24189                         //width: this.meterWidth + 'px'                                                
24190                         }
24191                     },
24192                     {                            
24193                          cls: 'roo-password-meter-text'                          
24194                     }
24195                 ]            
24196         });
24197
24198          
24199         if (this.hideTrigger) {
24200             this.trigger.setDisplayed(false);
24201         }
24202         this.setSize(this.width || '', this.height || '');
24203     },
24204     // private
24205     onDestroy: function ()
24206     {
24207         if (this.trigger) {
24208             this.trigger.removeAllListeners();
24209             this.trigger.remove();
24210         }
24211         if (this.wrap) {
24212             this.wrap.remove();
24213         }
24214         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24215     },
24216     // private
24217     checkStrength: function ()
24218     {
24219         var pwd = this.inputEl().getValue();
24220         if (pwd == this._lastPwd) {
24221             return;
24222         }
24223
24224         var strength;
24225         if (this.ClientSideStrongPassword(pwd)) {
24226             strength = 3;
24227         } else if (this.ClientSideMediumPassword(pwd)) {
24228             strength = 2;
24229         } else if (this.ClientSideWeakPassword(pwd)) {
24230             strength = 1;
24231         } else {
24232             strength = 0;
24233         }
24234         
24235         Roo.log('strength1: ' + strength);
24236         
24237         //var pm = this.trigger.child('div/div/div').dom;
24238         var pm = this.trigger.child('div/div');
24239         pm.removeClass(this.meterClass);
24240         pm.addClass(this.meterClass[strength]);
24241                 
24242         
24243         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24244                 
24245         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24246         
24247         this._lastPwd = pwd;
24248     },
24249     reset: function ()
24250     {
24251         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24252         
24253         this._lastPwd = '';
24254         
24255         var pm = this.trigger.child('div/div');
24256         pm.removeClass(this.meterClass);
24257         pm.addClass('roo-password-meter-grey');        
24258         
24259         
24260         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24261         
24262         pt.innerHTML = '';
24263         this.inputEl().dom.type='password';
24264     },
24265     // private
24266     validateValue: function (value)
24267     {
24268         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24269             return false;
24270         }
24271         if (value.length == 0) {
24272             if (this.allowBlank) {
24273                 this.clearInvalid();
24274                 return true;
24275             }
24276
24277             this.markInvalid(this.errors.PwdEmpty);
24278             this.errorMsg = this.errors.PwdEmpty;
24279             return false;
24280         }
24281         
24282         if(this.insecure){
24283             return true;
24284         }
24285         
24286         if (!value.match(/[\x21-\x7e]+/)) {
24287             this.markInvalid(this.errors.PwdBadChar);
24288             this.errorMsg = this.errors.PwdBadChar;
24289             return false;
24290         }
24291         if (value.length < 6) {
24292             this.markInvalid(this.errors.PwdShort);
24293             this.errorMsg = this.errors.PwdShort;
24294             return false;
24295         }
24296         if (value.length > 16) {
24297             this.markInvalid(this.errors.PwdLong);
24298             this.errorMsg = this.errors.PwdLong;
24299             return false;
24300         }
24301         var strength;
24302         if (this.ClientSideStrongPassword(value)) {
24303             strength = 3;
24304         } else if (this.ClientSideMediumPassword(value)) {
24305             strength = 2;
24306         } else if (this.ClientSideWeakPassword(value)) {
24307             strength = 1;
24308         } else {
24309             strength = 0;
24310         }
24311
24312         
24313         if (strength < 2) {
24314             //this.markInvalid(this.errors.TooWeak);
24315             this.errorMsg = this.errors.TooWeak;
24316             //return false;
24317         }
24318         
24319         
24320         console.log('strength2: ' + strength);
24321         
24322         //var pm = this.trigger.child('div/div/div').dom;
24323         
24324         var pm = this.trigger.child('div/div');
24325         pm.removeClass(this.meterClass);
24326         pm.addClass(this.meterClass[strength]);
24327                 
24328         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24329                 
24330         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24331         
24332         this.errorMsg = ''; 
24333         return true;
24334     },
24335     // private
24336     CharacterSetChecks: function (type)
24337     {
24338         this.type = type;
24339         this.fResult = false;
24340     },
24341     // private
24342     isctype: function (character, type)
24343     {
24344         switch (type) {  
24345             case this.kCapitalLetter:
24346                 if (character >= 'A' && character <= 'Z') {
24347                     return true;
24348                 }
24349                 break;
24350             
24351             case this.kSmallLetter:
24352                 if (character >= 'a' && character <= 'z') {
24353                     return true;
24354                 }
24355                 break;
24356             
24357             case this.kDigit:
24358                 if (character >= '0' && character <= '9') {
24359                     return true;
24360                 }
24361                 break;
24362             
24363             case this.kPunctuation:
24364                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24365                     return true;
24366                 }
24367                 break;
24368             
24369             default:
24370                 return false;
24371         }
24372
24373     },
24374     // private
24375     IsLongEnough: function (pwd, size)
24376     {
24377         return !(pwd == null || isNaN(size) || pwd.length < size);
24378     },
24379     // private
24380     SpansEnoughCharacterSets: function (word, nb)
24381     {
24382         if (!this.IsLongEnough(word, nb))
24383         {
24384             return false;
24385         }
24386
24387         var characterSetChecks = new Array(
24388             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24389             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24390         );
24391         
24392         for (var index = 0; index < word.length; ++index) {
24393             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24394                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24395                     characterSetChecks[nCharSet].fResult = true;
24396                     break;
24397                 }
24398             }
24399         }
24400
24401         var nCharSets = 0;
24402         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24403             if (characterSetChecks[nCharSet].fResult) {
24404                 ++nCharSets;
24405             }
24406         }
24407
24408         if (nCharSets < nb) {
24409             return false;
24410         }
24411         return true;
24412     },
24413     // private
24414     ClientSideStrongPassword: function (pwd)
24415     {
24416         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24417     },
24418     // private
24419     ClientSideMediumPassword: function (pwd)
24420     {
24421         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24422     },
24423     // private
24424     ClientSideWeakPassword: function (pwd)
24425     {
24426         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24427     }
24428           
24429 })//<script type="text/javascript">
24430
24431 /*
24432  * Based  Ext JS Library 1.1.1
24433  * Copyright(c) 2006-2007, Ext JS, LLC.
24434  * LGPL
24435  *
24436  */
24437  
24438 /**
24439  * @class Roo.HtmlEditorCore
24440  * @extends Roo.Component
24441  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24442  *
24443  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24444  */
24445
24446 Roo.HtmlEditorCore = function(config){
24447     
24448     
24449     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24450     
24451     
24452     this.addEvents({
24453         /**
24454          * @event initialize
24455          * Fires when the editor is fully initialized (including the iframe)
24456          * @param {Roo.HtmlEditorCore} this
24457          */
24458         initialize: true,
24459         /**
24460          * @event activate
24461          * Fires when the editor is first receives the focus. Any insertion must wait
24462          * until after this event.
24463          * @param {Roo.HtmlEditorCore} this
24464          */
24465         activate: true,
24466          /**
24467          * @event beforesync
24468          * Fires before the textarea is updated with content from the editor iframe. Return false
24469          * to cancel the sync.
24470          * @param {Roo.HtmlEditorCore} this
24471          * @param {String} html
24472          */
24473         beforesync: true,
24474          /**
24475          * @event beforepush
24476          * Fires before the iframe editor is updated with content from the textarea. Return false
24477          * to cancel the push.
24478          * @param {Roo.HtmlEditorCore} this
24479          * @param {String} html
24480          */
24481         beforepush: true,
24482          /**
24483          * @event sync
24484          * Fires when the textarea is updated with content from the editor iframe.
24485          * @param {Roo.HtmlEditorCore} this
24486          * @param {String} html
24487          */
24488         sync: true,
24489          /**
24490          * @event push
24491          * Fires when the iframe editor is updated with content from the textarea.
24492          * @param {Roo.HtmlEditorCore} this
24493          * @param {String} html
24494          */
24495         push: true,
24496         
24497         /**
24498          * @event editorevent
24499          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24500          * @param {Roo.HtmlEditorCore} this
24501          */
24502         editorevent: true
24503         
24504     });
24505     
24506     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24507     
24508     // defaults : white / black...
24509     this.applyBlacklists();
24510     
24511     
24512     
24513 };
24514
24515
24516 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24517
24518
24519      /**
24520      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24521      */
24522     
24523     owner : false,
24524     
24525      /**
24526      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24527      *                        Roo.resizable.
24528      */
24529     resizable : false,
24530      /**
24531      * @cfg {Number} height (in pixels)
24532      */   
24533     height: 300,
24534    /**
24535      * @cfg {Number} width (in pixels)
24536      */   
24537     width: 500,
24538     
24539     /**
24540      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24541      * 
24542      */
24543     stylesheets: false,
24544     
24545     // id of frame..
24546     frameId: false,
24547     
24548     // private properties
24549     validationEvent : false,
24550     deferHeight: true,
24551     initialized : false,
24552     activated : false,
24553     sourceEditMode : false,
24554     onFocus : Roo.emptyFn,
24555     iframePad:3,
24556     hideMode:'offsets',
24557     
24558     clearUp: true,
24559     
24560     // blacklist + whitelisted elements..
24561     black: false,
24562     white: false,
24563      
24564     bodyCls : '',
24565
24566     /**
24567      * Protected method that will not generally be called directly. It
24568      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24569      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24570      */
24571     getDocMarkup : function(){
24572         // body styles..
24573         var st = '';
24574         
24575         // inherit styels from page...?? 
24576         if (this.stylesheets === false) {
24577             
24578             Roo.get(document.head).select('style').each(function(node) {
24579                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24580             });
24581             
24582             Roo.get(document.head).select('link').each(function(node) { 
24583                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24584             });
24585             
24586         } else if (!this.stylesheets.length) {
24587                 // simple..
24588                 st = '<style type="text/css">' +
24589                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24590                    '</style>';
24591         } else {
24592             for (var i in this.stylesheets) { 
24593                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24594             }
24595             
24596         }
24597         
24598         st +=  '<style type="text/css">' +
24599             'IMG { cursor: pointer } ' +
24600         '</style>';
24601
24602         var cls = 'roo-htmleditor-body';
24603         
24604         if(this.bodyCls.length){
24605             cls += ' ' + this.bodyCls;
24606         }
24607         
24608         return '<html><head>' + st  +
24609             //<style type="text/css">' +
24610             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24611             //'</style>' +
24612             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24613     },
24614
24615     // private
24616     onRender : function(ct, position)
24617     {
24618         var _t = this;
24619         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24620         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24621         
24622         
24623         this.el.dom.style.border = '0 none';
24624         this.el.dom.setAttribute('tabIndex', -1);
24625         this.el.addClass('x-hidden hide');
24626         
24627         
24628         
24629         if(Roo.isIE){ // fix IE 1px bogus margin
24630             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24631         }
24632        
24633         
24634         this.frameId = Roo.id();
24635         
24636          
24637         
24638         var iframe = this.owner.wrap.createChild({
24639             tag: 'iframe',
24640             cls: 'form-control', // bootstrap..
24641             id: this.frameId,
24642             name: this.frameId,
24643             frameBorder : 'no',
24644             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24645         }, this.el
24646         );
24647         
24648         
24649         this.iframe = iframe.dom;
24650
24651          this.assignDocWin();
24652         
24653         this.doc.designMode = 'on';
24654        
24655         this.doc.open();
24656         this.doc.write(this.getDocMarkup());
24657         this.doc.close();
24658
24659         
24660         var task = { // must defer to wait for browser to be ready
24661             run : function(){
24662                 //console.log("run task?" + this.doc.readyState);
24663                 this.assignDocWin();
24664                 if(this.doc.body || this.doc.readyState == 'complete'){
24665                     try {
24666                         this.doc.designMode="on";
24667                     } catch (e) {
24668                         return;
24669                     }
24670                     Roo.TaskMgr.stop(task);
24671                     this.initEditor.defer(10, this);
24672                 }
24673             },
24674             interval : 10,
24675             duration: 10000,
24676             scope: this
24677         };
24678         Roo.TaskMgr.start(task);
24679
24680     },
24681
24682     // private
24683     onResize : function(w, h)
24684     {
24685          Roo.log('resize: ' +w + ',' + h );
24686         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24687         if(!this.iframe){
24688             return;
24689         }
24690         if(typeof w == 'number'){
24691             
24692             this.iframe.style.width = w + 'px';
24693         }
24694         if(typeof h == 'number'){
24695             
24696             this.iframe.style.height = h + 'px';
24697             if(this.doc){
24698                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24699             }
24700         }
24701         
24702     },
24703
24704     /**
24705      * Toggles the editor between standard and source edit mode.
24706      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24707      */
24708     toggleSourceEdit : function(sourceEditMode){
24709         
24710         this.sourceEditMode = sourceEditMode === true;
24711         
24712         if(this.sourceEditMode){
24713  
24714             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24715             
24716         }else{
24717             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24718             //this.iframe.className = '';
24719             this.deferFocus();
24720         }
24721         //this.setSize(this.owner.wrap.getSize());
24722         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24723     },
24724
24725     
24726   
24727
24728     /**
24729      * Protected method that will not generally be called directly. If you need/want
24730      * custom HTML cleanup, this is the method you should override.
24731      * @param {String} html The HTML to be cleaned
24732      * return {String} The cleaned HTML
24733      */
24734     cleanHtml : function(html){
24735         html = String(html);
24736         if(html.length > 5){
24737             if(Roo.isSafari){ // strip safari nonsense
24738                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24739             }
24740         }
24741         if(html == '&nbsp;'){
24742             html = '';
24743         }
24744         return html;
24745     },
24746
24747     /**
24748      * HTML Editor -> Textarea
24749      * Protected method that will not generally be called directly. Syncs the contents
24750      * of the editor iframe with the textarea.
24751      */
24752     syncValue : function(){
24753         if(this.initialized){
24754             var bd = (this.doc.body || this.doc.documentElement);
24755             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24756             var html = bd.innerHTML;
24757             if(Roo.isSafari){
24758                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24759                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24760                 if(m && m[1]){
24761                     html = '<div style="'+m[0]+'">' + html + '</div>';
24762                 }
24763             }
24764             html = this.cleanHtml(html);
24765             // fix up the special chars.. normaly like back quotes in word...
24766             // however we do not want to do this with chinese..
24767             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24768                 
24769                 var cc = match.charCodeAt();
24770
24771                 // Get the character value, handling surrogate pairs
24772                 if (match.length == 2) {
24773                     // It's a surrogate pair, calculate the Unicode code point
24774                     var high = match.charCodeAt(0) - 0xD800;
24775                     var low  = match.charCodeAt(1) - 0xDC00;
24776                     cc = (high * 0x400) + low + 0x10000;
24777                 }  else if (
24778                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24779                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24780                     (cc >= 0xf900 && cc < 0xfb00 )
24781                 ) {
24782                         return match;
24783                 }  
24784          
24785                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24786                 return "&#" + cc + ";";
24787                 
24788                 
24789             });
24790             
24791             
24792              
24793             if(this.owner.fireEvent('beforesync', this, html) !== false){
24794                 this.el.dom.value = html;
24795                 this.owner.fireEvent('sync', this, html);
24796             }
24797         }
24798     },
24799
24800     /**
24801      * Protected method that will not generally be called directly. Pushes the value of the textarea
24802      * into the iframe editor.
24803      */
24804     pushValue : function(){
24805         if(this.initialized){
24806             var v = this.el.dom.value.trim();
24807             
24808 //            if(v.length < 1){
24809 //                v = '&#160;';
24810 //            }
24811             
24812             if(this.owner.fireEvent('beforepush', this, v) !== false){
24813                 var d = (this.doc.body || this.doc.documentElement);
24814                 d.innerHTML = v;
24815                 this.cleanUpPaste();
24816                 this.el.dom.value = d.innerHTML;
24817                 this.owner.fireEvent('push', this, v);
24818             }
24819         }
24820     },
24821
24822     // private
24823     deferFocus : function(){
24824         this.focus.defer(10, this);
24825     },
24826
24827     // doc'ed in Field
24828     focus : function(){
24829         if(this.win && !this.sourceEditMode){
24830             this.win.focus();
24831         }else{
24832             this.el.focus();
24833         }
24834     },
24835     
24836     assignDocWin: function()
24837     {
24838         var iframe = this.iframe;
24839         
24840          if(Roo.isIE){
24841             this.doc = iframe.contentWindow.document;
24842             this.win = iframe.contentWindow;
24843         } else {
24844 //            if (!Roo.get(this.frameId)) {
24845 //                return;
24846 //            }
24847 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24848 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24849             
24850             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24851                 return;
24852             }
24853             
24854             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24855             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24856         }
24857     },
24858     
24859     // private
24860     initEditor : function(){
24861         //console.log("INIT EDITOR");
24862         this.assignDocWin();
24863         
24864         
24865         
24866         this.doc.designMode="on";
24867         this.doc.open();
24868         this.doc.write(this.getDocMarkup());
24869         this.doc.close();
24870         
24871         var dbody = (this.doc.body || this.doc.documentElement);
24872         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24873         // this copies styles from the containing element into thsi one..
24874         // not sure why we need all of this..
24875         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24876         
24877         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24878         //ss['background-attachment'] = 'fixed'; // w3c
24879         dbody.bgProperties = 'fixed'; // ie
24880         //Roo.DomHelper.applyStyles(dbody, ss);
24881         Roo.EventManager.on(this.doc, {
24882             //'mousedown': this.onEditorEvent,
24883             'mouseup': this.onEditorEvent,
24884             'dblclick': this.onEditorEvent,
24885             'click': this.onEditorEvent,
24886             'keyup': this.onEditorEvent,
24887             buffer:100,
24888             scope: this
24889         });
24890         if(Roo.isGecko){
24891             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24892         }
24893         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24894             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24895         }
24896         this.initialized = true;
24897
24898         this.owner.fireEvent('initialize', this);
24899         this.pushValue();
24900     },
24901
24902     // private
24903     onDestroy : function(){
24904         
24905         
24906         
24907         if(this.rendered){
24908             
24909             //for (var i =0; i < this.toolbars.length;i++) {
24910             //    // fixme - ask toolbars for heights?
24911             //    this.toolbars[i].onDestroy();
24912            // }
24913             
24914             //this.wrap.dom.innerHTML = '';
24915             //this.wrap.remove();
24916         }
24917     },
24918
24919     // private
24920     onFirstFocus : function(){
24921         
24922         this.assignDocWin();
24923         
24924         
24925         this.activated = true;
24926          
24927     
24928         if(Roo.isGecko){ // prevent silly gecko errors
24929             this.win.focus();
24930             var s = this.win.getSelection();
24931             if(!s.focusNode || s.focusNode.nodeType != 3){
24932                 var r = s.getRangeAt(0);
24933                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24934                 r.collapse(true);
24935                 this.deferFocus();
24936             }
24937             try{
24938                 this.execCmd('useCSS', true);
24939                 this.execCmd('styleWithCSS', false);
24940             }catch(e){}
24941         }
24942         this.owner.fireEvent('activate', this);
24943     },
24944
24945     // private
24946     adjustFont: function(btn){
24947         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24948         //if(Roo.isSafari){ // safari
24949         //    adjust *= 2;
24950        // }
24951         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24952         if(Roo.isSafari){ // safari
24953             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24954             v =  (v < 10) ? 10 : v;
24955             v =  (v > 48) ? 48 : v;
24956             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24957             
24958         }
24959         
24960         
24961         v = Math.max(1, v+adjust);
24962         
24963         this.execCmd('FontSize', v  );
24964     },
24965
24966     onEditorEvent : function(e)
24967     {
24968         this.owner.fireEvent('editorevent', this, e);
24969       //  this.updateToolbar();
24970         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24971     },
24972
24973     insertTag : function(tg)
24974     {
24975         // could be a bit smarter... -> wrap the current selected tRoo..
24976         if (tg.toLowerCase() == 'span' ||
24977             tg.toLowerCase() == 'code' ||
24978             tg.toLowerCase() == 'sup' ||
24979             tg.toLowerCase() == 'sub' 
24980             ) {
24981             
24982             range = this.createRange(this.getSelection());
24983             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24984             wrappingNode.appendChild(range.extractContents());
24985             range.insertNode(wrappingNode);
24986
24987             return;
24988             
24989             
24990             
24991         }
24992         this.execCmd("formatblock",   tg);
24993         
24994     },
24995     
24996     insertText : function(txt)
24997     {
24998         
24999         
25000         var range = this.createRange();
25001         range.deleteContents();
25002                //alert(Sender.getAttribute('label'));
25003                
25004         range.insertNode(this.doc.createTextNode(txt));
25005     } ,
25006     
25007      
25008
25009     /**
25010      * Executes a Midas editor command on the editor document and performs necessary focus and
25011      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25012      * @param {String} cmd The Midas command
25013      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25014      */
25015     relayCmd : function(cmd, value){
25016         this.win.focus();
25017         this.execCmd(cmd, value);
25018         this.owner.fireEvent('editorevent', this);
25019         //this.updateToolbar();
25020         this.owner.deferFocus();
25021     },
25022
25023     /**
25024      * Executes a Midas editor command directly on the editor document.
25025      * For visual commands, you should use {@link #relayCmd} instead.
25026      * <b>This should only be called after the editor is initialized.</b>
25027      * @param {String} cmd The Midas command
25028      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25029      */
25030     execCmd : function(cmd, value){
25031         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25032         this.syncValue();
25033     },
25034  
25035  
25036    
25037     /**
25038      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25039      * to insert tRoo.
25040      * @param {String} text | dom node.. 
25041      */
25042     insertAtCursor : function(text)
25043     {
25044         
25045         if(!this.activated){
25046             return;
25047         }
25048         /*
25049         if(Roo.isIE){
25050             this.win.focus();
25051             var r = this.doc.selection.createRange();
25052             if(r){
25053                 r.collapse(true);
25054                 r.pasteHTML(text);
25055                 this.syncValue();
25056                 this.deferFocus();
25057             
25058             }
25059             return;
25060         }
25061         */
25062         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25063             this.win.focus();
25064             
25065             
25066             // from jquery ui (MIT licenced)
25067             var range, node;
25068             var win = this.win;
25069             
25070             if (win.getSelection && win.getSelection().getRangeAt) {
25071                 range = win.getSelection().getRangeAt(0);
25072                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25073                 range.insertNode(node);
25074             } else if (win.document.selection && win.document.selection.createRange) {
25075                 // no firefox support
25076                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25077                 win.document.selection.createRange().pasteHTML(txt);
25078             } else {
25079                 // no firefox support
25080                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25081                 this.execCmd('InsertHTML', txt);
25082             } 
25083             
25084             this.syncValue();
25085             
25086             this.deferFocus();
25087         }
25088     },
25089  // private
25090     mozKeyPress : function(e){
25091         if(e.ctrlKey){
25092             var c = e.getCharCode(), cmd;
25093           
25094             if(c > 0){
25095                 c = String.fromCharCode(c).toLowerCase();
25096                 switch(c){
25097                     case 'b':
25098                         cmd = 'bold';
25099                         break;
25100                     case 'i':
25101                         cmd = 'italic';
25102                         break;
25103                     
25104                     case 'u':
25105                         cmd = 'underline';
25106                         break;
25107                     
25108                     case 'v':
25109                         this.cleanUpPaste.defer(100, this);
25110                         return;
25111                         
25112                 }
25113                 if(cmd){
25114                     this.win.focus();
25115                     this.execCmd(cmd);
25116                     this.deferFocus();
25117                     e.preventDefault();
25118                 }
25119                 
25120             }
25121         }
25122     },
25123
25124     // private
25125     fixKeys : function(){ // load time branching for fastest keydown performance
25126         if(Roo.isIE){
25127             return function(e){
25128                 var k = e.getKey(), r;
25129                 if(k == e.TAB){
25130                     e.stopEvent();
25131                     r = this.doc.selection.createRange();
25132                     if(r){
25133                         r.collapse(true);
25134                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25135                         this.deferFocus();
25136                     }
25137                     return;
25138                 }
25139                 
25140                 if(k == e.ENTER){
25141                     r = this.doc.selection.createRange();
25142                     if(r){
25143                         var target = r.parentElement();
25144                         if(!target || target.tagName.toLowerCase() != 'li'){
25145                             e.stopEvent();
25146                             r.pasteHTML('<br />');
25147                             r.collapse(false);
25148                             r.select();
25149                         }
25150                     }
25151                 }
25152                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25153                     this.cleanUpPaste.defer(100, this);
25154                     return;
25155                 }
25156                 
25157                 
25158             };
25159         }else if(Roo.isOpera){
25160             return function(e){
25161                 var k = e.getKey();
25162                 if(k == e.TAB){
25163                     e.stopEvent();
25164                     this.win.focus();
25165                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25166                     this.deferFocus();
25167                 }
25168                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25169                     this.cleanUpPaste.defer(100, this);
25170                     return;
25171                 }
25172                 
25173             };
25174         }else if(Roo.isSafari){
25175             return function(e){
25176                 var k = e.getKey();
25177                 
25178                 if(k == e.TAB){
25179                     e.stopEvent();
25180                     this.execCmd('InsertText','\t');
25181                     this.deferFocus();
25182                     return;
25183                 }
25184                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25185                     this.cleanUpPaste.defer(100, this);
25186                     return;
25187                 }
25188                 
25189              };
25190         }
25191     }(),
25192     
25193     getAllAncestors: function()
25194     {
25195         var p = this.getSelectedNode();
25196         var a = [];
25197         if (!p) {
25198             a.push(p); // push blank onto stack..
25199             p = this.getParentElement();
25200         }
25201         
25202         
25203         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25204             a.push(p);
25205             p = p.parentNode;
25206         }
25207         a.push(this.doc.body);
25208         return a;
25209     },
25210     lastSel : false,
25211     lastSelNode : false,
25212     
25213     
25214     getSelection : function() 
25215     {
25216         this.assignDocWin();
25217         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25218     },
25219     
25220     getSelectedNode: function() 
25221     {
25222         // this may only work on Gecko!!!
25223         
25224         // should we cache this!!!!
25225         
25226         
25227         
25228          
25229         var range = this.createRange(this.getSelection()).cloneRange();
25230         
25231         if (Roo.isIE) {
25232             var parent = range.parentElement();
25233             while (true) {
25234                 var testRange = range.duplicate();
25235                 testRange.moveToElementText(parent);
25236                 if (testRange.inRange(range)) {
25237                     break;
25238                 }
25239                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25240                     break;
25241                 }
25242                 parent = parent.parentElement;
25243             }
25244             return parent;
25245         }
25246         
25247         // is ancestor a text element.
25248         var ac =  range.commonAncestorContainer;
25249         if (ac.nodeType == 3) {
25250             ac = ac.parentNode;
25251         }
25252         
25253         var ar = ac.childNodes;
25254          
25255         var nodes = [];
25256         var other_nodes = [];
25257         var has_other_nodes = false;
25258         for (var i=0;i<ar.length;i++) {
25259             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25260                 continue;
25261             }
25262             // fullly contained node.
25263             
25264             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25265                 nodes.push(ar[i]);
25266                 continue;
25267             }
25268             
25269             // probably selected..
25270             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25271                 other_nodes.push(ar[i]);
25272                 continue;
25273             }
25274             // outer..
25275             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25276                 continue;
25277             }
25278             
25279             
25280             has_other_nodes = true;
25281         }
25282         if (!nodes.length && other_nodes.length) {
25283             nodes= other_nodes;
25284         }
25285         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25286             return false;
25287         }
25288         
25289         return nodes[0];
25290     },
25291     createRange: function(sel)
25292     {
25293         // this has strange effects when using with 
25294         // top toolbar - not sure if it's a great idea.
25295         //this.editor.contentWindow.focus();
25296         if (typeof sel != "undefined") {
25297             try {
25298                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25299             } catch(e) {
25300                 return this.doc.createRange();
25301             }
25302         } else {
25303             return this.doc.createRange();
25304         }
25305     },
25306     getParentElement: function()
25307     {
25308         
25309         this.assignDocWin();
25310         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25311         
25312         var range = this.createRange(sel);
25313          
25314         try {
25315             var p = range.commonAncestorContainer;
25316             while (p.nodeType == 3) { // text node
25317                 p = p.parentNode;
25318             }
25319             return p;
25320         } catch (e) {
25321             return null;
25322         }
25323     
25324     },
25325     /***
25326      *
25327      * Range intersection.. the hard stuff...
25328      *  '-1' = before
25329      *  '0' = hits..
25330      *  '1' = after.
25331      *         [ -- selected range --- ]
25332      *   [fail]                        [fail]
25333      *
25334      *    basically..
25335      *      if end is before start or  hits it. fail.
25336      *      if start is after end or hits it fail.
25337      *
25338      *   if either hits (but other is outside. - then it's not 
25339      *   
25340      *    
25341      **/
25342     
25343     
25344     // @see http://www.thismuchiknow.co.uk/?p=64.
25345     rangeIntersectsNode : function(range, node)
25346     {
25347         var nodeRange = node.ownerDocument.createRange();
25348         try {
25349             nodeRange.selectNode(node);
25350         } catch (e) {
25351             nodeRange.selectNodeContents(node);
25352         }
25353     
25354         var rangeStartRange = range.cloneRange();
25355         rangeStartRange.collapse(true);
25356     
25357         var rangeEndRange = range.cloneRange();
25358         rangeEndRange.collapse(false);
25359     
25360         var nodeStartRange = nodeRange.cloneRange();
25361         nodeStartRange.collapse(true);
25362     
25363         var nodeEndRange = nodeRange.cloneRange();
25364         nodeEndRange.collapse(false);
25365     
25366         return rangeStartRange.compareBoundaryPoints(
25367                  Range.START_TO_START, nodeEndRange) == -1 &&
25368                rangeEndRange.compareBoundaryPoints(
25369                  Range.START_TO_START, nodeStartRange) == 1;
25370         
25371          
25372     },
25373     rangeCompareNode : function(range, node)
25374     {
25375         var nodeRange = node.ownerDocument.createRange();
25376         try {
25377             nodeRange.selectNode(node);
25378         } catch (e) {
25379             nodeRange.selectNodeContents(node);
25380         }
25381         
25382         
25383         range.collapse(true);
25384     
25385         nodeRange.collapse(true);
25386      
25387         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25388         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25389          
25390         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25391         
25392         var nodeIsBefore   =  ss == 1;
25393         var nodeIsAfter    = ee == -1;
25394         
25395         if (nodeIsBefore && nodeIsAfter) {
25396             return 0; // outer
25397         }
25398         if (!nodeIsBefore && nodeIsAfter) {
25399             return 1; //right trailed.
25400         }
25401         
25402         if (nodeIsBefore && !nodeIsAfter) {
25403             return 2;  // left trailed.
25404         }
25405         // fully contined.
25406         return 3;
25407     },
25408
25409     // private? - in a new class?
25410     cleanUpPaste :  function()
25411     {
25412         // cleans up the whole document..
25413         Roo.log('cleanuppaste');
25414         
25415         this.cleanUpChildren(this.doc.body);
25416         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25417         if (clean != this.doc.body.innerHTML) {
25418             this.doc.body.innerHTML = clean;
25419         }
25420         
25421     },
25422     
25423     cleanWordChars : function(input) {// change the chars to hex code
25424         var he = Roo.HtmlEditorCore;
25425         
25426         var output = input;
25427         Roo.each(he.swapCodes, function(sw) { 
25428             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25429             
25430             output = output.replace(swapper, sw[1]);
25431         });
25432         
25433         return output;
25434     },
25435     
25436     
25437     cleanUpChildren : function (n)
25438     {
25439         if (!n.childNodes.length) {
25440             return;
25441         }
25442         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25443            this.cleanUpChild(n.childNodes[i]);
25444         }
25445     },
25446     
25447     
25448         
25449     
25450     cleanUpChild : function (node)
25451     {
25452         var ed = this;
25453         //console.log(node);
25454         if (node.nodeName == "#text") {
25455             // clean up silly Windows -- stuff?
25456             return; 
25457         }
25458         if (node.nodeName == "#comment") {
25459             node.parentNode.removeChild(node);
25460             // clean up silly Windows -- stuff?
25461             return; 
25462         }
25463         var lcname = node.tagName.toLowerCase();
25464         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25465         // whitelist of tags..
25466         
25467         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25468             // remove node.
25469             node.parentNode.removeChild(node);
25470             return;
25471             
25472         }
25473         
25474         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25475         
25476         // spans with no attributes - just remove them..
25477         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25478             remove_keep_children = true;
25479         }
25480         
25481         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25482         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25483         
25484         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25485         //    remove_keep_children = true;
25486         //}
25487         
25488         if (remove_keep_children) {
25489             this.cleanUpChildren(node);
25490             // inserts everything just before this node...
25491             while (node.childNodes.length) {
25492                 var cn = node.childNodes[0];
25493                 node.removeChild(cn);
25494                 node.parentNode.insertBefore(cn, node);
25495             }
25496             node.parentNode.removeChild(node);
25497             return;
25498         }
25499         
25500         if (!node.attributes || !node.attributes.length) {
25501             
25502           
25503             
25504             
25505             this.cleanUpChildren(node);
25506             return;
25507         }
25508         
25509         function cleanAttr(n,v)
25510         {
25511             
25512             if (v.match(/^\./) || v.match(/^\//)) {
25513                 return;
25514             }
25515             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25516                 return;
25517             }
25518             if (v.match(/^#/)) {
25519                 return;
25520             }
25521             if (v.match(/^\{/)) { // allow template editing.
25522                 return;
25523             }
25524 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25525             node.removeAttribute(n);
25526             
25527         }
25528         
25529         var cwhite = this.cwhite;
25530         var cblack = this.cblack;
25531             
25532         function cleanStyle(n,v)
25533         {
25534             if (v.match(/expression/)) { //XSS?? should we even bother..
25535                 node.removeAttribute(n);
25536                 return;
25537             }
25538             
25539             var parts = v.split(/;/);
25540             var clean = [];
25541             
25542             Roo.each(parts, function(p) {
25543                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25544                 if (!p.length) {
25545                     return true;
25546                 }
25547                 var l = p.split(':').shift().replace(/\s+/g,'');
25548                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25549                 
25550                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25551 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25552                     //node.removeAttribute(n);
25553                     return true;
25554                 }
25555                 //Roo.log()
25556                 // only allow 'c whitelisted system attributes'
25557                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25558 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25559                     //node.removeAttribute(n);
25560                     return true;
25561                 }
25562                 
25563                 
25564                  
25565                 
25566                 clean.push(p);
25567                 return true;
25568             });
25569             if (clean.length) { 
25570                 node.setAttribute(n, clean.join(';'));
25571             } else {
25572                 node.removeAttribute(n);
25573             }
25574             
25575         }
25576         
25577         
25578         for (var i = node.attributes.length-1; i > -1 ; i--) {
25579             var a = node.attributes[i];
25580             //console.log(a);
25581             
25582             if (a.name.toLowerCase().substr(0,2)=='on')  {
25583                 node.removeAttribute(a.name);
25584                 continue;
25585             }
25586             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25587                 node.removeAttribute(a.name);
25588                 continue;
25589             }
25590             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25591                 cleanAttr(a.name,a.value); // fixme..
25592                 continue;
25593             }
25594             if (a.name == 'style') {
25595                 cleanStyle(a.name,a.value);
25596                 continue;
25597             }
25598             /// clean up MS crap..
25599             // tecnically this should be a list of valid class'es..
25600             
25601             
25602             if (a.name == 'class') {
25603                 if (a.value.match(/^Mso/)) {
25604                     node.removeAttribute('class');
25605                 }
25606                 
25607                 if (a.value.match(/^body$/)) {
25608                     node.removeAttribute('class');
25609                 }
25610                 continue;
25611             }
25612             
25613             // style cleanup!?
25614             // class cleanup?
25615             
25616         }
25617         
25618         
25619         this.cleanUpChildren(node);
25620         
25621         
25622     },
25623     
25624     /**
25625      * Clean up MS wordisms...
25626      */
25627     cleanWord : function(node)
25628     {
25629         if (!node) {
25630             this.cleanWord(this.doc.body);
25631             return;
25632         }
25633         
25634         if(
25635                 node.nodeName == 'SPAN' &&
25636                 !node.hasAttributes() &&
25637                 node.childNodes.length == 1 &&
25638                 node.firstChild.nodeName == "#text"  
25639         ) {
25640             var textNode = node.firstChild;
25641             node.removeChild(textNode);
25642             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25643                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25644             }
25645             node.parentNode.insertBefore(textNode, node);
25646             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25647                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25648             }
25649             node.parentNode.removeChild(node);
25650         }
25651         
25652         if (node.nodeName == "#text") {
25653             // clean up silly Windows -- stuff?
25654             return; 
25655         }
25656         if (node.nodeName == "#comment") {
25657             node.parentNode.removeChild(node);
25658             // clean up silly Windows -- stuff?
25659             return; 
25660         }
25661         
25662         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25663             node.parentNode.removeChild(node);
25664             return;
25665         }
25666         //Roo.log(node.tagName);
25667         // remove - but keep children..
25668         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25669             //Roo.log('-- removed');
25670             while (node.childNodes.length) {
25671                 var cn = node.childNodes[0];
25672                 node.removeChild(cn);
25673                 node.parentNode.insertBefore(cn, node);
25674                 // move node to parent - and clean it..
25675                 this.cleanWord(cn);
25676             }
25677             node.parentNode.removeChild(node);
25678             /// no need to iterate chidlren = it's got none..
25679             //this.iterateChildren(node, this.cleanWord);
25680             return;
25681         }
25682         // clean styles
25683         if (node.className.length) {
25684             
25685             var cn = node.className.split(/\W+/);
25686             var cna = [];
25687             Roo.each(cn, function(cls) {
25688                 if (cls.match(/Mso[a-zA-Z]+/)) {
25689                     return;
25690                 }
25691                 cna.push(cls);
25692             });
25693             node.className = cna.length ? cna.join(' ') : '';
25694             if (!cna.length) {
25695                 node.removeAttribute("class");
25696             }
25697         }
25698         
25699         if (node.hasAttribute("lang")) {
25700             node.removeAttribute("lang");
25701         }
25702         
25703         if (node.hasAttribute("style")) {
25704             
25705             var styles = node.getAttribute("style").split(";");
25706             var nstyle = [];
25707             Roo.each(styles, function(s) {
25708                 if (!s.match(/:/)) {
25709                     return;
25710                 }
25711                 var kv = s.split(":");
25712                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25713                     return;
25714                 }
25715                 // what ever is left... we allow.
25716                 nstyle.push(s);
25717             });
25718             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25719             if (!nstyle.length) {
25720                 node.removeAttribute('style');
25721             }
25722         }
25723         this.iterateChildren(node, this.cleanWord);
25724         
25725         
25726         
25727     },
25728     /**
25729      * iterateChildren of a Node, calling fn each time, using this as the scole..
25730      * @param {DomNode} node node to iterate children of.
25731      * @param {Function} fn method of this class to call on each item.
25732      */
25733     iterateChildren : function(node, fn)
25734     {
25735         if (!node.childNodes.length) {
25736                 return;
25737         }
25738         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25739            fn.call(this, node.childNodes[i])
25740         }
25741     },
25742     
25743     
25744     /**
25745      * cleanTableWidths.
25746      *
25747      * Quite often pasting from word etc.. results in tables with column and widths.
25748      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25749      *
25750      */
25751     cleanTableWidths : function(node)
25752     {
25753          
25754          
25755         if (!node) {
25756             this.cleanTableWidths(this.doc.body);
25757             return;
25758         }
25759         
25760         // ignore list...
25761         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25762             return; 
25763         }
25764         Roo.log(node.tagName);
25765         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25766             this.iterateChildren(node, this.cleanTableWidths);
25767             return;
25768         }
25769         if (node.hasAttribute('width')) {
25770             node.removeAttribute('width');
25771         }
25772         
25773          
25774         if (node.hasAttribute("style")) {
25775             // pretty basic...
25776             
25777             var styles = node.getAttribute("style").split(";");
25778             var nstyle = [];
25779             Roo.each(styles, function(s) {
25780                 if (!s.match(/:/)) {
25781                     return;
25782                 }
25783                 var kv = s.split(":");
25784                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25785                     return;
25786                 }
25787                 // what ever is left... we allow.
25788                 nstyle.push(s);
25789             });
25790             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25791             if (!nstyle.length) {
25792                 node.removeAttribute('style');
25793             }
25794         }
25795         
25796         this.iterateChildren(node, this.cleanTableWidths);
25797         
25798         
25799     },
25800     
25801     
25802     
25803     
25804     domToHTML : function(currentElement, depth, nopadtext) {
25805         
25806         depth = depth || 0;
25807         nopadtext = nopadtext || false;
25808     
25809         if (!currentElement) {
25810             return this.domToHTML(this.doc.body);
25811         }
25812         
25813         //Roo.log(currentElement);
25814         var j;
25815         var allText = false;
25816         var nodeName = currentElement.nodeName;
25817         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25818         
25819         if  (nodeName == '#text') {
25820             
25821             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25822         }
25823         
25824         
25825         var ret = '';
25826         if (nodeName != 'BODY') {
25827              
25828             var i = 0;
25829             // Prints the node tagName, such as <A>, <IMG>, etc
25830             if (tagName) {
25831                 var attr = [];
25832                 for(i = 0; i < currentElement.attributes.length;i++) {
25833                     // quoting?
25834                     var aname = currentElement.attributes.item(i).name;
25835                     if (!currentElement.attributes.item(i).value.length) {
25836                         continue;
25837                     }
25838                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25839                 }
25840                 
25841                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25842             } 
25843             else {
25844                 
25845                 // eack
25846             }
25847         } else {
25848             tagName = false;
25849         }
25850         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25851             return ret;
25852         }
25853         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25854             nopadtext = true;
25855         }
25856         
25857         
25858         // Traverse the tree
25859         i = 0;
25860         var currentElementChild = currentElement.childNodes.item(i);
25861         var allText = true;
25862         var innerHTML  = '';
25863         lastnode = '';
25864         while (currentElementChild) {
25865             // Formatting code (indent the tree so it looks nice on the screen)
25866             var nopad = nopadtext;
25867             if (lastnode == 'SPAN') {
25868                 nopad  = true;
25869             }
25870             // text
25871             if  (currentElementChild.nodeName == '#text') {
25872                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25873                 toadd = nopadtext ? toadd : toadd.trim();
25874                 if (!nopad && toadd.length > 80) {
25875                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25876                 }
25877                 innerHTML  += toadd;
25878                 
25879                 i++;
25880                 currentElementChild = currentElement.childNodes.item(i);
25881                 lastNode = '';
25882                 continue;
25883             }
25884             allText = false;
25885             
25886             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25887                 
25888             // Recursively traverse the tree structure of the child node
25889             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25890             lastnode = currentElementChild.nodeName;
25891             i++;
25892             currentElementChild=currentElement.childNodes.item(i);
25893         }
25894         
25895         ret += innerHTML;
25896         
25897         if (!allText) {
25898                 // The remaining code is mostly for formatting the tree
25899             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25900         }
25901         
25902         
25903         if (tagName) {
25904             ret+= "</"+tagName+">";
25905         }
25906         return ret;
25907         
25908     },
25909         
25910     applyBlacklists : function()
25911     {
25912         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25913         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25914         
25915         this.white = [];
25916         this.black = [];
25917         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25918             if (b.indexOf(tag) > -1) {
25919                 return;
25920             }
25921             this.white.push(tag);
25922             
25923         }, this);
25924         
25925         Roo.each(w, function(tag) {
25926             if (b.indexOf(tag) > -1) {
25927                 return;
25928             }
25929             if (this.white.indexOf(tag) > -1) {
25930                 return;
25931             }
25932             this.white.push(tag);
25933             
25934         }, this);
25935         
25936         
25937         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25938             if (w.indexOf(tag) > -1) {
25939                 return;
25940             }
25941             this.black.push(tag);
25942             
25943         }, this);
25944         
25945         Roo.each(b, function(tag) {
25946             if (w.indexOf(tag) > -1) {
25947                 return;
25948             }
25949             if (this.black.indexOf(tag) > -1) {
25950                 return;
25951             }
25952             this.black.push(tag);
25953             
25954         }, this);
25955         
25956         
25957         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25958         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25959         
25960         this.cwhite = [];
25961         this.cblack = [];
25962         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25963             if (b.indexOf(tag) > -1) {
25964                 return;
25965             }
25966             this.cwhite.push(tag);
25967             
25968         }, this);
25969         
25970         Roo.each(w, function(tag) {
25971             if (b.indexOf(tag) > -1) {
25972                 return;
25973             }
25974             if (this.cwhite.indexOf(tag) > -1) {
25975                 return;
25976             }
25977             this.cwhite.push(tag);
25978             
25979         }, this);
25980         
25981         
25982         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25983             if (w.indexOf(tag) > -1) {
25984                 return;
25985             }
25986             this.cblack.push(tag);
25987             
25988         }, this);
25989         
25990         Roo.each(b, function(tag) {
25991             if (w.indexOf(tag) > -1) {
25992                 return;
25993             }
25994             if (this.cblack.indexOf(tag) > -1) {
25995                 return;
25996             }
25997             this.cblack.push(tag);
25998             
25999         }, this);
26000     },
26001     
26002     setStylesheets : function(stylesheets)
26003     {
26004         if(typeof(stylesheets) == 'string'){
26005             Roo.get(this.iframe.contentDocument.head).createChild({
26006                 tag : 'link',
26007                 rel : 'stylesheet',
26008                 type : 'text/css',
26009                 href : stylesheets
26010             });
26011             
26012             return;
26013         }
26014         var _this = this;
26015      
26016         Roo.each(stylesheets, function(s) {
26017             if(!s.length){
26018                 return;
26019             }
26020             
26021             Roo.get(_this.iframe.contentDocument.head).createChild({
26022                 tag : 'link',
26023                 rel : 'stylesheet',
26024                 type : 'text/css',
26025                 href : s
26026             });
26027         });
26028
26029         
26030     },
26031     
26032     removeStylesheets : function()
26033     {
26034         var _this = this;
26035         
26036         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26037             s.remove();
26038         });
26039     },
26040     
26041     setStyle : function(style)
26042     {
26043         Roo.get(this.iframe.contentDocument.head).createChild({
26044             tag : 'style',
26045             type : 'text/css',
26046             html : style
26047         });
26048
26049         return;
26050     }
26051     
26052     // hide stuff that is not compatible
26053     /**
26054      * @event blur
26055      * @hide
26056      */
26057     /**
26058      * @event change
26059      * @hide
26060      */
26061     /**
26062      * @event focus
26063      * @hide
26064      */
26065     /**
26066      * @event specialkey
26067      * @hide
26068      */
26069     /**
26070      * @cfg {String} fieldClass @hide
26071      */
26072     /**
26073      * @cfg {String} focusClass @hide
26074      */
26075     /**
26076      * @cfg {String} autoCreate @hide
26077      */
26078     /**
26079      * @cfg {String} inputType @hide
26080      */
26081     /**
26082      * @cfg {String} invalidClass @hide
26083      */
26084     /**
26085      * @cfg {String} invalidText @hide
26086      */
26087     /**
26088      * @cfg {String} msgFx @hide
26089      */
26090     /**
26091      * @cfg {String} validateOnBlur @hide
26092      */
26093 });
26094
26095 Roo.HtmlEditorCore.white = [
26096         'area', 'br', 'img', 'input', 'hr', 'wbr',
26097         
26098        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26099        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26100        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26101        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26102        'table',   'ul',         'xmp', 
26103        
26104        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26105       'thead',   'tr', 
26106      
26107       'dir', 'menu', 'ol', 'ul', 'dl',
26108        
26109       'embed',  'object'
26110 ];
26111
26112
26113 Roo.HtmlEditorCore.black = [
26114     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26115         'applet', // 
26116         'base',   'basefont', 'bgsound', 'blink',  'body', 
26117         'frame',  'frameset', 'head',    'html',   'ilayer', 
26118         'iframe', 'layer',  'link',     'meta',    'object',   
26119         'script', 'style' ,'title',  'xml' // clean later..
26120 ];
26121 Roo.HtmlEditorCore.clean = [
26122     'script', 'style', 'title', 'xml'
26123 ];
26124 Roo.HtmlEditorCore.remove = [
26125     'font'
26126 ];
26127 // attributes..
26128
26129 Roo.HtmlEditorCore.ablack = [
26130     'on'
26131 ];
26132     
26133 Roo.HtmlEditorCore.aclean = [ 
26134     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26135 ];
26136
26137 // protocols..
26138 Roo.HtmlEditorCore.pwhite= [
26139         'http',  'https',  'mailto'
26140 ];
26141
26142 // white listed style attributes.
26143 Roo.HtmlEditorCore.cwhite= [
26144       //  'text-align', /// default is to allow most things..
26145       
26146          
26147 //        'font-size'//??
26148 ];
26149
26150 // black listed style attributes.
26151 Roo.HtmlEditorCore.cblack= [
26152       //  'font-size' -- this can be set by the project 
26153 ];
26154
26155
26156 Roo.HtmlEditorCore.swapCodes   =[ 
26157     [    8211, "&#8211;" ], 
26158     [    8212, "&#8212;" ], 
26159     [    8216,  "'" ],  
26160     [    8217, "'" ],  
26161     [    8220, '"' ],  
26162     [    8221, '"' ],  
26163     [    8226, "*" ],  
26164     [    8230, "..." ]
26165 ]; 
26166
26167     /*
26168  * - LGPL
26169  *
26170  * HtmlEditor
26171  * 
26172  */
26173
26174 /**
26175  * @class Roo.bootstrap.HtmlEditor
26176  * @extends Roo.bootstrap.TextArea
26177  * Bootstrap HtmlEditor class
26178
26179  * @constructor
26180  * Create a new HtmlEditor
26181  * @param {Object} config The config object
26182  */
26183
26184 Roo.bootstrap.HtmlEditor = function(config){
26185     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26186     if (!this.toolbars) {
26187         this.toolbars = [];
26188     }
26189     
26190     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26191     this.addEvents({
26192             /**
26193              * @event initialize
26194              * Fires when the editor is fully initialized (including the iframe)
26195              * @param {HtmlEditor} this
26196              */
26197             initialize: true,
26198             /**
26199              * @event activate
26200              * Fires when the editor is first receives the focus. Any insertion must wait
26201              * until after this event.
26202              * @param {HtmlEditor} this
26203              */
26204             activate: true,
26205              /**
26206              * @event beforesync
26207              * Fires before the textarea is updated with content from the editor iframe. Return false
26208              * to cancel the sync.
26209              * @param {HtmlEditor} this
26210              * @param {String} html
26211              */
26212             beforesync: true,
26213              /**
26214              * @event beforepush
26215              * Fires before the iframe editor is updated with content from the textarea. Return false
26216              * to cancel the push.
26217              * @param {HtmlEditor} this
26218              * @param {String} html
26219              */
26220             beforepush: true,
26221              /**
26222              * @event sync
26223              * Fires when the textarea is updated with content from the editor iframe.
26224              * @param {HtmlEditor} this
26225              * @param {String} html
26226              */
26227             sync: true,
26228              /**
26229              * @event push
26230              * Fires when the iframe editor is updated with content from the textarea.
26231              * @param {HtmlEditor} this
26232              * @param {String} html
26233              */
26234             push: true,
26235              /**
26236              * @event editmodechange
26237              * Fires when the editor switches edit modes
26238              * @param {HtmlEditor} this
26239              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26240              */
26241             editmodechange: true,
26242             /**
26243              * @event editorevent
26244              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26245              * @param {HtmlEditor} this
26246              */
26247             editorevent: true,
26248             /**
26249              * @event firstfocus
26250              * Fires when on first focus - needed by toolbars..
26251              * @param {HtmlEditor} this
26252              */
26253             firstfocus: true,
26254             /**
26255              * @event autosave
26256              * Auto save the htmlEditor value as a file into Events
26257              * @param {HtmlEditor} this
26258              */
26259             autosave: true,
26260             /**
26261              * @event savedpreview
26262              * preview the saved version of htmlEditor
26263              * @param {HtmlEditor} this
26264              */
26265             savedpreview: true
26266         });
26267 };
26268
26269
26270 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26271     
26272     
26273       /**
26274      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26275      */
26276     toolbars : false,
26277     
26278      /**
26279     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26280     */
26281     btns : [],
26282    
26283      /**
26284      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26285      *                        Roo.resizable.
26286      */
26287     resizable : false,
26288      /**
26289      * @cfg {Number} height (in pixels)
26290      */   
26291     height: 300,
26292    /**
26293      * @cfg {Number} width (in pixels)
26294      */   
26295     width: false,
26296     
26297     /**
26298      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26299      * 
26300      */
26301     stylesheets: false,
26302     
26303     // id of frame..
26304     frameId: false,
26305     
26306     // private properties
26307     validationEvent : false,
26308     deferHeight: true,
26309     initialized : false,
26310     activated : false,
26311     
26312     onFocus : Roo.emptyFn,
26313     iframePad:3,
26314     hideMode:'offsets',
26315     
26316     tbContainer : false,
26317     
26318     bodyCls : '',
26319     
26320     toolbarContainer :function() {
26321         return this.wrap.select('.x-html-editor-tb',true).first();
26322     },
26323
26324     /**
26325      * Protected method that will not generally be called directly. It
26326      * is called when the editor creates its toolbar. Override this method if you need to
26327      * add custom toolbar buttons.
26328      * @param {HtmlEditor} editor
26329      */
26330     createToolbar : function(){
26331         Roo.log('renewing');
26332         Roo.log("create toolbars");
26333         
26334         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26335         this.toolbars[0].render(this.toolbarContainer());
26336         
26337         return;
26338         
26339 //        if (!editor.toolbars || !editor.toolbars.length) {
26340 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26341 //        }
26342 //        
26343 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26344 //            editor.toolbars[i] = Roo.factory(
26345 //                    typeof(editor.toolbars[i]) == 'string' ?
26346 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26347 //                Roo.bootstrap.HtmlEditor);
26348 //            editor.toolbars[i].init(editor);
26349 //        }
26350     },
26351
26352      
26353     // private
26354     onRender : function(ct, position)
26355     {
26356        // Roo.log("Call onRender: " + this.xtype);
26357         var _t = this;
26358         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26359       
26360         this.wrap = this.inputEl().wrap({
26361             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26362         });
26363         
26364         this.editorcore.onRender(ct, position);
26365          
26366         if (this.resizable) {
26367             this.resizeEl = new Roo.Resizable(this.wrap, {
26368                 pinned : true,
26369                 wrap: true,
26370                 dynamic : true,
26371                 minHeight : this.height,
26372                 height: this.height,
26373                 handles : this.resizable,
26374                 width: this.width,
26375                 listeners : {
26376                     resize : function(r, w, h) {
26377                         _t.onResize(w,h); // -something
26378                     }
26379                 }
26380             });
26381             
26382         }
26383         this.createToolbar(this);
26384        
26385         
26386         if(!this.width && this.resizable){
26387             this.setSize(this.wrap.getSize());
26388         }
26389         if (this.resizeEl) {
26390             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26391             // should trigger onReize..
26392         }
26393         
26394     },
26395
26396     // private
26397     onResize : function(w, h)
26398     {
26399         Roo.log('resize: ' +w + ',' + h );
26400         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26401         var ew = false;
26402         var eh = false;
26403         
26404         if(this.inputEl() ){
26405             if(typeof w == 'number'){
26406                 var aw = w - this.wrap.getFrameWidth('lr');
26407                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26408                 ew = aw;
26409             }
26410             if(typeof h == 'number'){
26411                  var tbh = -11;  // fixme it needs to tool bar size!
26412                 for (var i =0; i < this.toolbars.length;i++) {
26413                     // fixme - ask toolbars for heights?
26414                     tbh += this.toolbars[i].el.getHeight();
26415                     //if (this.toolbars[i].footer) {
26416                     //    tbh += this.toolbars[i].footer.el.getHeight();
26417                     //}
26418                 }
26419               
26420                 
26421                 
26422                 
26423                 
26424                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26425                 ah -= 5; // knock a few pixes off for look..
26426                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26427                 var eh = ah;
26428             }
26429         }
26430         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26431         this.editorcore.onResize(ew,eh);
26432         
26433     },
26434
26435     /**
26436      * Toggles the editor between standard and source edit mode.
26437      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26438      */
26439     toggleSourceEdit : function(sourceEditMode)
26440     {
26441         this.editorcore.toggleSourceEdit(sourceEditMode);
26442         
26443         if(this.editorcore.sourceEditMode){
26444             Roo.log('editor - showing textarea');
26445             
26446 //            Roo.log('in');
26447 //            Roo.log(this.syncValue());
26448             this.syncValue();
26449             this.inputEl().removeClass(['hide', 'x-hidden']);
26450             this.inputEl().dom.removeAttribute('tabIndex');
26451             this.inputEl().focus();
26452         }else{
26453             Roo.log('editor - hiding textarea');
26454 //            Roo.log('out')
26455 //            Roo.log(this.pushValue()); 
26456             this.pushValue();
26457             
26458             this.inputEl().addClass(['hide', 'x-hidden']);
26459             this.inputEl().dom.setAttribute('tabIndex', -1);
26460             //this.deferFocus();
26461         }
26462          
26463         if(this.resizable){
26464             this.setSize(this.wrap.getSize());
26465         }
26466         
26467         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26468     },
26469  
26470     // private (for BoxComponent)
26471     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26472
26473     // private (for BoxComponent)
26474     getResizeEl : function(){
26475         return this.wrap;
26476     },
26477
26478     // private (for BoxComponent)
26479     getPositionEl : function(){
26480         return this.wrap;
26481     },
26482
26483     // private
26484     initEvents : function(){
26485         this.originalValue = this.getValue();
26486     },
26487
26488 //    /**
26489 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26490 //     * @method
26491 //     */
26492 //    markInvalid : Roo.emptyFn,
26493 //    /**
26494 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26495 //     * @method
26496 //     */
26497 //    clearInvalid : Roo.emptyFn,
26498
26499     setValue : function(v){
26500         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26501         this.editorcore.pushValue();
26502     },
26503
26504      
26505     // private
26506     deferFocus : function(){
26507         this.focus.defer(10, this);
26508     },
26509
26510     // doc'ed in Field
26511     focus : function(){
26512         this.editorcore.focus();
26513         
26514     },
26515       
26516
26517     // private
26518     onDestroy : function(){
26519         
26520         
26521         
26522         if(this.rendered){
26523             
26524             for (var i =0; i < this.toolbars.length;i++) {
26525                 // fixme - ask toolbars for heights?
26526                 this.toolbars[i].onDestroy();
26527             }
26528             
26529             this.wrap.dom.innerHTML = '';
26530             this.wrap.remove();
26531         }
26532     },
26533
26534     // private
26535     onFirstFocus : function(){
26536         //Roo.log("onFirstFocus");
26537         this.editorcore.onFirstFocus();
26538          for (var i =0; i < this.toolbars.length;i++) {
26539             this.toolbars[i].onFirstFocus();
26540         }
26541         
26542     },
26543     
26544     // private
26545     syncValue : function()
26546     {   
26547         this.editorcore.syncValue();
26548     },
26549     
26550     pushValue : function()
26551     {   
26552         this.editorcore.pushValue();
26553     }
26554      
26555     
26556     // hide stuff that is not compatible
26557     /**
26558      * @event blur
26559      * @hide
26560      */
26561     /**
26562      * @event change
26563      * @hide
26564      */
26565     /**
26566      * @event focus
26567      * @hide
26568      */
26569     /**
26570      * @event specialkey
26571      * @hide
26572      */
26573     /**
26574      * @cfg {String} fieldClass @hide
26575      */
26576     /**
26577      * @cfg {String} focusClass @hide
26578      */
26579     /**
26580      * @cfg {String} autoCreate @hide
26581      */
26582     /**
26583      * @cfg {String} inputType @hide
26584      */
26585      
26586     /**
26587      * @cfg {String} invalidText @hide
26588      */
26589     /**
26590      * @cfg {String} msgFx @hide
26591      */
26592     /**
26593      * @cfg {String} validateOnBlur @hide
26594      */
26595 });
26596  
26597     
26598    
26599    
26600    
26601       
26602 Roo.namespace('Roo.bootstrap.htmleditor');
26603 /**
26604  * @class Roo.bootstrap.HtmlEditorToolbar1
26605  * Basic Toolbar
26606  * 
26607  * @example
26608  * Usage:
26609  *
26610  new Roo.bootstrap.HtmlEditor({
26611     ....
26612     toolbars : [
26613         new Roo.bootstrap.HtmlEditorToolbar1({
26614             disable : { fonts: 1 , format: 1, ..., ... , ...],
26615             btns : [ .... ]
26616         })
26617     }
26618      
26619  * 
26620  * @cfg {Object} disable List of elements to disable..
26621  * @cfg {Array} btns List of additional buttons.
26622  * 
26623  * 
26624  * NEEDS Extra CSS? 
26625  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26626  */
26627  
26628 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26629 {
26630     
26631     Roo.apply(this, config);
26632     
26633     // default disabled, based on 'good practice'..
26634     this.disable = this.disable || {};
26635     Roo.applyIf(this.disable, {
26636         fontSize : true,
26637         colors : true,
26638         specialElements : true
26639     });
26640     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26641     
26642     this.editor = config.editor;
26643     this.editorcore = config.editor.editorcore;
26644     
26645     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26646     
26647     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26648     // dont call parent... till later.
26649 }
26650 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26651      
26652     bar : true,
26653     
26654     editor : false,
26655     editorcore : false,
26656     
26657     
26658     formats : [
26659         "p" ,  
26660         "h1","h2","h3","h4","h5","h6", 
26661         "pre", "code", 
26662         "abbr", "acronym", "address", "cite", "samp", "var",
26663         'div','span'
26664     ],
26665     
26666     onRender : function(ct, position)
26667     {
26668        // Roo.log("Call onRender: " + this.xtype);
26669         
26670        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26671        Roo.log(this.el);
26672        this.el.dom.style.marginBottom = '0';
26673        var _this = this;
26674        var editorcore = this.editorcore;
26675        var editor= this.editor;
26676        
26677        var children = [];
26678        var btn = function(id,cmd , toggle, handler, html){
26679        
26680             var  event = toggle ? 'toggle' : 'click';
26681        
26682             var a = {
26683                 size : 'sm',
26684                 xtype: 'Button',
26685                 xns: Roo.bootstrap,
26686                 //glyphicon : id,
26687                 fa: id,
26688                 cmd : id || cmd,
26689                 enableToggle:toggle !== false,
26690                 html : html || '',
26691                 pressed : toggle ? false : null,
26692                 listeners : {}
26693             };
26694             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26695                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26696             };
26697             children.push(a);
26698             return a;
26699        }
26700        
26701     //    var cb_box = function...
26702         
26703         var style = {
26704                 xtype: 'Button',
26705                 size : 'sm',
26706                 xns: Roo.bootstrap,
26707                 fa : 'font',
26708                 //html : 'submit'
26709                 menu : {
26710                     xtype: 'Menu',
26711                     xns: Roo.bootstrap,
26712                     items:  []
26713                 }
26714         };
26715         Roo.each(this.formats, function(f) {
26716             style.menu.items.push({
26717                 xtype :'MenuItem',
26718                 xns: Roo.bootstrap,
26719                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26720                 tagname : f,
26721                 listeners : {
26722                     click : function()
26723                     {
26724                         editorcore.insertTag(this.tagname);
26725                         editor.focus();
26726                     }
26727                 }
26728                 
26729             });
26730         });
26731         children.push(style);   
26732         
26733         btn('bold',false,true);
26734         btn('italic',false,true);
26735         btn('align-left', 'justifyleft',true);
26736         btn('align-center', 'justifycenter',true);
26737         btn('align-right' , 'justifyright',true);
26738         btn('link', false, false, function(btn) {
26739             //Roo.log("create link?");
26740             var url = prompt(this.createLinkText, this.defaultLinkValue);
26741             if(url && url != 'http:/'+'/'){
26742                 this.editorcore.relayCmd('createlink', url);
26743             }
26744         }),
26745         btn('list','insertunorderedlist',true);
26746         btn('pencil', false,true, function(btn){
26747                 Roo.log(this);
26748                 this.toggleSourceEdit(btn.pressed);
26749         });
26750         
26751         if (this.editor.btns.length > 0) {
26752             for (var i = 0; i<this.editor.btns.length; i++) {
26753                 children.push(this.editor.btns[i]);
26754             }
26755         }
26756         
26757         /*
26758         var cog = {
26759                 xtype: 'Button',
26760                 size : 'sm',
26761                 xns: Roo.bootstrap,
26762                 glyphicon : 'cog',
26763                 //html : 'submit'
26764                 menu : {
26765                     xtype: 'Menu',
26766                     xns: Roo.bootstrap,
26767                     items:  []
26768                 }
26769         };
26770         
26771         cog.menu.items.push({
26772             xtype :'MenuItem',
26773             xns: Roo.bootstrap,
26774             html : Clean styles,
26775             tagname : f,
26776             listeners : {
26777                 click : function()
26778                 {
26779                     editorcore.insertTag(this.tagname);
26780                     editor.focus();
26781                 }
26782             }
26783             
26784         });
26785        */
26786         
26787          
26788        this.xtype = 'NavSimplebar';
26789         
26790         for(var i=0;i< children.length;i++) {
26791             
26792             this.buttons.add(this.addxtypeChild(children[i]));
26793             
26794         }
26795         
26796         editor.on('editorevent', this.updateToolbar, this);
26797     },
26798     onBtnClick : function(id)
26799     {
26800        this.editorcore.relayCmd(id);
26801        this.editorcore.focus();
26802     },
26803     
26804     /**
26805      * Protected method that will not generally be called directly. It triggers
26806      * a toolbar update by reading the markup state of the current selection in the editor.
26807      */
26808     updateToolbar: function(){
26809
26810         if(!this.editorcore.activated){
26811             this.editor.onFirstFocus(); // is this neeed?
26812             return;
26813         }
26814
26815         var btns = this.buttons; 
26816         var doc = this.editorcore.doc;
26817         btns.get('bold').setActive(doc.queryCommandState('bold'));
26818         btns.get('italic').setActive(doc.queryCommandState('italic'));
26819         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26820         
26821         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26822         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26823         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26824         
26825         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26826         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26827          /*
26828         
26829         var ans = this.editorcore.getAllAncestors();
26830         if (this.formatCombo) {
26831             
26832             
26833             var store = this.formatCombo.store;
26834             this.formatCombo.setValue("");
26835             for (var i =0; i < ans.length;i++) {
26836                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26837                     // select it..
26838                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26839                     break;
26840                 }
26841             }
26842         }
26843         
26844         
26845         
26846         // hides menus... - so this cant be on a menu...
26847         Roo.bootstrap.MenuMgr.hideAll();
26848         */
26849         Roo.bootstrap.MenuMgr.hideAll();
26850         //this.editorsyncValue();
26851     },
26852     onFirstFocus: function() {
26853         this.buttons.each(function(item){
26854            item.enable();
26855         });
26856     },
26857     toggleSourceEdit : function(sourceEditMode){
26858         
26859           
26860         if(sourceEditMode){
26861             Roo.log("disabling buttons");
26862            this.buttons.each( function(item){
26863                 if(item.cmd != 'pencil'){
26864                     item.disable();
26865                 }
26866             });
26867           
26868         }else{
26869             Roo.log("enabling buttons");
26870             if(this.editorcore.initialized){
26871                 this.buttons.each( function(item){
26872                     item.enable();
26873                 });
26874             }
26875             
26876         }
26877         Roo.log("calling toggole on editor");
26878         // tell the editor that it's been pressed..
26879         this.editor.toggleSourceEdit(sourceEditMode);
26880        
26881     }
26882 });
26883
26884
26885
26886
26887  
26888 /*
26889  * - LGPL
26890  */
26891
26892 /**
26893  * @class Roo.bootstrap.Markdown
26894  * @extends Roo.bootstrap.TextArea
26895  * Bootstrap Showdown editable area
26896  * @cfg {string} content
26897  * 
26898  * @constructor
26899  * Create a new Showdown
26900  */
26901
26902 Roo.bootstrap.Markdown = function(config){
26903     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26904    
26905 };
26906
26907 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26908     
26909     editing :false,
26910     
26911     initEvents : function()
26912     {
26913         
26914         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26915         this.markdownEl = this.el.createChild({
26916             cls : 'roo-markdown-area'
26917         });
26918         this.inputEl().addClass('d-none');
26919         if (this.getValue() == '') {
26920             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26921             
26922         } else {
26923             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26924         }
26925         this.markdownEl.on('click', this.toggleTextEdit, this);
26926         this.on('blur', this.toggleTextEdit, this);
26927         this.on('specialkey', this.resizeTextArea, this);
26928     },
26929     
26930     toggleTextEdit : function()
26931     {
26932         var sh = this.markdownEl.getHeight();
26933         this.inputEl().addClass('d-none');
26934         this.markdownEl.addClass('d-none');
26935         if (!this.editing) {
26936             // show editor?
26937             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26938             this.inputEl().removeClass('d-none');
26939             this.inputEl().focus();
26940             this.editing = true;
26941             return;
26942         }
26943         // show showdown...
26944         this.updateMarkdown();
26945         this.markdownEl.removeClass('d-none');
26946         this.editing = false;
26947         return;
26948     },
26949     updateMarkdown : function()
26950     {
26951         if (this.getValue() == '') {
26952             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26953             return;
26954         }
26955  
26956         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26957     },
26958     
26959     resizeTextArea: function () {
26960         
26961         var sh = 100;
26962         Roo.log([sh, this.getValue().split("\n").length * 30]);
26963         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26964     },
26965     setValue : function(val)
26966     {
26967         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26968         if (!this.editing) {
26969             this.updateMarkdown();
26970         }
26971         
26972     },
26973     focus : function()
26974     {
26975         if (!this.editing) {
26976             this.toggleTextEdit();
26977         }
26978         
26979     }
26980
26981
26982 });
26983 /**
26984  * @class Roo.bootstrap.Table.AbstractSelectionModel
26985  * @extends Roo.util.Observable
26986  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26987  * implemented by descendant classes.  This class should not be directly instantiated.
26988  * @constructor
26989  */
26990 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26991     this.locked = false;
26992     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26993 };
26994
26995
26996 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26997     /** @ignore Called by the grid automatically. Do not call directly. */
26998     init : function(grid){
26999         this.grid = grid;
27000         this.initEvents();
27001     },
27002
27003     /**
27004      * Locks the selections.
27005      */
27006     lock : function(){
27007         this.locked = true;
27008     },
27009
27010     /**
27011      * Unlocks the selections.
27012      */
27013     unlock : function(){
27014         this.locked = false;
27015     },
27016
27017     /**
27018      * Returns true if the selections are locked.
27019      * @return {Boolean}
27020      */
27021     isLocked : function(){
27022         return this.locked;
27023     },
27024     
27025     
27026     initEvents : function ()
27027     {
27028         
27029     }
27030 });
27031 /**
27032  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27033  * @class Roo.bootstrap.Table.RowSelectionModel
27034  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27035  * It supports multiple selections and keyboard selection/navigation. 
27036  * @constructor
27037  * @param {Object} config
27038  */
27039
27040 Roo.bootstrap.Table.RowSelectionModel = function(config){
27041     Roo.apply(this, config);
27042     this.selections = new Roo.util.MixedCollection(false, function(o){
27043         return o.id;
27044     });
27045
27046     this.last = false;
27047     this.lastActive = false;
27048
27049     this.addEvents({
27050         /**
27051              * @event selectionchange
27052              * Fires when the selection changes
27053              * @param {SelectionModel} this
27054              */
27055             "selectionchange" : true,
27056         /**
27057              * @event afterselectionchange
27058              * Fires after the selection changes (eg. by key press or clicking)
27059              * @param {SelectionModel} this
27060              */
27061             "afterselectionchange" : true,
27062         /**
27063              * @event beforerowselect
27064              * Fires when a row is selected being selected, return false to cancel.
27065              * @param {SelectionModel} this
27066              * @param {Number} rowIndex The selected index
27067              * @param {Boolean} keepExisting False if other selections will be cleared
27068              */
27069             "beforerowselect" : true,
27070         /**
27071              * @event rowselect
27072              * Fires when a row is selected.
27073              * @param {SelectionModel} this
27074              * @param {Number} rowIndex The selected index
27075              * @param {Roo.data.Record} r The record
27076              */
27077             "rowselect" : true,
27078         /**
27079              * @event rowdeselect
27080              * Fires when a row is deselected.
27081              * @param {SelectionModel} this
27082              * @param {Number} rowIndex The selected index
27083              */
27084         "rowdeselect" : true
27085     });
27086     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27087     this.locked = false;
27088  };
27089
27090 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27091     /**
27092      * @cfg {Boolean} singleSelect
27093      * True to allow selection of only one row at a time (defaults to false)
27094      */
27095     singleSelect : false,
27096
27097     // private
27098     initEvents : function()
27099     {
27100
27101         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27102         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27103         //}else{ // allow click to work like normal
27104          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27105         //}
27106         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27107         this.grid.on("rowclick", this.handleMouseDown, this);
27108         
27109         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27110             "up" : function(e){
27111                 if(!e.shiftKey){
27112                     this.selectPrevious(e.shiftKey);
27113                 }else if(this.last !== false && this.lastActive !== false){
27114                     var last = this.last;
27115                     this.selectRange(this.last,  this.lastActive-1);
27116                     this.grid.getView().focusRow(this.lastActive);
27117                     if(last !== false){
27118                         this.last = last;
27119                     }
27120                 }else{
27121                     this.selectFirstRow();
27122                 }
27123                 this.fireEvent("afterselectionchange", this);
27124             },
27125             "down" : function(e){
27126                 if(!e.shiftKey){
27127                     this.selectNext(e.shiftKey);
27128                 }else if(this.last !== false && this.lastActive !== false){
27129                     var last = this.last;
27130                     this.selectRange(this.last,  this.lastActive+1);
27131                     this.grid.getView().focusRow(this.lastActive);
27132                     if(last !== false){
27133                         this.last = last;
27134                     }
27135                 }else{
27136                     this.selectFirstRow();
27137                 }
27138                 this.fireEvent("afterselectionchange", this);
27139             },
27140             scope: this
27141         });
27142         this.grid.store.on('load', function(){
27143             this.selections.clear();
27144         },this);
27145         /*
27146         var view = this.grid.view;
27147         view.on("refresh", this.onRefresh, this);
27148         view.on("rowupdated", this.onRowUpdated, this);
27149         view.on("rowremoved", this.onRemove, this);
27150         */
27151     },
27152
27153     // private
27154     onRefresh : function()
27155     {
27156         var ds = this.grid.store, i, v = this.grid.view;
27157         var s = this.selections;
27158         s.each(function(r){
27159             if((i = ds.indexOfId(r.id)) != -1){
27160                 v.onRowSelect(i);
27161             }else{
27162                 s.remove(r);
27163             }
27164         });
27165     },
27166
27167     // private
27168     onRemove : function(v, index, r){
27169         this.selections.remove(r);
27170     },
27171
27172     // private
27173     onRowUpdated : function(v, index, r){
27174         if(this.isSelected(r)){
27175             v.onRowSelect(index);
27176         }
27177     },
27178
27179     /**
27180      * Select records.
27181      * @param {Array} records The records to select
27182      * @param {Boolean} keepExisting (optional) True to keep existing selections
27183      */
27184     selectRecords : function(records, keepExisting)
27185     {
27186         if(!keepExisting){
27187             this.clearSelections();
27188         }
27189             var ds = this.grid.store;
27190         for(var i = 0, len = records.length; i < len; i++){
27191             this.selectRow(ds.indexOf(records[i]), true);
27192         }
27193     },
27194
27195     /**
27196      * Gets the number of selected rows.
27197      * @return {Number}
27198      */
27199     getCount : function(){
27200         return this.selections.length;
27201     },
27202
27203     /**
27204      * Selects the first row in the grid.
27205      */
27206     selectFirstRow : function(){
27207         this.selectRow(0);
27208     },
27209
27210     /**
27211      * Select the last row.
27212      * @param {Boolean} keepExisting (optional) True to keep existing selections
27213      */
27214     selectLastRow : function(keepExisting){
27215         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27216         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27217     },
27218
27219     /**
27220      * Selects the row immediately following the last selected row.
27221      * @param {Boolean} keepExisting (optional) True to keep existing selections
27222      */
27223     selectNext : function(keepExisting)
27224     {
27225             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27226             this.selectRow(this.last+1, keepExisting);
27227             this.grid.getView().focusRow(this.last);
27228         }
27229     },
27230
27231     /**
27232      * Selects the row that precedes the last selected row.
27233      * @param {Boolean} keepExisting (optional) True to keep existing selections
27234      */
27235     selectPrevious : function(keepExisting){
27236         if(this.last){
27237             this.selectRow(this.last-1, keepExisting);
27238             this.grid.getView().focusRow(this.last);
27239         }
27240     },
27241
27242     /**
27243      * Returns the selected records
27244      * @return {Array} Array of selected records
27245      */
27246     getSelections : function(){
27247         return [].concat(this.selections.items);
27248     },
27249
27250     /**
27251      * Returns the first selected record.
27252      * @return {Record}
27253      */
27254     getSelected : function(){
27255         return this.selections.itemAt(0);
27256     },
27257
27258
27259     /**
27260      * Clears all selections.
27261      */
27262     clearSelections : function(fast)
27263     {
27264         if(this.locked) {
27265             return;
27266         }
27267         if(fast !== true){
27268                 var ds = this.grid.store;
27269             var s = this.selections;
27270             s.each(function(r){
27271                 this.deselectRow(ds.indexOfId(r.id));
27272             }, this);
27273             s.clear();
27274         }else{
27275             this.selections.clear();
27276         }
27277         this.last = false;
27278     },
27279
27280
27281     /**
27282      * Selects all rows.
27283      */
27284     selectAll : function(){
27285         if(this.locked) {
27286             return;
27287         }
27288         this.selections.clear();
27289         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27290             this.selectRow(i, true);
27291         }
27292     },
27293
27294     /**
27295      * Returns True if there is a selection.
27296      * @return {Boolean}
27297      */
27298     hasSelection : function(){
27299         return this.selections.length > 0;
27300     },
27301
27302     /**
27303      * Returns True if the specified row is selected.
27304      * @param {Number/Record} record The record or index of the record to check
27305      * @return {Boolean}
27306      */
27307     isSelected : function(index){
27308             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27309         return (r && this.selections.key(r.id) ? true : false);
27310     },
27311
27312     /**
27313      * Returns True if the specified record id is selected.
27314      * @param {String} id The id of record to check
27315      * @return {Boolean}
27316      */
27317     isIdSelected : function(id){
27318         return (this.selections.key(id) ? true : false);
27319     },
27320
27321
27322     // private
27323     handleMouseDBClick : function(e, t){
27324         
27325     },
27326     // private
27327     handleMouseDown : function(e, t)
27328     {
27329             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27330         if(this.isLocked() || rowIndex < 0 ){
27331             return;
27332         };
27333         if(e.shiftKey && this.last !== false){
27334             var last = this.last;
27335             this.selectRange(last, rowIndex, e.ctrlKey);
27336             this.last = last; // reset the last
27337             t.focus();
27338     
27339         }else{
27340             var isSelected = this.isSelected(rowIndex);
27341             //Roo.log("select row:" + rowIndex);
27342             if(isSelected){
27343                 this.deselectRow(rowIndex);
27344             } else {
27345                         this.selectRow(rowIndex, true);
27346             }
27347     
27348             /*
27349                 if(e.button !== 0 && isSelected){
27350                 alert('rowIndex 2: ' + rowIndex);
27351                     view.focusRow(rowIndex);
27352                 }else if(e.ctrlKey && isSelected){
27353                     this.deselectRow(rowIndex);
27354                 }else if(!isSelected){
27355                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27356                     view.focusRow(rowIndex);
27357                 }
27358             */
27359         }
27360         this.fireEvent("afterselectionchange", this);
27361     },
27362     // private
27363     handleDragableRowClick :  function(grid, rowIndex, e) 
27364     {
27365         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27366             this.selectRow(rowIndex, false);
27367             grid.view.focusRow(rowIndex);
27368              this.fireEvent("afterselectionchange", this);
27369         }
27370     },
27371     
27372     /**
27373      * Selects multiple rows.
27374      * @param {Array} rows Array of the indexes of the row to select
27375      * @param {Boolean} keepExisting (optional) True to keep existing selections
27376      */
27377     selectRows : function(rows, keepExisting){
27378         if(!keepExisting){
27379             this.clearSelections();
27380         }
27381         for(var i = 0, len = rows.length; i < len; i++){
27382             this.selectRow(rows[i], true);
27383         }
27384     },
27385
27386     /**
27387      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27388      * @param {Number} startRow The index of the first row in the range
27389      * @param {Number} endRow The index of the last row in the range
27390      * @param {Boolean} keepExisting (optional) True to retain existing selections
27391      */
27392     selectRange : function(startRow, endRow, keepExisting){
27393         if(this.locked) {
27394             return;
27395         }
27396         if(!keepExisting){
27397             this.clearSelections();
27398         }
27399         if(startRow <= endRow){
27400             for(var i = startRow; i <= endRow; i++){
27401                 this.selectRow(i, true);
27402             }
27403         }else{
27404             for(var i = startRow; i >= endRow; i--){
27405                 this.selectRow(i, true);
27406             }
27407         }
27408     },
27409
27410     /**
27411      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27412      * @param {Number} startRow The index of the first row in the range
27413      * @param {Number} endRow The index of the last row in the range
27414      */
27415     deselectRange : function(startRow, endRow, preventViewNotify){
27416         if(this.locked) {
27417             return;
27418         }
27419         for(var i = startRow; i <= endRow; i++){
27420             this.deselectRow(i, preventViewNotify);
27421         }
27422     },
27423
27424     /**
27425      * Selects a row.
27426      * @param {Number} row The index of the row to select
27427      * @param {Boolean} keepExisting (optional) True to keep existing selections
27428      */
27429     selectRow : function(index, keepExisting, preventViewNotify)
27430     {
27431             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27432             return;
27433         }
27434         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27435             if(!keepExisting || this.singleSelect){
27436                 this.clearSelections();
27437             }
27438             
27439             var r = this.grid.store.getAt(index);
27440             //console.log('selectRow - record id :' + r.id);
27441             
27442             this.selections.add(r);
27443             this.last = this.lastActive = index;
27444             if(!preventViewNotify){
27445                 var proxy = new Roo.Element(
27446                                 this.grid.getRowDom(index)
27447                 );
27448                 proxy.addClass('bg-info info');
27449             }
27450             this.fireEvent("rowselect", this, index, r);
27451             this.fireEvent("selectionchange", this);
27452         }
27453     },
27454
27455     /**
27456      * Deselects a row.
27457      * @param {Number} row The index of the row to deselect
27458      */
27459     deselectRow : function(index, preventViewNotify)
27460     {
27461         if(this.locked) {
27462             return;
27463         }
27464         if(this.last == index){
27465             this.last = false;
27466         }
27467         if(this.lastActive == index){
27468             this.lastActive = false;
27469         }
27470         
27471         var r = this.grid.store.getAt(index);
27472         if (!r) {
27473             return;
27474         }
27475         
27476         this.selections.remove(r);
27477         //.console.log('deselectRow - record id :' + r.id);
27478         if(!preventViewNotify){
27479         
27480             var proxy = new Roo.Element(
27481                 this.grid.getRowDom(index)
27482             );
27483             proxy.removeClass('bg-info info');
27484         }
27485         this.fireEvent("rowdeselect", this, index);
27486         this.fireEvent("selectionchange", this);
27487     },
27488
27489     // private
27490     restoreLast : function(){
27491         if(this._last){
27492             this.last = this._last;
27493         }
27494     },
27495
27496     // private
27497     acceptsNav : function(row, col, cm){
27498         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27499     },
27500
27501     // private
27502     onEditorKey : function(field, e){
27503         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27504         if(k == e.TAB){
27505             e.stopEvent();
27506             ed.completeEdit();
27507             if(e.shiftKey){
27508                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27509             }else{
27510                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27511             }
27512         }else if(k == e.ENTER && !e.ctrlKey){
27513             e.stopEvent();
27514             ed.completeEdit();
27515             if(e.shiftKey){
27516                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27517             }else{
27518                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27519             }
27520         }else if(k == e.ESC){
27521             ed.cancelEdit();
27522         }
27523         if(newCell){
27524             g.startEditing(newCell[0], newCell[1]);
27525         }
27526     }
27527 });
27528 /*
27529  * Based on:
27530  * Ext JS Library 1.1.1
27531  * Copyright(c) 2006-2007, Ext JS, LLC.
27532  *
27533  * Originally Released Under LGPL - original licence link has changed is not relivant.
27534  *
27535  * Fork - LGPL
27536  * <script type="text/javascript">
27537  */
27538  
27539 /**
27540  * @class Roo.bootstrap.PagingToolbar
27541  * @extends Roo.bootstrap.NavSimplebar
27542  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27543  * @constructor
27544  * Create a new PagingToolbar
27545  * @param {Object} config The config object
27546  * @param {Roo.data.Store} store
27547  */
27548 Roo.bootstrap.PagingToolbar = function(config)
27549 {
27550     // old args format still supported... - xtype is prefered..
27551         // created from xtype...
27552     
27553     this.ds = config.dataSource;
27554     
27555     if (config.store && !this.ds) {
27556         this.store= Roo.factory(config.store, Roo.data);
27557         this.ds = this.store;
27558         this.ds.xmodule = this.xmodule || false;
27559     }
27560     
27561     this.toolbarItems = [];
27562     if (config.items) {
27563         this.toolbarItems = config.items;
27564     }
27565     
27566     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27567     
27568     this.cursor = 0;
27569     
27570     if (this.ds) { 
27571         this.bind(this.ds);
27572     }
27573     
27574     if (Roo.bootstrap.version == 4) {
27575         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27576     } else {
27577         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27578     }
27579     
27580 };
27581
27582 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27583     /**
27584      * @cfg {Roo.data.Store} dataSource
27585      * The underlying data store providing the paged data
27586      */
27587     /**
27588      * @cfg {String/HTMLElement/Element} container
27589      * container The id or element that will contain the toolbar
27590      */
27591     /**
27592      * @cfg {Boolean} displayInfo
27593      * True to display the displayMsg (defaults to false)
27594      */
27595     /**
27596      * @cfg {Number} pageSize
27597      * The number of records to display per page (defaults to 20)
27598      */
27599     pageSize: 20,
27600     /**
27601      * @cfg {String} displayMsg
27602      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27603      */
27604     displayMsg : 'Displaying {0} - {1} of {2}',
27605     /**
27606      * @cfg {String} emptyMsg
27607      * The message to display when no records are found (defaults to "No data to display")
27608      */
27609     emptyMsg : 'No data to display',
27610     /**
27611      * Customizable piece of the default paging text (defaults to "Page")
27612      * @type String
27613      */
27614     beforePageText : "Page",
27615     /**
27616      * Customizable piece of the default paging text (defaults to "of %0")
27617      * @type String
27618      */
27619     afterPageText : "of {0}",
27620     /**
27621      * Customizable piece of the default paging text (defaults to "First Page")
27622      * @type String
27623      */
27624     firstText : "First Page",
27625     /**
27626      * Customizable piece of the default paging text (defaults to "Previous Page")
27627      * @type String
27628      */
27629     prevText : "Previous Page",
27630     /**
27631      * Customizable piece of the default paging text (defaults to "Next Page")
27632      * @type String
27633      */
27634     nextText : "Next Page",
27635     /**
27636      * Customizable piece of the default paging text (defaults to "Last Page")
27637      * @type String
27638      */
27639     lastText : "Last Page",
27640     /**
27641      * Customizable piece of the default paging text (defaults to "Refresh")
27642      * @type String
27643      */
27644     refreshText : "Refresh",
27645
27646     buttons : false,
27647     // private
27648     onRender : function(ct, position) 
27649     {
27650         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27651         this.navgroup.parentId = this.id;
27652         this.navgroup.onRender(this.el, null);
27653         // add the buttons to the navgroup
27654         
27655         if(this.displayInfo){
27656             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27657             this.displayEl = this.el.select('.x-paging-info', true).first();
27658 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27659 //            this.displayEl = navel.el.select('span',true).first();
27660         }
27661         
27662         var _this = this;
27663         
27664         if(this.buttons){
27665             Roo.each(_this.buttons, function(e){ // this might need to use render????
27666                Roo.factory(e).render(_this.el);
27667             });
27668         }
27669             
27670         Roo.each(_this.toolbarItems, function(e) {
27671             _this.navgroup.addItem(e);
27672         });
27673         
27674         
27675         this.first = this.navgroup.addItem({
27676             tooltip: this.firstText,
27677             cls: "prev btn-outline-secondary",
27678             html : ' <i class="fa fa-step-backward"></i>',
27679             disabled: true,
27680             preventDefault: true,
27681             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27682         });
27683         
27684         this.prev =  this.navgroup.addItem({
27685             tooltip: this.prevText,
27686             cls: "prev btn-outline-secondary",
27687             html : ' <i class="fa fa-backward"></i>',
27688             disabled: true,
27689             preventDefault: true,
27690             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27691         });
27692     //this.addSeparator();
27693         
27694         
27695         var field = this.navgroup.addItem( {
27696             tagtype : 'span',
27697             cls : 'x-paging-position  btn-outline-secondary',
27698              disabled: true,
27699             html : this.beforePageText  +
27700                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27701                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27702          } ); //?? escaped?
27703         
27704         this.field = field.el.select('input', true).first();
27705         this.field.on("keydown", this.onPagingKeydown, this);
27706         this.field.on("focus", function(){this.dom.select();});
27707     
27708     
27709         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27710         //this.field.setHeight(18);
27711         //this.addSeparator();
27712         this.next = this.navgroup.addItem({
27713             tooltip: this.nextText,
27714             cls: "next btn-outline-secondary",
27715             html : ' <i class="fa fa-forward"></i>',
27716             disabled: true,
27717             preventDefault: true,
27718             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27719         });
27720         this.last = this.navgroup.addItem({
27721             tooltip: this.lastText,
27722             html : ' <i class="fa fa-step-forward"></i>',
27723             cls: "next btn-outline-secondary",
27724             disabled: true,
27725             preventDefault: true,
27726             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27727         });
27728     //this.addSeparator();
27729         this.loading = this.navgroup.addItem({
27730             tooltip: this.refreshText,
27731             cls: "btn-outline-secondary",
27732             html : ' <i class="fa fa-refresh"></i>',
27733             preventDefault: true,
27734             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27735         });
27736         
27737     },
27738
27739     // private
27740     updateInfo : function(){
27741         if(this.displayEl){
27742             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27743             var msg = count == 0 ?
27744                 this.emptyMsg :
27745                 String.format(
27746                     this.displayMsg,
27747                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27748                 );
27749             this.displayEl.update(msg);
27750         }
27751     },
27752
27753     // private
27754     onLoad : function(ds, r, o)
27755     {
27756         this.cursor = o.params && o.params.start ? o.params.start : 0;
27757         
27758         var d = this.getPageData(),
27759             ap = d.activePage,
27760             ps = d.pages;
27761         
27762         
27763         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27764         this.field.dom.value = ap;
27765         this.first.setDisabled(ap == 1);
27766         this.prev.setDisabled(ap == 1);
27767         this.next.setDisabled(ap == ps);
27768         this.last.setDisabled(ap == ps);
27769         this.loading.enable();
27770         this.updateInfo();
27771     },
27772
27773     // private
27774     getPageData : function(){
27775         var total = this.ds.getTotalCount();
27776         return {
27777             total : total,
27778             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27779             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27780         };
27781     },
27782
27783     // private
27784     onLoadError : function(){
27785         this.loading.enable();
27786     },
27787
27788     // private
27789     onPagingKeydown : function(e){
27790         var k = e.getKey();
27791         var d = this.getPageData();
27792         if(k == e.RETURN){
27793             var v = this.field.dom.value, pageNum;
27794             if(!v || isNaN(pageNum = parseInt(v, 10))){
27795                 this.field.dom.value = d.activePage;
27796                 return;
27797             }
27798             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27799             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27800             e.stopEvent();
27801         }
27802         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))
27803         {
27804           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27805           this.field.dom.value = pageNum;
27806           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27807           e.stopEvent();
27808         }
27809         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27810         {
27811           var v = this.field.dom.value, pageNum; 
27812           var increment = (e.shiftKey) ? 10 : 1;
27813           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27814                 increment *= -1;
27815           }
27816           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27817             this.field.dom.value = d.activePage;
27818             return;
27819           }
27820           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27821           {
27822             this.field.dom.value = parseInt(v, 10) + increment;
27823             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27824             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27825           }
27826           e.stopEvent();
27827         }
27828     },
27829
27830     // private
27831     beforeLoad : function(){
27832         if(this.loading){
27833             this.loading.disable();
27834         }
27835     },
27836
27837     // private
27838     onClick : function(which){
27839         
27840         var ds = this.ds;
27841         if (!ds) {
27842             return;
27843         }
27844         
27845         switch(which){
27846             case "first":
27847                 ds.load({params:{start: 0, limit: this.pageSize}});
27848             break;
27849             case "prev":
27850                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27851             break;
27852             case "next":
27853                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27854             break;
27855             case "last":
27856                 var total = ds.getTotalCount();
27857                 var extra = total % this.pageSize;
27858                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27859                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27860             break;
27861             case "refresh":
27862                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27863             break;
27864         }
27865     },
27866
27867     /**
27868      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27869      * @param {Roo.data.Store} store The data store to unbind
27870      */
27871     unbind : function(ds){
27872         ds.un("beforeload", this.beforeLoad, this);
27873         ds.un("load", this.onLoad, this);
27874         ds.un("loadexception", this.onLoadError, this);
27875         ds.un("remove", this.updateInfo, this);
27876         ds.un("add", this.updateInfo, this);
27877         this.ds = undefined;
27878     },
27879
27880     /**
27881      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27882      * @param {Roo.data.Store} store The data store to bind
27883      */
27884     bind : function(ds){
27885         ds.on("beforeload", this.beforeLoad, this);
27886         ds.on("load", this.onLoad, this);
27887         ds.on("loadexception", this.onLoadError, this);
27888         ds.on("remove", this.updateInfo, this);
27889         ds.on("add", this.updateInfo, this);
27890         this.ds = ds;
27891     }
27892 });/*
27893  * - LGPL
27894  *
27895  * element
27896  * 
27897  */
27898
27899 /**
27900  * @class Roo.bootstrap.MessageBar
27901  * @extends Roo.bootstrap.Component
27902  * Bootstrap MessageBar class
27903  * @cfg {String} html contents of the MessageBar
27904  * @cfg {String} weight (info | success | warning | danger) default info
27905  * @cfg {String} beforeClass insert the bar before the given class
27906  * @cfg {Boolean} closable (true | false) default false
27907  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27908  * 
27909  * @constructor
27910  * Create a new Element
27911  * @param {Object} config The config object
27912  */
27913
27914 Roo.bootstrap.MessageBar = function(config){
27915     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27916 };
27917
27918 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27919     
27920     html: '',
27921     weight: 'info',
27922     closable: false,
27923     fixed: false,
27924     beforeClass: 'bootstrap-sticky-wrap',
27925     
27926     getAutoCreate : function(){
27927         
27928         var cfg = {
27929             tag: 'div',
27930             cls: 'alert alert-dismissable alert-' + this.weight,
27931             cn: [
27932                 {
27933                     tag: 'span',
27934                     cls: 'message',
27935                     html: this.html || ''
27936                 }
27937             ]
27938         };
27939         
27940         if(this.fixed){
27941             cfg.cls += ' alert-messages-fixed';
27942         }
27943         
27944         if(this.closable){
27945             cfg.cn.push({
27946                 tag: 'button',
27947                 cls: 'close',
27948                 html: 'x'
27949             });
27950         }
27951         
27952         return cfg;
27953     },
27954     
27955     onRender : function(ct, position)
27956     {
27957         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27958         
27959         if(!this.el){
27960             var cfg = Roo.apply({},  this.getAutoCreate());
27961             cfg.id = Roo.id();
27962             
27963             if (this.cls) {
27964                 cfg.cls += ' ' + this.cls;
27965             }
27966             if (this.style) {
27967                 cfg.style = this.style;
27968             }
27969             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27970             
27971             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27972         }
27973         
27974         this.el.select('>button.close').on('click', this.hide, this);
27975         
27976     },
27977     
27978     show : function()
27979     {
27980         if (!this.rendered) {
27981             this.render();
27982         }
27983         
27984         this.el.show();
27985         
27986         this.fireEvent('show', this);
27987         
27988     },
27989     
27990     hide : function()
27991     {
27992         if (!this.rendered) {
27993             this.render();
27994         }
27995         
27996         this.el.hide();
27997         
27998         this.fireEvent('hide', this);
27999     },
28000     
28001     update : function()
28002     {
28003 //        var e = this.el.dom.firstChild;
28004 //        
28005 //        if(this.closable){
28006 //            e = e.nextSibling;
28007 //        }
28008 //        
28009 //        e.data = this.html || '';
28010
28011         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28012     }
28013    
28014 });
28015
28016  
28017
28018      /*
28019  * - LGPL
28020  *
28021  * Graph
28022  * 
28023  */
28024
28025
28026 /**
28027  * @class Roo.bootstrap.Graph
28028  * @extends Roo.bootstrap.Component
28029  * Bootstrap Graph class
28030 > Prameters
28031  -sm {number} sm 4
28032  -md {number} md 5
28033  @cfg {String} graphtype  bar | vbar | pie
28034  @cfg {number} g_x coodinator | centre x (pie)
28035  @cfg {number} g_y coodinator | centre y (pie)
28036  @cfg {number} g_r radius (pie)
28037  @cfg {number} g_height height of the chart (respected by all elements in the set)
28038  @cfg {number} g_width width of the chart (respected by all elements in the set)
28039  @cfg {Object} title The title of the chart
28040     
28041  -{Array}  values
28042  -opts (object) options for the chart 
28043      o {
28044      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28045      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28046      o vgutter (number)
28047      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.
28048      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28049      o to
28050      o stretch (boolean)
28051      o }
28052  -opts (object) options for the pie
28053      o{
28054      o cut
28055      o startAngle (number)
28056      o endAngle (number)
28057      } 
28058  *
28059  * @constructor
28060  * Create a new Input
28061  * @param {Object} config The config object
28062  */
28063
28064 Roo.bootstrap.Graph = function(config){
28065     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28066     
28067     this.addEvents({
28068         // img events
28069         /**
28070          * @event click
28071          * The img click event for the img.
28072          * @param {Roo.EventObject} e
28073          */
28074         "click" : true
28075     });
28076 };
28077
28078 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28079     
28080     sm: 4,
28081     md: 5,
28082     graphtype: 'bar',
28083     g_height: 250,
28084     g_width: 400,
28085     g_x: 50,
28086     g_y: 50,
28087     g_r: 30,
28088     opts:{
28089         //g_colors: this.colors,
28090         g_type: 'soft',
28091         g_gutter: '20%'
28092
28093     },
28094     title : false,
28095
28096     getAutoCreate : function(){
28097         
28098         var cfg = {
28099             tag: 'div',
28100             html : null
28101         };
28102         
28103         
28104         return  cfg;
28105     },
28106
28107     onRender : function(ct,position){
28108         
28109         
28110         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28111         
28112         if (typeof(Raphael) == 'undefined') {
28113             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28114             return;
28115         }
28116         
28117         this.raphael = Raphael(this.el.dom);
28118         
28119                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28120                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28121                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28122                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28123                 /*
28124                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28125                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28126                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28127                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28128                 
28129                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28130                 r.barchart(330, 10, 300, 220, data1);
28131                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28132                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28133                 */
28134                 
28135                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28136                 // r.barchart(30, 30, 560, 250,  xdata, {
28137                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28138                 //     axis : "0 0 1 1",
28139                 //     axisxlabels :  xdata
28140                 //     //yvalues : cols,
28141                    
28142                 // });
28143 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28144 //        
28145 //        this.load(null,xdata,{
28146 //                axis : "0 0 1 1",
28147 //                axisxlabels :  xdata
28148 //                });
28149
28150     },
28151
28152     load : function(graphtype,xdata,opts)
28153     {
28154         this.raphael.clear();
28155         if(!graphtype) {
28156             graphtype = this.graphtype;
28157         }
28158         if(!opts){
28159             opts = this.opts;
28160         }
28161         var r = this.raphael,
28162             fin = function () {
28163                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28164             },
28165             fout = function () {
28166                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28167             },
28168             pfin = function() {
28169                 this.sector.stop();
28170                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28171
28172                 if (this.label) {
28173                     this.label[0].stop();
28174                     this.label[0].attr({ r: 7.5 });
28175                     this.label[1].attr({ "font-weight": 800 });
28176                 }
28177             },
28178             pfout = function() {
28179                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28180
28181                 if (this.label) {
28182                     this.label[0].animate({ r: 5 }, 500, "bounce");
28183                     this.label[1].attr({ "font-weight": 400 });
28184                 }
28185             };
28186
28187         switch(graphtype){
28188             case 'bar':
28189                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28190                 break;
28191             case 'hbar':
28192                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28193                 break;
28194             case 'pie':
28195 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28196 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28197 //            
28198                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28199                 
28200                 break;
28201
28202         }
28203         
28204         if(this.title){
28205             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28206         }
28207         
28208     },
28209     
28210     setTitle: function(o)
28211     {
28212         this.title = o;
28213     },
28214     
28215     initEvents: function() {
28216         
28217         if(!this.href){
28218             this.el.on('click', this.onClick, this);
28219         }
28220     },
28221     
28222     onClick : function(e)
28223     {
28224         Roo.log('img onclick');
28225         this.fireEvent('click', this, e);
28226     }
28227    
28228 });
28229
28230  
28231 /*
28232  * - LGPL
28233  *
28234  * numberBox
28235  * 
28236  */
28237 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28238
28239 /**
28240  * @class Roo.bootstrap.dash.NumberBox
28241  * @extends Roo.bootstrap.Component
28242  * Bootstrap NumberBox class
28243  * @cfg {String} headline Box headline
28244  * @cfg {String} content Box content
28245  * @cfg {String} icon Box icon
28246  * @cfg {String} footer Footer text
28247  * @cfg {String} fhref Footer href
28248  * 
28249  * @constructor
28250  * Create a new NumberBox
28251  * @param {Object} config The config object
28252  */
28253
28254
28255 Roo.bootstrap.dash.NumberBox = function(config){
28256     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28257     
28258 };
28259
28260 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28261     
28262     headline : '',
28263     content : '',
28264     icon : '',
28265     footer : '',
28266     fhref : '',
28267     ficon : '',
28268     
28269     getAutoCreate : function(){
28270         
28271         var cfg = {
28272             tag : 'div',
28273             cls : 'small-box ',
28274             cn : [
28275                 {
28276                     tag : 'div',
28277                     cls : 'inner',
28278                     cn :[
28279                         {
28280                             tag : 'h3',
28281                             cls : 'roo-headline',
28282                             html : this.headline
28283                         },
28284                         {
28285                             tag : 'p',
28286                             cls : 'roo-content',
28287                             html : this.content
28288                         }
28289                     ]
28290                 }
28291             ]
28292         };
28293         
28294         if(this.icon){
28295             cfg.cn.push({
28296                 tag : 'div',
28297                 cls : 'icon',
28298                 cn :[
28299                     {
28300                         tag : 'i',
28301                         cls : 'ion ' + this.icon
28302                     }
28303                 ]
28304             });
28305         }
28306         
28307         if(this.footer){
28308             var footer = {
28309                 tag : 'a',
28310                 cls : 'small-box-footer',
28311                 href : this.fhref || '#',
28312                 html : this.footer
28313             };
28314             
28315             cfg.cn.push(footer);
28316             
28317         }
28318         
28319         return  cfg;
28320     },
28321
28322     onRender : function(ct,position){
28323         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28324
28325
28326        
28327                 
28328     },
28329
28330     setHeadline: function (value)
28331     {
28332         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28333     },
28334     
28335     setFooter: function (value, href)
28336     {
28337         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28338         
28339         if(href){
28340             this.el.select('a.small-box-footer',true).first().attr('href', href);
28341         }
28342         
28343     },
28344
28345     setContent: function (value)
28346     {
28347         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28348     },
28349
28350     initEvents: function() 
28351     {   
28352         
28353     }
28354     
28355 });
28356
28357  
28358 /*
28359  * - LGPL
28360  *
28361  * TabBox
28362  * 
28363  */
28364 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28365
28366 /**
28367  * @class Roo.bootstrap.dash.TabBox
28368  * @extends Roo.bootstrap.Component
28369  * Bootstrap TabBox class
28370  * @cfg {String} title Title of the TabBox
28371  * @cfg {String} icon Icon of the TabBox
28372  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28373  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28374  * 
28375  * @constructor
28376  * Create a new TabBox
28377  * @param {Object} config The config object
28378  */
28379
28380
28381 Roo.bootstrap.dash.TabBox = function(config){
28382     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28383     this.addEvents({
28384         // raw events
28385         /**
28386          * @event addpane
28387          * When a pane is added
28388          * @param {Roo.bootstrap.dash.TabPane} pane
28389          */
28390         "addpane" : true,
28391         /**
28392          * @event activatepane
28393          * When a pane is activated
28394          * @param {Roo.bootstrap.dash.TabPane} pane
28395          */
28396         "activatepane" : true
28397         
28398          
28399     });
28400     
28401     this.panes = [];
28402 };
28403
28404 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28405
28406     title : '',
28407     icon : false,
28408     showtabs : true,
28409     tabScrollable : false,
28410     
28411     getChildContainer : function()
28412     {
28413         return this.el.select('.tab-content', true).first();
28414     },
28415     
28416     getAutoCreate : function(){
28417         
28418         var header = {
28419             tag: 'li',
28420             cls: 'pull-left header',
28421             html: this.title,
28422             cn : []
28423         };
28424         
28425         if(this.icon){
28426             header.cn.push({
28427                 tag: 'i',
28428                 cls: 'fa ' + this.icon
28429             });
28430         }
28431         
28432         var h = {
28433             tag: 'ul',
28434             cls: 'nav nav-tabs pull-right',
28435             cn: [
28436                 header
28437             ]
28438         };
28439         
28440         if(this.tabScrollable){
28441             h = {
28442                 tag: 'div',
28443                 cls: 'tab-header',
28444                 cn: [
28445                     {
28446                         tag: 'ul',
28447                         cls: 'nav nav-tabs pull-right',
28448                         cn: [
28449                             header
28450                         ]
28451                     }
28452                 ]
28453             };
28454         }
28455         
28456         var cfg = {
28457             tag: 'div',
28458             cls: 'nav-tabs-custom',
28459             cn: [
28460                 h,
28461                 {
28462                     tag: 'div',
28463                     cls: 'tab-content no-padding',
28464                     cn: []
28465                 }
28466             ]
28467         };
28468
28469         return  cfg;
28470     },
28471     initEvents : function()
28472     {
28473         //Roo.log('add add pane handler');
28474         this.on('addpane', this.onAddPane, this);
28475     },
28476      /**
28477      * Updates the box title
28478      * @param {String} html to set the title to.
28479      */
28480     setTitle : function(value)
28481     {
28482         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28483     },
28484     onAddPane : function(pane)
28485     {
28486         this.panes.push(pane);
28487         //Roo.log('addpane');
28488         //Roo.log(pane);
28489         // tabs are rendere left to right..
28490         if(!this.showtabs){
28491             return;
28492         }
28493         
28494         var ctr = this.el.select('.nav-tabs', true).first();
28495          
28496          
28497         var existing = ctr.select('.nav-tab',true);
28498         var qty = existing.getCount();;
28499         
28500         
28501         var tab = ctr.createChild({
28502             tag : 'li',
28503             cls : 'nav-tab' + (qty ? '' : ' active'),
28504             cn : [
28505                 {
28506                     tag : 'a',
28507                     href:'#',
28508                     html : pane.title
28509                 }
28510             ]
28511         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28512         pane.tab = tab;
28513         
28514         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28515         if (!qty) {
28516             pane.el.addClass('active');
28517         }
28518         
28519                 
28520     },
28521     onTabClick : function(ev,un,ob,pane)
28522     {
28523         //Roo.log('tab - prev default');
28524         ev.preventDefault();
28525         
28526         
28527         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28528         pane.tab.addClass('active');
28529         //Roo.log(pane.title);
28530         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28531         // technically we should have a deactivate event.. but maybe add later.
28532         // and it should not de-activate the selected tab...
28533         this.fireEvent('activatepane', pane);
28534         pane.el.addClass('active');
28535         pane.fireEvent('activate');
28536         
28537         
28538     },
28539     
28540     getActivePane : function()
28541     {
28542         var r = false;
28543         Roo.each(this.panes, function(p) {
28544             if(p.el.hasClass('active')){
28545                 r = p;
28546                 return false;
28547             }
28548             
28549             return;
28550         });
28551         
28552         return r;
28553     }
28554     
28555     
28556 });
28557
28558  
28559 /*
28560  * - LGPL
28561  *
28562  * Tab pane
28563  * 
28564  */
28565 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28566 /**
28567  * @class Roo.bootstrap.TabPane
28568  * @extends Roo.bootstrap.Component
28569  * Bootstrap TabPane class
28570  * @cfg {Boolean} active (false | true) Default false
28571  * @cfg {String} title title of panel
28572
28573  * 
28574  * @constructor
28575  * Create a new TabPane
28576  * @param {Object} config The config object
28577  */
28578
28579 Roo.bootstrap.dash.TabPane = function(config){
28580     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28581     
28582     this.addEvents({
28583         // raw events
28584         /**
28585          * @event activate
28586          * When a pane is activated
28587          * @param {Roo.bootstrap.dash.TabPane} pane
28588          */
28589         "activate" : true
28590          
28591     });
28592 };
28593
28594 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28595     
28596     active : false,
28597     title : '',
28598     
28599     // the tabBox that this is attached to.
28600     tab : false,
28601      
28602     getAutoCreate : function() 
28603     {
28604         var cfg = {
28605             tag: 'div',
28606             cls: 'tab-pane'
28607         };
28608         
28609         if(this.active){
28610             cfg.cls += ' active';
28611         }
28612         
28613         return cfg;
28614     },
28615     initEvents  : function()
28616     {
28617         //Roo.log('trigger add pane handler');
28618         this.parent().fireEvent('addpane', this)
28619     },
28620     
28621      /**
28622      * Updates the tab title 
28623      * @param {String} html to set the title to.
28624      */
28625     setTitle: function(str)
28626     {
28627         if (!this.tab) {
28628             return;
28629         }
28630         this.title = str;
28631         this.tab.select('a', true).first().dom.innerHTML = str;
28632         
28633     }
28634     
28635     
28636     
28637 });
28638
28639  
28640
28641
28642  /*
28643  * - LGPL
28644  *
28645  * menu
28646  * 
28647  */
28648 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28649
28650 /**
28651  * @class Roo.bootstrap.menu.Menu
28652  * @extends Roo.bootstrap.Component
28653  * Bootstrap Menu class - container for Menu
28654  * @cfg {String} html Text of the menu
28655  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28656  * @cfg {String} icon Font awesome icon
28657  * @cfg {String} pos Menu align to (top | bottom) default bottom
28658  * 
28659  * 
28660  * @constructor
28661  * Create a new Menu
28662  * @param {Object} config The config object
28663  */
28664
28665
28666 Roo.bootstrap.menu.Menu = function(config){
28667     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28668     
28669     this.addEvents({
28670         /**
28671          * @event beforeshow
28672          * Fires before this menu is displayed
28673          * @param {Roo.bootstrap.menu.Menu} this
28674          */
28675         beforeshow : true,
28676         /**
28677          * @event beforehide
28678          * Fires before this menu is hidden
28679          * @param {Roo.bootstrap.menu.Menu} this
28680          */
28681         beforehide : true,
28682         /**
28683          * @event show
28684          * Fires after this menu is displayed
28685          * @param {Roo.bootstrap.menu.Menu} this
28686          */
28687         show : true,
28688         /**
28689          * @event hide
28690          * Fires after this menu is hidden
28691          * @param {Roo.bootstrap.menu.Menu} this
28692          */
28693         hide : true,
28694         /**
28695          * @event click
28696          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28697          * @param {Roo.bootstrap.menu.Menu} this
28698          * @param {Roo.EventObject} e
28699          */
28700         click : true
28701     });
28702     
28703 };
28704
28705 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28706     
28707     submenu : false,
28708     html : '',
28709     weight : 'default',
28710     icon : false,
28711     pos : 'bottom',
28712     
28713     
28714     getChildContainer : function() {
28715         if(this.isSubMenu){
28716             return this.el;
28717         }
28718         
28719         return this.el.select('ul.dropdown-menu', true).first();  
28720     },
28721     
28722     getAutoCreate : function()
28723     {
28724         var text = [
28725             {
28726                 tag : 'span',
28727                 cls : 'roo-menu-text',
28728                 html : this.html
28729             }
28730         ];
28731         
28732         if(this.icon){
28733             text.unshift({
28734                 tag : 'i',
28735                 cls : 'fa ' + this.icon
28736             })
28737         }
28738         
28739         
28740         var cfg = {
28741             tag : 'div',
28742             cls : 'btn-group',
28743             cn : [
28744                 {
28745                     tag : 'button',
28746                     cls : 'dropdown-button btn btn-' + this.weight,
28747                     cn : text
28748                 },
28749                 {
28750                     tag : 'button',
28751                     cls : 'dropdown-toggle btn btn-' + this.weight,
28752                     cn : [
28753                         {
28754                             tag : 'span',
28755                             cls : 'caret'
28756                         }
28757                     ]
28758                 },
28759                 {
28760                     tag : 'ul',
28761                     cls : 'dropdown-menu'
28762                 }
28763             ]
28764             
28765         };
28766         
28767         if(this.pos == 'top'){
28768             cfg.cls += ' dropup';
28769         }
28770         
28771         if(this.isSubMenu){
28772             cfg = {
28773                 tag : 'ul',
28774                 cls : 'dropdown-menu'
28775             }
28776         }
28777         
28778         return cfg;
28779     },
28780     
28781     onRender : function(ct, position)
28782     {
28783         this.isSubMenu = ct.hasClass('dropdown-submenu');
28784         
28785         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28786     },
28787     
28788     initEvents : function() 
28789     {
28790         if(this.isSubMenu){
28791             return;
28792         }
28793         
28794         this.hidden = true;
28795         
28796         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28797         this.triggerEl.on('click', this.onTriggerPress, this);
28798         
28799         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28800         this.buttonEl.on('click', this.onClick, this);
28801         
28802     },
28803     
28804     list : function()
28805     {
28806         if(this.isSubMenu){
28807             return this.el;
28808         }
28809         
28810         return this.el.select('ul.dropdown-menu', true).first();
28811     },
28812     
28813     onClick : function(e)
28814     {
28815         this.fireEvent("click", this, e);
28816     },
28817     
28818     onTriggerPress  : function(e)
28819     {   
28820         if (this.isVisible()) {
28821             this.hide();
28822         } else {
28823             this.show();
28824         }
28825     },
28826     
28827     isVisible : function(){
28828         return !this.hidden;
28829     },
28830     
28831     show : function()
28832     {
28833         this.fireEvent("beforeshow", this);
28834         
28835         this.hidden = false;
28836         this.el.addClass('open');
28837         
28838         Roo.get(document).on("mouseup", this.onMouseUp, this);
28839         
28840         this.fireEvent("show", this);
28841         
28842         
28843     },
28844     
28845     hide : function()
28846     {
28847         this.fireEvent("beforehide", this);
28848         
28849         this.hidden = true;
28850         this.el.removeClass('open');
28851         
28852         Roo.get(document).un("mouseup", this.onMouseUp);
28853         
28854         this.fireEvent("hide", this);
28855     },
28856     
28857     onMouseUp : function()
28858     {
28859         this.hide();
28860     }
28861     
28862 });
28863
28864  
28865  /*
28866  * - LGPL
28867  *
28868  * menu item
28869  * 
28870  */
28871 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28872
28873 /**
28874  * @class Roo.bootstrap.menu.Item
28875  * @extends Roo.bootstrap.Component
28876  * Bootstrap MenuItem class
28877  * @cfg {Boolean} submenu (true | false) default false
28878  * @cfg {String} html text of the item
28879  * @cfg {String} href the link
28880  * @cfg {Boolean} disable (true | false) default false
28881  * @cfg {Boolean} preventDefault (true | false) default true
28882  * @cfg {String} icon Font awesome icon
28883  * @cfg {String} pos Submenu align to (left | right) default right 
28884  * 
28885  * 
28886  * @constructor
28887  * Create a new Item
28888  * @param {Object} config The config object
28889  */
28890
28891
28892 Roo.bootstrap.menu.Item = function(config){
28893     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28894     this.addEvents({
28895         /**
28896          * @event mouseover
28897          * Fires when the mouse is hovering over this menu
28898          * @param {Roo.bootstrap.menu.Item} this
28899          * @param {Roo.EventObject} e
28900          */
28901         mouseover : true,
28902         /**
28903          * @event mouseout
28904          * Fires when the mouse exits this menu
28905          * @param {Roo.bootstrap.menu.Item} this
28906          * @param {Roo.EventObject} e
28907          */
28908         mouseout : true,
28909         // raw events
28910         /**
28911          * @event click
28912          * The raw click event for the entire grid.
28913          * @param {Roo.EventObject} e
28914          */
28915         click : true
28916     });
28917 };
28918
28919 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28920     
28921     submenu : false,
28922     href : '',
28923     html : '',
28924     preventDefault: true,
28925     disable : false,
28926     icon : false,
28927     pos : 'right',
28928     
28929     getAutoCreate : function()
28930     {
28931         var text = [
28932             {
28933                 tag : 'span',
28934                 cls : 'roo-menu-item-text',
28935                 html : this.html
28936             }
28937         ];
28938         
28939         if(this.icon){
28940             text.unshift({
28941                 tag : 'i',
28942                 cls : 'fa ' + this.icon
28943             })
28944         }
28945         
28946         var cfg = {
28947             tag : 'li',
28948             cn : [
28949                 {
28950                     tag : 'a',
28951                     href : this.href || '#',
28952                     cn : text
28953                 }
28954             ]
28955         };
28956         
28957         if(this.disable){
28958             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28959         }
28960         
28961         if(this.submenu){
28962             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28963             
28964             if(this.pos == 'left'){
28965                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28966             }
28967         }
28968         
28969         return cfg;
28970     },
28971     
28972     initEvents : function() 
28973     {
28974         this.el.on('mouseover', this.onMouseOver, this);
28975         this.el.on('mouseout', this.onMouseOut, this);
28976         
28977         this.el.select('a', true).first().on('click', this.onClick, this);
28978         
28979     },
28980     
28981     onClick : function(e)
28982     {
28983         if(this.preventDefault){
28984             e.preventDefault();
28985         }
28986         
28987         this.fireEvent("click", this, e);
28988     },
28989     
28990     onMouseOver : function(e)
28991     {
28992         if(this.submenu && this.pos == 'left'){
28993             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28994         }
28995         
28996         this.fireEvent("mouseover", this, e);
28997     },
28998     
28999     onMouseOut : function(e)
29000     {
29001         this.fireEvent("mouseout", this, e);
29002     }
29003 });
29004
29005  
29006
29007  /*
29008  * - LGPL
29009  *
29010  * menu separator
29011  * 
29012  */
29013 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29014
29015 /**
29016  * @class Roo.bootstrap.menu.Separator
29017  * @extends Roo.bootstrap.Component
29018  * Bootstrap Separator class
29019  * 
29020  * @constructor
29021  * Create a new Separator
29022  * @param {Object} config The config object
29023  */
29024
29025
29026 Roo.bootstrap.menu.Separator = function(config){
29027     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29028 };
29029
29030 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29031     
29032     getAutoCreate : function(){
29033         var cfg = {
29034             tag : 'li',
29035             cls: 'dropdown-divider divider'
29036         };
29037         
29038         return cfg;
29039     }
29040    
29041 });
29042
29043  
29044
29045  /*
29046  * - LGPL
29047  *
29048  * Tooltip
29049  * 
29050  */
29051
29052 /**
29053  * @class Roo.bootstrap.Tooltip
29054  * Bootstrap Tooltip class
29055  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29056  * to determine which dom element triggers the tooltip.
29057  * 
29058  * It needs to add support for additional attributes like tooltip-position
29059  * 
29060  * @constructor
29061  * Create a new Toolti
29062  * @param {Object} config The config object
29063  */
29064
29065 Roo.bootstrap.Tooltip = function(config){
29066     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29067     
29068     this.alignment = Roo.bootstrap.Tooltip.alignment;
29069     
29070     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29071         this.alignment = config.alignment;
29072     }
29073     
29074 };
29075
29076 Roo.apply(Roo.bootstrap.Tooltip, {
29077     /**
29078      * @function init initialize tooltip monitoring.
29079      * @static
29080      */
29081     currentEl : false,
29082     currentTip : false,
29083     currentRegion : false,
29084     
29085     //  init : delay?
29086     
29087     init : function()
29088     {
29089         Roo.get(document).on('mouseover', this.enter ,this);
29090         Roo.get(document).on('mouseout', this.leave, this);
29091          
29092         
29093         this.currentTip = new Roo.bootstrap.Tooltip();
29094     },
29095     
29096     enter : function(ev)
29097     {
29098         var dom = ev.getTarget();
29099         
29100         //Roo.log(['enter',dom]);
29101         var el = Roo.fly(dom);
29102         if (this.currentEl) {
29103             //Roo.log(dom);
29104             //Roo.log(this.currentEl);
29105             //Roo.log(this.currentEl.contains(dom));
29106             if (this.currentEl == el) {
29107                 return;
29108             }
29109             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29110                 return;
29111             }
29112
29113         }
29114         
29115         if (this.currentTip.el) {
29116             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29117         }    
29118         //Roo.log(ev);
29119         
29120         if(!el || el.dom == document){
29121             return;
29122         }
29123         
29124         var bindEl = el; 
29125         var pel = false;
29126         if (!el.attr('tooltip')) {
29127             pel = el.findParent("[tooltip]");
29128             if (pel) {
29129                 bindEl = Roo.get(pel);
29130             }
29131         }
29132         
29133        
29134         
29135         // you can not look for children, as if el is the body.. then everythign is the child..
29136         if (!pel && !el.attr('tooltip')) { //
29137             if (!el.select("[tooltip]").elements.length) {
29138                 return;
29139             }
29140             // is the mouse over this child...?
29141             bindEl = el.select("[tooltip]").first();
29142             var xy = ev.getXY();
29143             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29144                 //Roo.log("not in region.");
29145                 return;
29146             }
29147             //Roo.log("child element over..");
29148             
29149         }
29150         this.currentEl = el;
29151         this.currentTip.bind(bindEl);
29152         this.currentRegion = Roo.lib.Region.getRegion(dom);
29153         this.currentTip.enter();
29154         
29155     },
29156     leave : function(ev)
29157     {
29158         var dom = ev.getTarget();
29159         //Roo.log(['leave',dom]);
29160         if (!this.currentEl) {
29161             return;
29162         }
29163         
29164         
29165         if (dom != this.currentEl.dom) {
29166             return;
29167         }
29168         var xy = ev.getXY();
29169         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29170             return;
29171         }
29172         // only activate leave if mouse cursor is outside... bounding box..
29173         
29174         
29175         
29176         
29177         if (this.currentTip) {
29178             this.currentTip.leave();
29179         }
29180         //Roo.log('clear currentEl');
29181         this.currentEl = false;
29182         
29183         
29184     },
29185     alignment : {
29186         'left' : ['r-l', [-2,0], 'right'],
29187         'right' : ['l-r', [2,0], 'left'],
29188         'bottom' : ['t-b', [0,2], 'top'],
29189         'top' : [ 'b-t', [0,-2], 'bottom']
29190     }
29191     
29192 });
29193
29194
29195 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29196     
29197     
29198     bindEl : false,
29199     
29200     delay : null, // can be { show : 300 , hide: 500}
29201     
29202     timeout : null,
29203     
29204     hoverState : null, //???
29205     
29206     placement : 'bottom', 
29207     
29208     alignment : false,
29209     
29210     getAutoCreate : function(){
29211     
29212         var cfg = {
29213            cls : 'tooltip',   
29214            role : 'tooltip',
29215            cn : [
29216                 {
29217                     cls : 'tooltip-arrow arrow'
29218                 },
29219                 {
29220                     cls : 'tooltip-inner'
29221                 }
29222            ]
29223         };
29224         
29225         return cfg;
29226     },
29227     bind : function(el)
29228     {
29229         this.bindEl = el;
29230     },
29231     
29232     initEvents : function()
29233     {
29234         this.arrowEl = this.el.select('.arrow', true).first();
29235         this.innerEl = this.el.select('.tooltip-inner', true).first();
29236     },
29237     
29238     enter : function () {
29239        
29240         if (this.timeout != null) {
29241             clearTimeout(this.timeout);
29242         }
29243         
29244         this.hoverState = 'in';
29245          //Roo.log("enter - show");
29246         if (!this.delay || !this.delay.show) {
29247             this.show();
29248             return;
29249         }
29250         var _t = this;
29251         this.timeout = setTimeout(function () {
29252             if (_t.hoverState == 'in') {
29253                 _t.show();
29254             }
29255         }, this.delay.show);
29256     },
29257     leave : function()
29258     {
29259         clearTimeout(this.timeout);
29260     
29261         this.hoverState = 'out';
29262          if (!this.delay || !this.delay.hide) {
29263             this.hide();
29264             return;
29265         }
29266        
29267         var _t = this;
29268         this.timeout = setTimeout(function () {
29269             //Roo.log("leave - timeout");
29270             
29271             if (_t.hoverState == 'out') {
29272                 _t.hide();
29273                 Roo.bootstrap.Tooltip.currentEl = false;
29274             }
29275         }, delay);
29276     },
29277     
29278     show : function (msg)
29279     {
29280         if (!this.el) {
29281             this.render(document.body);
29282         }
29283         // set content.
29284         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29285         
29286         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29287         
29288         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29289         
29290         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29291                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29292         
29293         var placement = typeof this.placement == 'function' ?
29294             this.placement.call(this, this.el, on_el) :
29295             this.placement;
29296             
29297         var autoToken = /\s?auto?\s?/i;
29298         var autoPlace = autoToken.test(placement);
29299         if (autoPlace) {
29300             placement = placement.replace(autoToken, '') || 'top';
29301         }
29302         
29303         //this.el.detach()
29304         //this.el.setXY([0,0]);
29305         this.el.show();
29306         //this.el.dom.style.display='block';
29307         
29308         //this.el.appendTo(on_el);
29309         
29310         var p = this.getPosition();
29311         var box = this.el.getBox();
29312         
29313         if (autoPlace) {
29314             // fixme..
29315         }
29316         
29317         var align = this.alignment[placement];
29318         
29319         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29320         
29321         if(placement == 'top' || placement == 'bottom'){
29322             if(xy[0] < 0){
29323                 placement = 'right';
29324             }
29325             
29326             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29327                 placement = 'left';
29328             }
29329             
29330             var scroll = Roo.select('body', true).first().getScroll();
29331             
29332             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29333                 placement = 'top';
29334             }
29335             
29336             align = this.alignment[placement];
29337             
29338             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29339             
29340         }
29341         
29342         var elems = document.getElementsByTagName('div');
29343         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29344         for (var i = 0; i < elems.length; i++) {
29345           var zindex = Number.parseInt(
29346                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29347                 10
29348           );
29349           if (zindex > highest) {
29350             highest = zindex;
29351           }
29352         }
29353         
29354         
29355         
29356         this.el.dom.style.zIndex = highest;
29357         
29358         this.el.alignTo(this.bindEl, align[0],align[1]);
29359         //var arrow = this.el.select('.arrow',true).first();
29360         //arrow.set(align[2], 
29361         
29362         this.el.addClass(placement);
29363         this.el.addClass("bs-tooltip-"+ placement);
29364         
29365         this.el.addClass('in fade show');
29366         
29367         this.hoverState = null;
29368         
29369         if (this.el.hasClass('fade')) {
29370             // fade it?
29371         }
29372         
29373         
29374         
29375         
29376         
29377     },
29378     hide : function()
29379     {
29380          
29381         if (!this.el) {
29382             return;
29383         }
29384         //this.el.setXY([0,0]);
29385         this.el.removeClass(['show', 'in']);
29386         //this.el.hide();
29387         
29388     }
29389     
29390 });
29391  
29392
29393  /*
29394  * - LGPL
29395  *
29396  * Location Picker
29397  * 
29398  */
29399
29400 /**
29401  * @class Roo.bootstrap.LocationPicker
29402  * @extends Roo.bootstrap.Component
29403  * Bootstrap LocationPicker class
29404  * @cfg {Number} latitude Position when init default 0
29405  * @cfg {Number} longitude Position when init default 0
29406  * @cfg {Number} zoom default 15
29407  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29408  * @cfg {Boolean} mapTypeControl default false
29409  * @cfg {Boolean} disableDoubleClickZoom default false
29410  * @cfg {Boolean} scrollwheel default true
29411  * @cfg {Boolean} streetViewControl default false
29412  * @cfg {Number} radius default 0
29413  * @cfg {String} locationName
29414  * @cfg {Boolean} draggable default true
29415  * @cfg {Boolean} enableAutocomplete default false
29416  * @cfg {Boolean} enableReverseGeocode default true
29417  * @cfg {String} markerTitle
29418  * 
29419  * @constructor
29420  * Create a new LocationPicker
29421  * @param {Object} config The config object
29422  */
29423
29424
29425 Roo.bootstrap.LocationPicker = function(config){
29426     
29427     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29428     
29429     this.addEvents({
29430         /**
29431          * @event initial
29432          * Fires when the picker initialized.
29433          * @param {Roo.bootstrap.LocationPicker} this
29434          * @param {Google Location} location
29435          */
29436         initial : true,
29437         /**
29438          * @event positionchanged
29439          * Fires when the picker position changed.
29440          * @param {Roo.bootstrap.LocationPicker} this
29441          * @param {Google Location} location
29442          */
29443         positionchanged : true,
29444         /**
29445          * @event resize
29446          * Fires when the map resize.
29447          * @param {Roo.bootstrap.LocationPicker} this
29448          */
29449         resize : true,
29450         /**
29451          * @event show
29452          * Fires when the map show.
29453          * @param {Roo.bootstrap.LocationPicker} this
29454          */
29455         show : true,
29456         /**
29457          * @event hide
29458          * Fires when the map hide.
29459          * @param {Roo.bootstrap.LocationPicker} this
29460          */
29461         hide : true,
29462         /**
29463          * @event mapClick
29464          * Fires when click the map.
29465          * @param {Roo.bootstrap.LocationPicker} this
29466          * @param {Map event} e
29467          */
29468         mapClick : true,
29469         /**
29470          * @event mapRightClick
29471          * Fires when right click the map.
29472          * @param {Roo.bootstrap.LocationPicker} this
29473          * @param {Map event} e
29474          */
29475         mapRightClick : true,
29476         /**
29477          * @event markerClick
29478          * Fires when click the marker.
29479          * @param {Roo.bootstrap.LocationPicker} this
29480          * @param {Map event} e
29481          */
29482         markerClick : true,
29483         /**
29484          * @event markerRightClick
29485          * Fires when right click the marker.
29486          * @param {Roo.bootstrap.LocationPicker} this
29487          * @param {Map event} e
29488          */
29489         markerRightClick : true,
29490         /**
29491          * @event OverlayViewDraw
29492          * Fires when OverlayView Draw
29493          * @param {Roo.bootstrap.LocationPicker} this
29494          */
29495         OverlayViewDraw : true,
29496         /**
29497          * @event OverlayViewOnAdd
29498          * Fires when OverlayView Draw
29499          * @param {Roo.bootstrap.LocationPicker} this
29500          */
29501         OverlayViewOnAdd : true,
29502         /**
29503          * @event OverlayViewOnRemove
29504          * Fires when OverlayView Draw
29505          * @param {Roo.bootstrap.LocationPicker} this
29506          */
29507         OverlayViewOnRemove : true,
29508         /**
29509          * @event OverlayViewShow
29510          * Fires when OverlayView Draw
29511          * @param {Roo.bootstrap.LocationPicker} this
29512          * @param {Pixel} cpx
29513          */
29514         OverlayViewShow : true,
29515         /**
29516          * @event OverlayViewHide
29517          * Fires when OverlayView Draw
29518          * @param {Roo.bootstrap.LocationPicker} this
29519          */
29520         OverlayViewHide : true,
29521         /**
29522          * @event loadexception
29523          * Fires when load google lib failed.
29524          * @param {Roo.bootstrap.LocationPicker} this
29525          */
29526         loadexception : true
29527     });
29528         
29529 };
29530
29531 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29532     
29533     gMapContext: false,
29534     
29535     latitude: 0,
29536     longitude: 0,
29537     zoom: 15,
29538     mapTypeId: false,
29539     mapTypeControl: false,
29540     disableDoubleClickZoom: false,
29541     scrollwheel: true,
29542     streetViewControl: false,
29543     radius: 0,
29544     locationName: '',
29545     draggable: true,
29546     enableAutocomplete: false,
29547     enableReverseGeocode: true,
29548     markerTitle: '',
29549     
29550     getAutoCreate: function()
29551     {
29552
29553         var cfg = {
29554             tag: 'div',
29555             cls: 'roo-location-picker'
29556         };
29557         
29558         return cfg
29559     },
29560     
29561     initEvents: function(ct, position)
29562     {       
29563         if(!this.el.getWidth() || this.isApplied()){
29564             return;
29565         }
29566         
29567         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29568         
29569         this.initial();
29570     },
29571     
29572     initial: function()
29573     {
29574         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29575             this.fireEvent('loadexception', this);
29576             return;
29577         }
29578         
29579         if(!this.mapTypeId){
29580             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29581         }
29582         
29583         this.gMapContext = this.GMapContext();
29584         
29585         this.initOverlayView();
29586         
29587         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29588         
29589         var _this = this;
29590                 
29591         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29592             _this.setPosition(_this.gMapContext.marker.position);
29593         });
29594         
29595         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29596             _this.fireEvent('mapClick', this, event);
29597             
29598         });
29599
29600         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29601             _this.fireEvent('mapRightClick', this, event);
29602             
29603         });
29604         
29605         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29606             _this.fireEvent('markerClick', this, event);
29607             
29608         });
29609
29610         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29611             _this.fireEvent('markerRightClick', this, event);
29612             
29613         });
29614         
29615         this.setPosition(this.gMapContext.location);
29616         
29617         this.fireEvent('initial', this, this.gMapContext.location);
29618     },
29619     
29620     initOverlayView: function()
29621     {
29622         var _this = this;
29623         
29624         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29625             
29626             draw: function()
29627             {
29628                 _this.fireEvent('OverlayViewDraw', _this);
29629             },
29630             
29631             onAdd: function()
29632             {
29633                 _this.fireEvent('OverlayViewOnAdd', _this);
29634             },
29635             
29636             onRemove: function()
29637             {
29638                 _this.fireEvent('OverlayViewOnRemove', _this);
29639             },
29640             
29641             show: function(cpx)
29642             {
29643                 _this.fireEvent('OverlayViewShow', _this, cpx);
29644             },
29645             
29646             hide: function()
29647             {
29648                 _this.fireEvent('OverlayViewHide', _this);
29649             }
29650             
29651         });
29652     },
29653     
29654     fromLatLngToContainerPixel: function(event)
29655     {
29656         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29657     },
29658     
29659     isApplied: function() 
29660     {
29661         return this.getGmapContext() == false ? false : true;
29662     },
29663     
29664     getGmapContext: function() 
29665     {
29666         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29667     },
29668     
29669     GMapContext: function() 
29670     {
29671         var position = new google.maps.LatLng(this.latitude, this.longitude);
29672         
29673         var _map = new google.maps.Map(this.el.dom, {
29674             center: position,
29675             zoom: this.zoom,
29676             mapTypeId: this.mapTypeId,
29677             mapTypeControl: this.mapTypeControl,
29678             disableDoubleClickZoom: this.disableDoubleClickZoom,
29679             scrollwheel: this.scrollwheel,
29680             streetViewControl: this.streetViewControl,
29681             locationName: this.locationName,
29682             draggable: this.draggable,
29683             enableAutocomplete: this.enableAutocomplete,
29684             enableReverseGeocode: this.enableReverseGeocode
29685         });
29686         
29687         var _marker = new google.maps.Marker({
29688             position: position,
29689             map: _map,
29690             title: this.markerTitle,
29691             draggable: this.draggable
29692         });
29693         
29694         return {
29695             map: _map,
29696             marker: _marker,
29697             circle: null,
29698             location: position,
29699             radius: this.radius,
29700             locationName: this.locationName,
29701             addressComponents: {
29702                 formatted_address: null,
29703                 addressLine1: null,
29704                 addressLine2: null,
29705                 streetName: null,
29706                 streetNumber: null,
29707                 city: null,
29708                 district: null,
29709                 state: null,
29710                 stateOrProvince: null
29711             },
29712             settings: this,
29713             domContainer: this.el.dom,
29714             geodecoder: new google.maps.Geocoder()
29715         };
29716     },
29717     
29718     drawCircle: function(center, radius, options) 
29719     {
29720         if (this.gMapContext.circle != null) {
29721             this.gMapContext.circle.setMap(null);
29722         }
29723         if (radius > 0) {
29724             radius *= 1;
29725             options = Roo.apply({}, options, {
29726                 strokeColor: "#0000FF",
29727                 strokeOpacity: .35,
29728                 strokeWeight: 2,
29729                 fillColor: "#0000FF",
29730                 fillOpacity: .2
29731             });
29732             
29733             options.map = this.gMapContext.map;
29734             options.radius = radius;
29735             options.center = center;
29736             this.gMapContext.circle = new google.maps.Circle(options);
29737             return this.gMapContext.circle;
29738         }
29739         
29740         return null;
29741     },
29742     
29743     setPosition: function(location) 
29744     {
29745         this.gMapContext.location = location;
29746         this.gMapContext.marker.setPosition(location);
29747         this.gMapContext.map.panTo(location);
29748         this.drawCircle(location, this.gMapContext.radius, {});
29749         
29750         var _this = this;
29751         
29752         if (this.gMapContext.settings.enableReverseGeocode) {
29753             this.gMapContext.geodecoder.geocode({
29754                 latLng: this.gMapContext.location
29755             }, function(results, status) {
29756                 
29757                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29758                     _this.gMapContext.locationName = results[0].formatted_address;
29759                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29760                     
29761                     _this.fireEvent('positionchanged', this, location);
29762                 }
29763             });
29764             
29765             return;
29766         }
29767         
29768         this.fireEvent('positionchanged', this, location);
29769     },
29770     
29771     resize: function()
29772     {
29773         google.maps.event.trigger(this.gMapContext.map, "resize");
29774         
29775         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29776         
29777         this.fireEvent('resize', this);
29778     },
29779     
29780     setPositionByLatLng: function(latitude, longitude)
29781     {
29782         this.setPosition(new google.maps.LatLng(latitude, longitude));
29783     },
29784     
29785     getCurrentPosition: function() 
29786     {
29787         return {
29788             latitude: this.gMapContext.location.lat(),
29789             longitude: this.gMapContext.location.lng()
29790         };
29791     },
29792     
29793     getAddressName: function() 
29794     {
29795         return this.gMapContext.locationName;
29796     },
29797     
29798     getAddressComponents: function() 
29799     {
29800         return this.gMapContext.addressComponents;
29801     },
29802     
29803     address_component_from_google_geocode: function(address_components) 
29804     {
29805         var result = {};
29806         
29807         for (var i = 0; i < address_components.length; i++) {
29808             var component = address_components[i];
29809             if (component.types.indexOf("postal_code") >= 0) {
29810                 result.postalCode = component.short_name;
29811             } else if (component.types.indexOf("street_number") >= 0) {
29812                 result.streetNumber = component.short_name;
29813             } else if (component.types.indexOf("route") >= 0) {
29814                 result.streetName = component.short_name;
29815             } else if (component.types.indexOf("neighborhood") >= 0) {
29816                 result.city = component.short_name;
29817             } else if (component.types.indexOf("locality") >= 0) {
29818                 result.city = component.short_name;
29819             } else if (component.types.indexOf("sublocality") >= 0) {
29820                 result.district = component.short_name;
29821             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29822                 result.stateOrProvince = component.short_name;
29823             } else if (component.types.indexOf("country") >= 0) {
29824                 result.country = component.short_name;
29825             }
29826         }
29827         
29828         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29829         result.addressLine2 = "";
29830         return result;
29831     },
29832     
29833     setZoomLevel: function(zoom)
29834     {
29835         this.gMapContext.map.setZoom(zoom);
29836     },
29837     
29838     show: function()
29839     {
29840         if(!this.el){
29841             return;
29842         }
29843         
29844         this.el.show();
29845         
29846         this.resize();
29847         
29848         this.fireEvent('show', this);
29849     },
29850     
29851     hide: function()
29852     {
29853         if(!this.el){
29854             return;
29855         }
29856         
29857         this.el.hide();
29858         
29859         this.fireEvent('hide', this);
29860     }
29861     
29862 });
29863
29864 Roo.apply(Roo.bootstrap.LocationPicker, {
29865     
29866     OverlayView : function(map, options)
29867     {
29868         options = options || {};
29869         
29870         this.setMap(map);
29871     }
29872     
29873     
29874 });/**
29875  * @class Roo.bootstrap.Alert
29876  * @extends Roo.bootstrap.Component
29877  * Bootstrap Alert class - shows an alert area box
29878  * eg
29879  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29880   Enter a valid email address
29881 </div>
29882  * @licence LGPL
29883  * @cfg {String} title The title of alert
29884  * @cfg {String} html The content of alert
29885  * @cfg {String} weight (  success | info | warning | danger )
29886  * @cfg {String} fa font-awesomeicon
29887  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29888  * @cfg {Boolean} close true to show a x closer
29889  * 
29890  * 
29891  * @constructor
29892  * Create a new alert
29893  * @param {Object} config The config object
29894  */
29895
29896
29897 Roo.bootstrap.Alert = function(config){
29898     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29899     
29900 };
29901
29902 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29903     
29904     title: '',
29905     html: '',
29906     weight: false,
29907     fa: false,
29908     faicon: false, // BC
29909     close : false,
29910     
29911     
29912     getAutoCreate : function()
29913     {
29914         
29915         var cfg = {
29916             tag : 'div',
29917             cls : 'alert',
29918             cn : [
29919                 {
29920                     tag: 'button',
29921                     type :  "button",
29922                     cls: "close",
29923                     html : '×',
29924                     style : this.close ? '' : 'display:none'
29925                 },
29926                 {
29927                     tag : 'i',
29928                     cls : 'roo-alert-icon'
29929                     
29930                 },
29931                 {
29932                     tag : 'b',
29933                     cls : 'roo-alert-title',
29934                     html : this.title
29935                 },
29936                 {
29937                     tag : 'span',
29938                     cls : 'roo-alert-text',
29939                     html : this.html
29940                 }
29941             ]
29942         };
29943         
29944         if(this.faicon){
29945             cfg.cn[0].cls += ' fa ' + this.faicon;
29946         }
29947         if(this.fa){
29948             cfg.cn[0].cls += ' fa ' + this.fa;
29949         }
29950         
29951         if(this.weight){
29952             cfg.cls += ' alert-' + this.weight;
29953         }
29954         
29955         return cfg;
29956     },
29957     
29958     initEvents: function() 
29959     {
29960         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29961         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29962         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29963         if (this.seconds > 0) {
29964             this.hide.defer(this.seconds, this);
29965         }
29966     },
29967     
29968     setTitle : function(str)
29969     {
29970         this.titleEl.dom.innerHTML = str;
29971     },
29972     
29973     setText : function(str)
29974     {
29975         this.titleEl.dom.innerHTML = str;
29976     },
29977     
29978     setWeight : function(weight)
29979     {
29980         if(this.weight){
29981             this.el.removeClass('alert-' + this.weight);
29982         }
29983         
29984         this.weight = weight;
29985         
29986         this.el.addClass('alert-' + this.weight);
29987     },
29988     
29989     setIcon : function(icon)
29990     {
29991         if(this.faicon){
29992             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
29993         }
29994         
29995         this.faicon = icon;
29996         
29997         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
29998     },
29999     
30000     hide: function() 
30001     {
30002         this.el.hide();   
30003     },
30004     
30005     show: function() 
30006     {  
30007         this.el.show();   
30008     }
30009     
30010 });
30011
30012  
30013 /*
30014 * Licence: LGPL
30015 */
30016
30017 /**
30018  * @class Roo.bootstrap.UploadCropbox
30019  * @extends Roo.bootstrap.Component
30020  * Bootstrap UploadCropbox class
30021  * @cfg {String} emptyText show when image has been loaded
30022  * @cfg {String} rotateNotify show when image too small to rotate
30023  * @cfg {Number} errorTimeout default 3000
30024  * @cfg {Number} minWidth default 300
30025  * @cfg {Number} minHeight default 300
30026  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30027  * @cfg {Boolean} isDocument (true|false) default false
30028  * @cfg {String} url action url
30029  * @cfg {String} paramName default 'imageUpload'
30030  * @cfg {String} method default POST
30031  * @cfg {Boolean} loadMask (true|false) default true
30032  * @cfg {Boolean} loadingText default 'Loading...'
30033  * 
30034  * @constructor
30035  * Create a new UploadCropbox
30036  * @param {Object} config The config object
30037  */
30038
30039 Roo.bootstrap.UploadCropbox = function(config){
30040     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30041     
30042     this.addEvents({
30043         /**
30044          * @event beforeselectfile
30045          * Fire before select file
30046          * @param {Roo.bootstrap.UploadCropbox} this
30047          */
30048         "beforeselectfile" : true,
30049         /**
30050          * @event initial
30051          * Fire after initEvent
30052          * @param {Roo.bootstrap.UploadCropbox} this
30053          */
30054         "initial" : true,
30055         /**
30056          * @event crop
30057          * Fire after initEvent
30058          * @param {Roo.bootstrap.UploadCropbox} this
30059          * @param {String} data
30060          */
30061         "crop" : true,
30062         /**
30063          * @event prepare
30064          * Fire when preparing the file data
30065          * @param {Roo.bootstrap.UploadCropbox} this
30066          * @param {Object} file
30067          */
30068         "prepare" : true,
30069         /**
30070          * @event exception
30071          * Fire when get exception
30072          * @param {Roo.bootstrap.UploadCropbox} this
30073          * @param {XMLHttpRequest} xhr
30074          */
30075         "exception" : true,
30076         /**
30077          * @event beforeloadcanvas
30078          * Fire before load the canvas
30079          * @param {Roo.bootstrap.UploadCropbox} this
30080          * @param {String} src
30081          */
30082         "beforeloadcanvas" : true,
30083         /**
30084          * @event trash
30085          * Fire when trash image
30086          * @param {Roo.bootstrap.UploadCropbox} this
30087          */
30088         "trash" : true,
30089         /**
30090          * @event download
30091          * Fire when download the image
30092          * @param {Roo.bootstrap.UploadCropbox} this
30093          */
30094         "download" : true,
30095         /**
30096          * @event footerbuttonclick
30097          * Fire when footerbuttonclick
30098          * @param {Roo.bootstrap.UploadCropbox} this
30099          * @param {String} type
30100          */
30101         "footerbuttonclick" : true,
30102         /**
30103          * @event resize
30104          * Fire when resize
30105          * @param {Roo.bootstrap.UploadCropbox} this
30106          */
30107         "resize" : true,
30108         /**
30109          * @event rotate
30110          * Fire when rotate the image
30111          * @param {Roo.bootstrap.UploadCropbox} this
30112          * @param {String} pos
30113          */
30114         "rotate" : true,
30115         /**
30116          * @event inspect
30117          * Fire when inspect the file
30118          * @param {Roo.bootstrap.UploadCropbox} this
30119          * @param {Object} file
30120          */
30121         "inspect" : true,
30122         /**
30123          * @event upload
30124          * Fire when xhr upload the file
30125          * @param {Roo.bootstrap.UploadCropbox} this
30126          * @param {Object} data
30127          */
30128         "upload" : true,
30129         /**
30130          * @event arrange
30131          * Fire when arrange the file data
30132          * @param {Roo.bootstrap.UploadCropbox} this
30133          * @param {Object} formData
30134          */
30135         "arrange" : true
30136     });
30137     
30138     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30139 };
30140
30141 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30142     
30143     emptyText : 'Click to upload image',
30144     rotateNotify : 'Image is too small to rotate',
30145     errorTimeout : 3000,
30146     scale : 0,
30147     baseScale : 1,
30148     rotate : 0,
30149     dragable : false,
30150     pinching : false,
30151     mouseX : 0,
30152     mouseY : 0,
30153     cropData : false,
30154     minWidth : 300,
30155     minHeight : 300,
30156     file : false,
30157     exif : {},
30158     baseRotate : 1,
30159     cropType : 'image/jpeg',
30160     buttons : false,
30161     canvasLoaded : false,
30162     isDocument : false,
30163     method : 'POST',
30164     paramName : 'imageUpload',
30165     loadMask : true,
30166     loadingText : 'Loading...',
30167     maskEl : false,
30168     
30169     getAutoCreate : function()
30170     {
30171         var cfg = {
30172             tag : 'div',
30173             cls : 'roo-upload-cropbox',
30174             cn : [
30175                 {
30176                     tag : 'input',
30177                     cls : 'roo-upload-cropbox-selector',
30178                     type : 'file'
30179                 },
30180                 {
30181                     tag : 'div',
30182                     cls : 'roo-upload-cropbox-body',
30183                     style : 'cursor:pointer',
30184                     cn : [
30185                         {
30186                             tag : 'div',
30187                             cls : 'roo-upload-cropbox-preview'
30188                         },
30189                         {
30190                             tag : 'div',
30191                             cls : 'roo-upload-cropbox-thumb'
30192                         },
30193                         {
30194                             tag : 'div',
30195                             cls : 'roo-upload-cropbox-empty-notify',
30196                             html : this.emptyText
30197                         },
30198                         {
30199                             tag : 'div',
30200                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30201                             html : this.rotateNotify
30202                         }
30203                     ]
30204                 },
30205                 {
30206                     tag : 'div',
30207                     cls : 'roo-upload-cropbox-footer',
30208                     cn : {
30209                         tag : 'div',
30210                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30211                         cn : []
30212                     }
30213                 }
30214             ]
30215         };
30216         
30217         return cfg;
30218     },
30219     
30220     onRender : function(ct, position)
30221     {
30222         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30223         
30224         if (this.buttons.length) {
30225             
30226             Roo.each(this.buttons, function(bb) {
30227                 
30228                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30229                 
30230                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30231                 
30232             }, this);
30233         }
30234         
30235         if(this.loadMask){
30236             this.maskEl = this.el;
30237         }
30238     },
30239     
30240     initEvents : function()
30241     {
30242         this.urlAPI = (window.createObjectURL && window) || 
30243                                 (window.URL && URL.revokeObjectURL && URL) || 
30244                                 (window.webkitURL && webkitURL);
30245                         
30246         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30247         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30248         
30249         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30250         this.selectorEl.hide();
30251         
30252         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30253         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30254         
30255         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30256         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30257         this.thumbEl.hide();
30258         
30259         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30260         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30261         
30262         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30263         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30264         this.errorEl.hide();
30265         
30266         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30267         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30268         this.footerEl.hide();
30269         
30270         this.setThumbBoxSize();
30271         
30272         this.bind();
30273         
30274         this.resize();
30275         
30276         this.fireEvent('initial', this);
30277     },
30278
30279     bind : function()
30280     {
30281         var _this = this;
30282         
30283         window.addEventListener("resize", function() { _this.resize(); } );
30284         
30285         this.bodyEl.on('click', this.beforeSelectFile, this);
30286         
30287         if(Roo.isTouch){
30288             this.bodyEl.on('touchstart', this.onTouchStart, this);
30289             this.bodyEl.on('touchmove', this.onTouchMove, this);
30290             this.bodyEl.on('touchend', this.onTouchEnd, this);
30291         }
30292         
30293         if(!Roo.isTouch){
30294             this.bodyEl.on('mousedown', this.onMouseDown, this);
30295             this.bodyEl.on('mousemove', this.onMouseMove, this);
30296             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30297             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30298             Roo.get(document).on('mouseup', this.onMouseUp, this);
30299         }
30300         
30301         this.selectorEl.on('change', this.onFileSelected, this);
30302     },
30303     
30304     reset : function()
30305     {    
30306         this.scale = 0;
30307         this.baseScale = 1;
30308         this.rotate = 0;
30309         this.baseRotate = 1;
30310         this.dragable = false;
30311         this.pinching = false;
30312         this.mouseX = 0;
30313         this.mouseY = 0;
30314         this.cropData = false;
30315         this.notifyEl.dom.innerHTML = this.emptyText;
30316         
30317         this.selectorEl.dom.value = '';
30318         
30319     },
30320     
30321     resize : function()
30322     {
30323         if(this.fireEvent('resize', this) != false){
30324             this.setThumbBoxPosition();
30325             this.setCanvasPosition();
30326         }
30327     },
30328     
30329     onFooterButtonClick : function(e, el, o, type)
30330     {
30331         switch (type) {
30332             case 'rotate-left' :
30333                 this.onRotateLeft(e);
30334                 break;
30335             case 'rotate-right' :
30336                 this.onRotateRight(e);
30337                 break;
30338             case 'picture' :
30339                 this.beforeSelectFile(e);
30340                 break;
30341             case 'trash' :
30342                 this.trash(e);
30343                 break;
30344             case 'crop' :
30345                 this.crop(e);
30346                 break;
30347             case 'download' :
30348                 this.download(e);
30349                 break;
30350             default :
30351                 break;
30352         }
30353         
30354         this.fireEvent('footerbuttonclick', this, type);
30355     },
30356     
30357     beforeSelectFile : function(e)
30358     {
30359         e.preventDefault();
30360         
30361         if(this.fireEvent('beforeselectfile', this) != false){
30362             this.selectorEl.dom.click();
30363         }
30364     },
30365     
30366     onFileSelected : function(e)
30367     {
30368         e.preventDefault();
30369         
30370         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30371             return;
30372         }
30373         
30374         var file = this.selectorEl.dom.files[0];
30375         
30376         if(this.fireEvent('inspect', this, file) != false){
30377             this.prepare(file);
30378         }
30379         
30380     },
30381     
30382     trash : function(e)
30383     {
30384         this.fireEvent('trash', this);
30385     },
30386     
30387     download : function(e)
30388     {
30389         this.fireEvent('download', this);
30390     },
30391     
30392     loadCanvas : function(src)
30393     {   
30394         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30395             
30396             this.reset();
30397             
30398             this.imageEl = document.createElement('img');
30399             
30400             var _this = this;
30401             
30402             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30403             
30404             this.imageEl.src = src;
30405         }
30406     },
30407     
30408     onLoadCanvas : function()
30409     {   
30410         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30411         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30412         
30413         this.bodyEl.un('click', this.beforeSelectFile, this);
30414         
30415         this.notifyEl.hide();
30416         this.thumbEl.show();
30417         this.footerEl.show();
30418         
30419         this.baseRotateLevel();
30420         
30421         if(this.isDocument){
30422             this.setThumbBoxSize();
30423         }
30424         
30425         this.setThumbBoxPosition();
30426         
30427         this.baseScaleLevel();
30428         
30429         this.draw();
30430         
30431         this.resize();
30432         
30433         this.canvasLoaded = true;
30434         
30435         if(this.loadMask){
30436             this.maskEl.unmask();
30437         }
30438         
30439     },
30440     
30441     setCanvasPosition : function()
30442     {   
30443         if(!this.canvasEl){
30444             return;
30445         }
30446         
30447         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30448         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30449         
30450         this.previewEl.setLeft(pw);
30451         this.previewEl.setTop(ph);
30452         
30453     },
30454     
30455     onMouseDown : function(e)
30456     {   
30457         e.stopEvent();
30458         
30459         this.dragable = true;
30460         this.pinching = false;
30461         
30462         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30463             this.dragable = false;
30464             return;
30465         }
30466         
30467         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30468         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30469         
30470     },
30471     
30472     onMouseMove : function(e)
30473     {   
30474         e.stopEvent();
30475         
30476         if(!this.canvasLoaded){
30477             return;
30478         }
30479         
30480         if (!this.dragable){
30481             return;
30482         }
30483         
30484         var minX = Math.ceil(this.thumbEl.getLeft(true));
30485         var minY = Math.ceil(this.thumbEl.getTop(true));
30486         
30487         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30488         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30489         
30490         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30491         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30492         
30493         x = x - this.mouseX;
30494         y = y - this.mouseY;
30495         
30496         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30497         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30498         
30499         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30500         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30501         
30502         this.previewEl.setLeft(bgX);
30503         this.previewEl.setTop(bgY);
30504         
30505         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30506         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30507     },
30508     
30509     onMouseUp : function(e)
30510     {   
30511         e.stopEvent();
30512         
30513         this.dragable = false;
30514     },
30515     
30516     onMouseWheel : function(e)
30517     {   
30518         e.stopEvent();
30519         
30520         this.startScale = this.scale;
30521         
30522         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30523         
30524         if(!this.zoomable()){
30525             this.scale = this.startScale;
30526             return;
30527         }
30528         
30529         this.draw();
30530         
30531         return;
30532     },
30533     
30534     zoomable : function()
30535     {
30536         var minScale = this.thumbEl.getWidth() / this.minWidth;
30537         
30538         if(this.minWidth < this.minHeight){
30539             minScale = this.thumbEl.getHeight() / this.minHeight;
30540         }
30541         
30542         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30543         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30544         
30545         if(
30546                 this.isDocument &&
30547                 (this.rotate == 0 || this.rotate == 180) && 
30548                 (
30549                     width > this.imageEl.OriginWidth || 
30550                     height > this.imageEl.OriginHeight ||
30551                     (width < this.minWidth && height < this.minHeight)
30552                 )
30553         ){
30554             return false;
30555         }
30556         
30557         if(
30558                 this.isDocument &&
30559                 (this.rotate == 90 || this.rotate == 270) && 
30560                 (
30561                     width > this.imageEl.OriginWidth || 
30562                     height > this.imageEl.OriginHeight ||
30563                     (width < this.minHeight && height < this.minWidth)
30564                 )
30565         ){
30566             return false;
30567         }
30568         
30569         if(
30570                 !this.isDocument &&
30571                 (this.rotate == 0 || this.rotate == 180) && 
30572                 (
30573                     width < this.minWidth || 
30574                     width > this.imageEl.OriginWidth || 
30575                     height < this.minHeight || 
30576                     height > this.imageEl.OriginHeight
30577                 )
30578         ){
30579             return false;
30580         }
30581         
30582         if(
30583                 !this.isDocument &&
30584                 (this.rotate == 90 || this.rotate == 270) && 
30585                 (
30586                     width < this.minHeight || 
30587                     width > this.imageEl.OriginWidth || 
30588                     height < this.minWidth || 
30589                     height > this.imageEl.OriginHeight
30590                 )
30591         ){
30592             return false;
30593         }
30594         
30595         return true;
30596         
30597     },
30598     
30599     onRotateLeft : function(e)
30600     {   
30601         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30602             
30603             var minScale = this.thumbEl.getWidth() / this.minWidth;
30604             
30605             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30606             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30607             
30608             this.startScale = this.scale;
30609             
30610             while (this.getScaleLevel() < minScale){
30611             
30612                 this.scale = this.scale + 1;
30613                 
30614                 if(!this.zoomable()){
30615                     break;
30616                 }
30617                 
30618                 if(
30619                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30620                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30621                 ){
30622                     continue;
30623                 }
30624                 
30625                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30626
30627                 this.draw();
30628                 
30629                 return;
30630             }
30631             
30632             this.scale = this.startScale;
30633             
30634             this.onRotateFail();
30635             
30636             return false;
30637         }
30638         
30639         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30640
30641         if(this.isDocument){
30642             this.setThumbBoxSize();
30643             this.setThumbBoxPosition();
30644             this.setCanvasPosition();
30645         }
30646         
30647         this.draw();
30648         
30649         this.fireEvent('rotate', this, 'left');
30650         
30651     },
30652     
30653     onRotateRight : function(e)
30654     {
30655         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30656             
30657             var minScale = this.thumbEl.getWidth() / this.minWidth;
30658         
30659             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30660             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30661             
30662             this.startScale = this.scale;
30663             
30664             while (this.getScaleLevel() < minScale){
30665             
30666                 this.scale = this.scale + 1;
30667                 
30668                 if(!this.zoomable()){
30669                     break;
30670                 }
30671                 
30672                 if(
30673                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30674                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30675                 ){
30676                     continue;
30677                 }
30678                 
30679                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30680
30681                 this.draw();
30682                 
30683                 return;
30684             }
30685             
30686             this.scale = this.startScale;
30687             
30688             this.onRotateFail();
30689             
30690             return false;
30691         }
30692         
30693         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30694
30695         if(this.isDocument){
30696             this.setThumbBoxSize();
30697             this.setThumbBoxPosition();
30698             this.setCanvasPosition();
30699         }
30700         
30701         this.draw();
30702         
30703         this.fireEvent('rotate', this, 'right');
30704     },
30705     
30706     onRotateFail : function()
30707     {
30708         this.errorEl.show(true);
30709         
30710         var _this = this;
30711         
30712         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30713     },
30714     
30715     draw : function()
30716     {
30717         this.previewEl.dom.innerHTML = '';
30718         
30719         var canvasEl = document.createElement("canvas");
30720         
30721         var contextEl = canvasEl.getContext("2d");
30722         
30723         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30724         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30725         var center = this.imageEl.OriginWidth / 2;
30726         
30727         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30728             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30729             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30730             center = this.imageEl.OriginHeight / 2;
30731         }
30732         
30733         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30734         
30735         contextEl.translate(center, center);
30736         contextEl.rotate(this.rotate * Math.PI / 180);
30737
30738         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30739         
30740         this.canvasEl = document.createElement("canvas");
30741         
30742         this.contextEl = this.canvasEl.getContext("2d");
30743         
30744         switch (this.rotate) {
30745             case 0 :
30746                 
30747                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30748                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30749                 
30750                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30751                 
30752                 break;
30753             case 90 : 
30754                 
30755                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30756                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30757                 
30758                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30759                     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);
30760                     break;
30761                 }
30762                 
30763                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30764                 
30765                 break;
30766             case 180 :
30767                 
30768                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30769                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30770                 
30771                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30772                     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);
30773                     break;
30774                 }
30775                 
30776                 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);
30777                 
30778                 break;
30779             case 270 :
30780                 
30781                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30782                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30783         
30784                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30785                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30786                     break;
30787                 }
30788                 
30789                 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);
30790                 
30791                 break;
30792             default : 
30793                 break;
30794         }
30795         
30796         this.previewEl.appendChild(this.canvasEl);
30797         
30798         this.setCanvasPosition();
30799     },
30800     
30801     crop : function()
30802     {
30803         if(!this.canvasLoaded){
30804             return;
30805         }
30806         
30807         var imageCanvas = document.createElement("canvas");
30808         
30809         var imageContext = imageCanvas.getContext("2d");
30810         
30811         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30812         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30813         
30814         var center = imageCanvas.width / 2;
30815         
30816         imageContext.translate(center, center);
30817         
30818         imageContext.rotate(this.rotate * Math.PI / 180);
30819         
30820         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30821         
30822         var canvas = document.createElement("canvas");
30823         
30824         var context = canvas.getContext("2d");
30825                 
30826         canvas.width = this.minWidth;
30827         canvas.height = this.minHeight;
30828
30829         switch (this.rotate) {
30830             case 0 :
30831                 
30832                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30833                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30834                 
30835                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30836                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30837                 
30838                 var targetWidth = this.minWidth - 2 * x;
30839                 var targetHeight = this.minHeight - 2 * y;
30840                 
30841                 var scale = 1;
30842                 
30843                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30844                     scale = targetWidth / width;
30845                 }
30846                 
30847                 if(x > 0 && y == 0){
30848                     scale = targetHeight / height;
30849                 }
30850                 
30851                 if(x > 0 && y > 0){
30852                     scale = targetWidth / width;
30853                     
30854                     if(width < height){
30855                         scale = targetHeight / height;
30856                     }
30857                 }
30858                 
30859                 context.scale(scale, scale);
30860                 
30861                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30862                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30863
30864                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30865                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30866
30867                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30868                 
30869                 break;
30870             case 90 : 
30871                 
30872                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30873                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30874                 
30875                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30876                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30877                 
30878                 var targetWidth = this.minWidth - 2 * x;
30879                 var targetHeight = this.minHeight - 2 * y;
30880                 
30881                 var scale = 1;
30882                 
30883                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30884                     scale = targetWidth / width;
30885                 }
30886                 
30887                 if(x > 0 && y == 0){
30888                     scale = targetHeight / height;
30889                 }
30890                 
30891                 if(x > 0 && y > 0){
30892                     scale = targetWidth / width;
30893                     
30894                     if(width < height){
30895                         scale = targetHeight / height;
30896                     }
30897                 }
30898                 
30899                 context.scale(scale, scale);
30900                 
30901                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30902                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30903
30904                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30905                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30906                 
30907                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30908                 
30909                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30910                 
30911                 break;
30912             case 180 :
30913                 
30914                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30915                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30916                 
30917                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30918                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30919                 
30920                 var targetWidth = this.minWidth - 2 * x;
30921                 var targetHeight = this.minHeight - 2 * y;
30922                 
30923                 var scale = 1;
30924                 
30925                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30926                     scale = targetWidth / width;
30927                 }
30928                 
30929                 if(x > 0 && y == 0){
30930                     scale = targetHeight / height;
30931                 }
30932                 
30933                 if(x > 0 && y > 0){
30934                     scale = targetWidth / width;
30935                     
30936                     if(width < height){
30937                         scale = targetHeight / height;
30938                     }
30939                 }
30940                 
30941                 context.scale(scale, scale);
30942                 
30943                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30944                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30945
30946                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30947                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30948
30949                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30950                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30951                 
30952                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30953                 
30954                 break;
30955             case 270 :
30956                 
30957                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30958                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30959                 
30960                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30961                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30962                 
30963                 var targetWidth = this.minWidth - 2 * x;
30964                 var targetHeight = this.minHeight - 2 * y;
30965                 
30966                 var scale = 1;
30967                 
30968                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30969                     scale = targetWidth / width;
30970                 }
30971                 
30972                 if(x > 0 && y == 0){
30973                     scale = targetHeight / height;
30974                 }
30975                 
30976                 if(x > 0 && y > 0){
30977                     scale = targetWidth / width;
30978                     
30979                     if(width < height){
30980                         scale = targetHeight / height;
30981                     }
30982                 }
30983                 
30984                 context.scale(scale, scale);
30985                 
30986                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30987                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30988
30989                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30990                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30991                 
30992                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30993                 
30994                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30995                 
30996                 break;
30997             default : 
30998                 break;
30999         }
31000         
31001         this.cropData = canvas.toDataURL(this.cropType);
31002         
31003         if(this.fireEvent('crop', this, this.cropData) !== false){
31004             this.process(this.file, this.cropData);
31005         }
31006         
31007         return;
31008         
31009     },
31010     
31011     setThumbBoxSize : function()
31012     {
31013         var width, height;
31014         
31015         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31016             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31017             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31018             
31019             this.minWidth = width;
31020             this.minHeight = height;
31021             
31022             if(this.rotate == 90 || this.rotate == 270){
31023                 this.minWidth = height;
31024                 this.minHeight = width;
31025             }
31026         }
31027         
31028         height = 300;
31029         width = Math.ceil(this.minWidth * height / this.minHeight);
31030         
31031         if(this.minWidth > this.minHeight){
31032             width = 300;
31033             height = Math.ceil(this.minHeight * width / this.minWidth);
31034         }
31035         
31036         this.thumbEl.setStyle({
31037             width : width + 'px',
31038             height : height + 'px'
31039         });
31040
31041         return;
31042             
31043     },
31044     
31045     setThumbBoxPosition : function()
31046     {
31047         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31048         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31049         
31050         this.thumbEl.setLeft(x);
31051         this.thumbEl.setTop(y);
31052         
31053     },
31054     
31055     baseRotateLevel : function()
31056     {
31057         this.baseRotate = 1;
31058         
31059         if(
31060                 typeof(this.exif) != 'undefined' &&
31061                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31062                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31063         ){
31064             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31065         }
31066         
31067         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31068         
31069     },
31070     
31071     baseScaleLevel : function()
31072     {
31073         var width, height;
31074         
31075         if(this.isDocument){
31076             
31077             if(this.baseRotate == 6 || this.baseRotate == 8){
31078             
31079                 height = this.thumbEl.getHeight();
31080                 this.baseScale = height / this.imageEl.OriginWidth;
31081
31082                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31083                     width = this.thumbEl.getWidth();
31084                     this.baseScale = width / this.imageEl.OriginHeight;
31085                 }
31086
31087                 return;
31088             }
31089
31090             height = this.thumbEl.getHeight();
31091             this.baseScale = height / this.imageEl.OriginHeight;
31092
31093             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31094                 width = this.thumbEl.getWidth();
31095                 this.baseScale = width / this.imageEl.OriginWidth;
31096             }
31097
31098             return;
31099         }
31100         
31101         if(this.baseRotate == 6 || this.baseRotate == 8){
31102             
31103             width = this.thumbEl.getHeight();
31104             this.baseScale = width / this.imageEl.OriginHeight;
31105             
31106             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31107                 height = this.thumbEl.getWidth();
31108                 this.baseScale = height / this.imageEl.OriginHeight;
31109             }
31110             
31111             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31112                 height = this.thumbEl.getWidth();
31113                 this.baseScale = height / this.imageEl.OriginHeight;
31114                 
31115                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31116                     width = this.thumbEl.getHeight();
31117                     this.baseScale = width / this.imageEl.OriginWidth;
31118                 }
31119             }
31120             
31121             return;
31122         }
31123         
31124         width = this.thumbEl.getWidth();
31125         this.baseScale = width / this.imageEl.OriginWidth;
31126         
31127         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31128             height = this.thumbEl.getHeight();
31129             this.baseScale = height / this.imageEl.OriginHeight;
31130         }
31131         
31132         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31133             
31134             height = this.thumbEl.getHeight();
31135             this.baseScale = height / this.imageEl.OriginHeight;
31136             
31137             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31138                 width = this.thumbEl.getWidth();
31139                 this.baseScale = width / this.imageEl.OriginWidth;
31140             }
31141             
31142         }
31143         
31144         return;
31145     },
31146     
31147     getScaleLevel : function()
31148     {
31149         return this.baseScale * Math.pow(1.1, this.scale);
31150     },
31151     
31152     onTouchStart : function(e)
31153     {
31154         if(!this.canvasLoaded){
31155             this.beforeSelectFile(e);
31156             return;
31157         }
31158         
31159         var touches = e.browserEvent.touches;
31160         
31161         if(!touches){
31162             return;
31163         }
31164         
31165         if(touches.length == 1){
31166             this.onMouseDown(e);
31167             return;
31168         }
31169         
31170         if(touches.length != 2){
31171             return;
31172         }
31173         
31174         var coords = [];
31175         
31176         for(var i = 0, finger; finger = touches[i]; i++){
31177             coords.push(finger.pageX, finger.pageY);
31178         }
31179         
31180         var x = Math.pow(coords[0] - coords[2], 2);
31181         var y = Math.pow(coords[1] - coords[3], 2);
31182         
31183         this.startDistance = Math.sqrt(x + y);
31184         
31185         this.startScale = this.scale;
31186         
31187         this.pinching = true;
31188         this.dragable = false;
31189         
31190     },
31191     
31192     onTouchMove : function(e)
31193     {
31194         if(!this.pinching && !this.dragable){
31195             return;
31196         }
31197         
31198         var touches = e.browserEvent.touches;
31199         
31200         if(!touches){
31201             return;
31202         }
31203         
31204         if(this.dragable){
31205             this.onMouseMove(e);
31206             return;
31207         }
31208         
31209         var coords = [];
31210         
31211         for(var i = 0, finger; finger = touches[i]; i++){
31212             coords.push(finger.pageX, finger.pageY);
31213         }
31214         
31215         var x = Math.pow(coords[0] - coords[2], 2);
31216         var y = Math.pow(coords[1] - coords[3], 2);
31217         
31218         this.endDistance = Math.sqrt(x + y);
31219         
31220         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31221         
31222         if(!this.zoomable()){
31223             this.scale = this.startScale;
31224             return;
31225         }
31226         
31227         this.draw();
31228         
31229     },
31230     
31231     onTouchEnd : function(e)
31232     {
31233         this.pinching = false;
31234         this.dragable = false;
31235         
31236     },
31237     
31238     process : function(file, crop)
31239     {
31240         if(this.loadMask){
31241             this.maskEl.mask(this.loadingText);
31242         }
31243         
31244         this.xhr = new XMLHttpRequest();
31245         
31246         file.xhr = this.xhr;
31247
31248         this.xhr.open(this.method, this.url, true);
31249         
31250         var headers = {
31251             "Accept": "application/json",
31252             "Cache-Control": "no-cache",
31253             "X-Requested-With": "XMLHttpRequest"
31254         };
31255         
31256         for (var headerName in headers) {
31257             var headerValue = headers[headerName];
31258             if (headerValue) {
31259                 this.xhr.setRequestHeader(headerName, headerValue);
31260             }
31261         }
31262         
31263         var _this = this;
31264         
31265         this.xhr.onload = function()
31266         {
31267             _this.xhrOnLoad(_this.xhr);
31268         }
31269         
31270         this.xhr.onerror = function()
31271         {
31272             _this.xhrOnError(_this.xhr);
31273         }
31274         
31275         var formData = new FormData();
31276
31277         formData.append('returnHTML', 'NO');
31278         
31279         if(crop){
31280             formData.append('crop', crop);
31281         }
31282         
31283         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31284             formData.append(this.paramName, file, file.name);
31285         }
31286         
31287         if(typeof(file.filename) != 'undefined'){
31288             formData.append('filename', file.filename);
31289         }
31290         
31291         if(typeof(file.mimetype) != 'undefined'){
31292             formData.append('mimetype', file.mimetype);
31293         }
31294         
31295         if(this.fireEvent('arrange', this, formData) != false){
31296             this.xhr.send(formData);
31297         };
31298     },
31299     
31300     xhrOnLoad : function(xhr)
31301     {
31302         if(this.loadMask){
31303             this.maskEl.unmask();
31304         }
31305         
31306         if (xhr.readyState !== 4) {
31307             this.fireEvent('exception', this, xhr);
31308             return;
31309         }
31310
31311         var response = Roo.decode(xhr.responseText);
31312         
31313         if(!response.success){
31314             this.fireEvent('exception', this, xhr);
31315             return;
31316         }
31317         
31318         var response = Roo.decode(xhr.responseText);
31319         
31320         this.fireEvent('upload', this, response);
31321         
31322     },
31323     
31324     xhrOnError : function()
31325     {
31326         if(this.loadMask){
31327             this.maskEl.unmask();
31328         }
31329         
31330         Roo.log('xhr on error');
31331         
31332         var response = Roo.decode(xhr.responseText);
31333           
31334         Roo.log(response);
31335         
31336     },
31337     
31338     prepare : function(file)
31339     {   
31340         if(this.loadMask){
31341             this.maskEl.mask(this.loadingText);
31342         }
31343         
31344         this.file = false;
31345         this.exif = {};
31346         
31347         if(typeof(file) === 'string'){
31348             this.loadCanvas(file);
31349             return;
31350         }
31351         
31352         if(!file || !this.urlAPI){
31353             return;
31354         }
31355         
31356         this.file = file;
31357         this.cropType = file.type;
31358         
31359         var _this = this;
31360         
31361         if(this.fireEvent('prepare', this, this.file) != false){
31362             
31363             var reader = new FileReader();
31364             
31365             reader.onload = function (e) {
31366                 if (e.target.error) {
31367                     Roo.log(e.target.error);
31368                     return;
31369                 }
31370                 
31371                 var buffer = e.target.result,
31372                     dataView = new DataView(buffer),
31373                     offset = 2,
31374                     maxOffset = dataView.byteLength - 4,
31375                     markerBytes,
31376                     markerLength;
31377                 
31378                 if (dataView.getUint16(0) === 0xffd8) {
31379                     while (offset < maxOffset) {
31380                         markerBytes = dataView.getUint16(offset);
31381                         
31382                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31383                             markerLength = dataView.getUint16(offset + 2) + 2;
31384                             if (offset + markerLength > dataView.byteLength) {
31385                                 Roo.log('Invalid meta data: Invalid segment size.');
31386                                 break;
31387                             }
31388                             
31389                             if(markerBytes == 0xffe1){
31390                                 _this.parseExifData(
31391                                     dataView,
31392                                     offset,
31393                                     markerLength
31394                                 );
31395                             }
31396                             
31397                             offset += markerLength;
31398                             
31399                             continue;
31400                         }
31401                         
31402                         break;
31403                     }
31404                     
31405                 }
31406                 
31407                 var url = _this.urlAPI.createObjectURL(_this.file);
31408                 
31409                 _this.loadCanvas(url);
31410                 
31411                 return;
31412             }
31413             
31414             reader.readAsArrayBuffer(this.file);
31415             
31416         }
31417         
31418     },
31419     
31420     parseExifData : function(dataView, offset, length)
31421     {
31422         var tiffOffset = offset + 10,
31423             littleEndian,
31424             dirOffset;
31425     
31426         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31427             // No Exif data, might be XMP data instead
31428             return;
31429         }
31430         
31431         // Check for the ASCII code for "Exif" (0x45786966):
31432         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31433             // No Exif data, might be XMP data instead
31434             return;
31435         }
31436         if (tiffOffset + 8 > dataView.byteLength) {
31437             Roo.log('Invalid Exif data: Invalid segment size.');
31438             return;
31439         }
31440         // Check for the two null bytes:
31441         if (dataView.getUint16(offset + 8) !== 0x0000) {
31442             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31443             return;
31444         }
31445         // Check the byte alignment:
31446         switch (dataView.getUint16(tiffOffset)) {
31447         case 0x4949:
31448             littleEndian = true;
31449             break;
31450         case 0x4D4D:
31451             littleEndian = false;
31452             break;
31453         default:
31454             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31455             return;
31456         }
31457         // Check for the TIFF tag marker (0x002A):
31458         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31459             Roo.log('Invalid Exif data: Missing TIFF marker.');
31460             return;
31461         }
31462         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31463         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31464         
31465         this.parseExifTags(
31466             dataView,
31467             tiffOffset,
31468             tiffOffset + dirOffset,
31469             littleEndian
31470         );
31471     },
31472     
31473     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31474     {
31475         var tagsNumber,
31476             dirEndOffset,
31477             i;
31478         if (dirOffset + 6 > dataView.byteLength) {
31479             Roo.log('Invalid Exif data: Invalid directory offset.');
31480             return;
31481         }
31482         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31483         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31484         if (dirEndOffset + 4 > dataView.byteLength) {
31485             Roo.log('Invalid Exif data: Invalid directory size.');
31486             return;
31487         }
31488         for (i = 0; i < tagsNumber; i += 1) {
31489             this.parseExifTag(
31490                 dataView,
31491                 tiffOffset,
31492                 dirOffset + 2 + 12 * i, // tag offset
31493                 littleEndian
31494             );
31495         }
31496         // Return the offset to the next directory:
31497         return dataView.getUint32(dirEndOffset, littleEndian);
31498     },
31499     
31500     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31501     {
31502         var tag = dataView.getUint16(offset, littleEndian);
31503         
31504         this.exif[tag] = this.getExifValue(
31505             dataView,
31506             tiffOffset,
31507             offset,
31508             dataView.getUint16(offset + 2, littleEndian), // tag type
31509             dataView.getUint32(offset + 4, littleEndian), // tag length
31510             littleEndian
31511         );
31512     },
31513     
31514     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31515     {
31516         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31517             tagSize,
31518             dataOffset,
31519             values,
31520             i,
31521             str,
31522             c;
31523     
31524         if (!tagType) {
31525             Roo.log('Invalid Exif data: Invalid tag type.');
31526             return;
31527         }
31528         
31529         tagSize = tagType.size * length;
31530         // Determine if the value is contained in the dataOffset bytes,
31531         // or if the value at the dataOffset is a pointer to the actual data:
31532         dataOffset = tagSize > 4 ?
31533                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31534         if (dataOffset + tagSize > dataView.byteLength) {
31535             Roo.log('Invalid Exif data: Invalid data offset.');
31536             return;
31537         }
31538         if (length === 1) {
31539             return tagType.getValue(dataView, dataOffset, littleEndian);
31540         }
31541         values = [];
31542         for (i = 0; i < length; i += 1) {
31543             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31544         }
31545         
31546         if (tagType.ascii) {
31547             str = '';
31548             // Concatenate the chars:
31549             for (i = 0; i < values.length; i += 1) {
31550                 c = values[i];
31551                 // Ignore the terminating NULL byte(s):
31552                 if (c === '\u0000') {
31553                     break;
31554                 }
31555                 str += c;
31556             }
31557             return str;
31558         }
31559         return values;
31560     }
31561     
31562 });
31563
31564 Roo.apply(Roo.bootstrap.UploadCropbox, {
31565     tags : {
31566         'Orientation': 0x0112
31567     },
31568     
31569     Orientation: {
31570             1: 0, //'top-left',
31571 //            2: 'top-right',
31572             3: 180, //'bottom-right',
31573 //            4: 'bottom-left',
31574 //            5: 'left-top',
31575             6: 90, //'right-top',
31576 //            7: 'right-bottom',
31577             8: 270 //'left-bottom'
31578     },
31579     
31580     exifTagTypes : {
31581         // byte, 8-bit unsigned int:
31582         1: {
31583             getValue: function (dataView, dataOffset) {
31584                 return dataView.getUint8(dataOffset);
31585             },
31586             size: 1
31587         },
31588         // ascii, 8-bit byte:
31589         2: {
31590             getValue: function (dataView, dataOffset) {
31591                 return String.fromCharCode(dataView.getUint8(dataOffset));
31592             },
31593             size: 1,
31594             ascii: true
31595         },
31596         // short, 16 bit int:
31597         3: {
31598             getValue: function (dataView, dataOffset, littleEndian) {
31599                 return dataView.getUint16(dataOffset, littleEndian);
31600             },
31601             size: 2
31602         },
31603         // long, 32 bit int:
31604         4: {
31605             getValue: function (dataView, dataOffset, littleEndian) {
31606                 return dataView.getUint32(dataOffset, littleEndian);
31607             },
31608             size: 4
31609         },
31610         // rational = two long values, first is numerator, second is denominator:
31611         5: {
31612             getValue: function (dataView, dataOffset, littleEndian) {
31613                 return dataView.getUint32(dataOffset, littleEndian) /
31614                     dataView.getUint32(dataOffset + 4, littleEndian);
31615             },
31616             size: 8
31617         },
31618         // slong, 32 bit signed int:
31619         9: {
31620             getValue: function (dataView, dataOffset, littleEndian) {
31621                 return dataView.getInt32(dataOffset, littleEndian);
31622             },
31623             size: 4
31624         },
31625         // srational, two slongs, first is numerator, second is denominator:
31626         10: {
31627             getValue: function (dataView, dataOffset, littleEndian) {
31628                 return dataView.getInt32(dataOffset, littleEndian) /
31629                     dataView.getInt32(dataOffset + 4, littleEndian);
31630             },
31631             size: 8
31632         }
31633     },
31634     
31635     footer : {
31636         STANDARD : [
31637             {
31638                 tag : 'div',
31639                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31640                 action : 'rotate-left',
31641                 cn : [
31642                     {
31643                         tag : 'button',
31644                         cls : 'btn btn-default',
31645                         html : '<i class="fa fa-undo"></i>'
31646                     }
31647                 ]
31648             },
31649             {
31650                 tag : 'div',
31651                 cls : 'btn-group roo-upload-cropbox-picture',
31652                 action : 'picture',
31653                 cn : [
31654                     {
31655                         tag : 'button',
31656                         cls : 'btn btn-default',
31657                         html : '<i class="fa fa-picture-o"></i>'
31658                     }
31659                 ]
31660             },
31661             {
31662                 tag : 'div',
31663                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31664                 action : 'rotate-right',
31665                 cn : [
31666                     {
31667                         tag : 'button',
31668                         cls : 'btn btn-default',
31669                         html : '<i class="fa fa-repeat"></i>'
31670                     }
31671                 ]
31672             }
31673         ],
31674         DOCUMENT : [
31675             {
31676                 tag : 'div',
31677                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31678                 action : 'rotate-left',
31679                 cn : [
31680                     {
31681                         tag : 'button',
31682                         cls : 'btn btn-default',
31683                         html : '<i class="fa fa-undo"></i>'
31684                     }
31685                 ]
31686             },
31687             {
31688                 tag : 'div',
31689                 cls : 'btn-group roo-upload-cropbox-download',
31690                 action : 'download',
31691                 cn : [
31692                     {
31693                         tag : 'button',
31694                         cls : 'btn btn-default',
31695                         html : '<i class="fa fa-download"></i>'
31696                     }
31697                 ]
31698             },
31699             {
31700                 tag : 'div',
31701                 cls : 'btn-group roo-upload-cropbox-crop',
31702                 action : 'crop',
31703                 cn : [
31704                     {
31705                         tag : 'button',
31706                         cls : 'btn btn-default',
31707                         html : '<i class="fa fa-crop"></i>'
31708                     }
31709                 ]
31710             },
31711             {
31712                 tag : 'div',
31713                 cls : 'btn-group roo-upload-cropbox-trash',
31714                 action : 'trash',
31715                 cn : [
31716                     {
31717                         tag : 'button',
31718                         cls : 'btn btn-default',
31719                         html : '<i class="fa fa-trash"></i>'
31720                     }
31721                 ]
31722             },
31723             {
31724                 tag : 'div',
31725                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31726                 action : 'rotate-right',
31727                 cn : [
31728                     {
31729                         tag : 'button',
31730                         cls : 'btn btn-default',
31731                         html : '<i class="fa fa-repeat"></i>'
31732                     }
31733                 ]
31734             }
31735         ],
31736         ROTATOR : [
31737             {
31738                 tag : 'div',
31739                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31740                 action : 'rotate-left',
31741                 cn : [
31742                     {
31743                         tag : 'button',
31744                         cls : 'btn btn-default',
31745                         html : '<i class="fa fa-undo"></i>'
31746                     }
31747                 ]
31748             },
31749             {
31750                 tag : 'div',
31751                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31752                 action : 'rotate-right',
31753                 cn : [
31754                     {
31755                         tag : 'button',
31756                         cls : 'btn btn-default',
31757                         html : '<i class="fa fa-repeat"></i>'
31758                     }
31759                 ]
31760             }
31761         ]
31762     }
31763 });
31764
31765 /*
31766 * Licence: LGPL
31767 */
31768
31769 /**
31770  * @class Roo.bootstrap.DocumentManager
31771  * @extends Roo.bootstrap.Component
31772  * Bootstrap DocumentManager class
31773  * @cfg {String} paramName default 'imageUpload'
31774  * @cfg {String} toolTipName default 'filename'
31775  * @cfg {String} method default POST
31776  * @cfg {String} url action url
31777  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31778  * @cfg {Boolean} multiple multiple upload default true
31779  * @cfg {Number} thumbSize default 300
31780  * @cfg {String} fieldLabel
31781  * @cfg {Number} labelWidth default 4
31782  * @cfg {String} labelAlign (left|top) default left
31783  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31784 * @cfg {Number} labellg set the width of label (1-12)
31785  * @cfg {Number} labelmd set the width of label (1-12)
31786  * @cfg {Number} labelsm set the width of label (1-12)
31787  * @cfg {Number} labelxs set the width of label (1-12)
31788  * 
31789  * @constructor
31790  * Create a new DocumentManager
31791  * @param {Object} config The config object
31792  */
31793
31794 Roo.bootstrap.DocumentManager = function(config){
31795     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31796     
31797     this.files = [];
31798     this.delegates = [];
31799     
31800     this.addEvents({
31801         /**
31802          * @event initial
31803          * Fire when initial the DocumentManager
31804          * @param {Roo.bootstrap.DocumentManager} this
31805          */
31806         "initial" : true,
31807         /**
31808          * @event inspect
31809          * inspect selected file
31810          * @param {Roo.bootstrap.DocumentManager} this
31811          * @param {File} file
31812          */
31813         "inspect" : true,
31814         /**
31815          * @event exception
31816          * Fire when xhr load exception
31817          * @param {Roo.bootstrap.DocumentManager} this
31818          * @param {XMLHttpRequest} xhr
31819          */
31820         "exception" : true,
31821         /**
31822          * @event afterupload
31823          * Fire when xhr load exception
31824          * @param {Roo.bootstrap.DocumentManager} this
31825          * @param {XMLHttpRequest} xhr
31826          */
31827         "afterupload" : true,
31828         /**
31829          * @event prepare
31830          * prepare the form data
31831          * @param {Roo.bootstrap.DocumentManager} this
31832          * @param {Object} formData
31833          */
31834         "prepare" : true,
31835         /**
31836          * @event remove
31837          * Fire when remove the file
31838          * @param {Roo.bootstrap.DocumentManager} this
31839          * @param {Object} file
31840          */
31841         "remove" : true,
31842         /**
31843          * @event refresh
31844          * Fire after refresh the file
31845          * @param {Roo.bootstrap.DocumentManager} this
31846          */
31847         "refresh" : true,
31848         /**
31849          * @event click
31850          * Fire after click the image
31851          * @param {Roo.bootstrap.DocumentManager} this
31852          * @param {Object} file
31853          */
31854         "click" : true,
31855         /**
31856          * @event edit
31857          * Fire when upload a image and editable set to true
31858          * @param {Roo.bootstrap.DocumentManager} this
31859          * @param {Object} file
31860          */
31861         "edit" : true,
31862         /**
31863          * @event beforeselectfile
31864          * Fire before select file
31865          * @param {Roo.bootstrap.DocumentManager} this
31866          */
31867         "beforeselectfile" : true,
31868         /**
31869          * @event process
31870          * Fire before process file
31871          * @param {Roo.bootstrap.DocumentManager} this
31872          * @param {Object} file
31873          */
31874         "process" : true,
31875         /**
31876          * @event previewrendered
31877          * Fire when preview rendered
31878          * @param {Roo.bootstrap.DocumentManager} this
31879          * @param {Object} file
31880          */
31881         "previewrendered" : true,
31882         /**
31883          */
31884         "previewResize" : true
31885         
31886     });
31887 };
31888
31889 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31890     
31891     boxes : 0,
31892     inputName : '',
31893     thumbSize : 300,
31894     multiple : true,
31895     files : false,
31896     method : 'POST',
31897     url : '',
31898     paramName : 'imageUpload',
31899     toolTipName : 'filename',
31900     fieldLabel : '',
31901     labelWidth : 4,
31902     labelAlign : 'left',
31903     editable : true,
31904     delegates : false,
31905     xhr : false, 
31906     
31907     labellg : 0,
31908     labelmd : 0,
31909     labelsm : 0,
31910     labelxs : 0,
31911     
31912     getAutoCreate : function()
31913     {   
31914         var managerWidget = {
31915             tag : 'div',
31916             cls : 'roo-document-manager',
31917             cn : [
31918                 {
31919                     tag : 'input',
31920                     cls : 'roo-document-manager-selector',
31921                     type : 'file'
31922                 },
31923                 {
31924                     tag : 'div',
31925                     cls : 'roo-document-manager-uploader',
31926                     cn : [
31927                         {
31928                             tag : 'div',
31929                             cls : 'roo-document-manager-upload-btn',
31930                             html : '<i class="fa fa-plus"></i>'
31931                         }
31932                     ]
31933                     
31934                 }
31935             ]
31936         };
31937         
31938         var content = [
31939             {
31940                 tag : 'div',
31941                 cls : 'column col-md-12',
31942                 cn : managerWidget
31943             }
31944         ];
31945         
31946         if(this.fieldLabel.length){
31947             
31948             content = [
31949                 {
31950                     tag : 'div',
31951                     cls : 'column col-md-12',
31952                     html : this.fieldLabel
31953                 },
31954                 {
31955                     tag : 'div',
31956                     cls : 'column col-md-12',
31957                     cn : managerWidget
31958                 }
31959             ];
31960
31961             if(this.labelAlign == 'left'){
31962                 content = [
31963                     {
31964                         tag : 'div',
31965                         cls : 'column',
31966                         html : this.fieldLabel
31967                     },
31968                     {
31969                         tag : 'div',
31970                         cls : 'column',
31971                         cn : managerWidget
31972                     }
31973                 ];
31974                 
31975                 if(this.labelWidth > 12){
31976                     content[0].style = "width: " + this.labelWidth + 'px';
31977                 }
31978
31979                 if(this.labelWidth < 13 && this.labelmd == 0){
31980                     this.labelmd = this.labelWidth;
31981                 }
31982
31983                 if(this.labellg > 0){
31984                     content[0].cls += ' col-lg-' + this.labellg;
31985                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31986                 }
31987
31988                 if(this.labelmd > 0){
31989                     content[0].cls += ' col-md-' + this.labelmd;
31990                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31991                 }
31992
31993                 if(this.labelsm > 0){
31994                     content[0].cls += ' col-sm-' + this.labelsm;
31995                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31996                 }
31997
31998                 if(this.labelxs > 0){
31999                     content[0].cls += ' col-xs-' + this.labelxs;
32000                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32001                 }
32002                 
32003             }
32004         }
32005         
32006         var cfg = {
32007             tag : 'div',
32008             cls : 'row clearfix',
32009             cn : content
32010         };
32011         
32012         return cfg;
32013         
32014     },
32015     
32016     initEvents : function()
32017     {
32018         this.managerEl = this.el.select('.roo-document-manager', true).first();
32019         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32020         
32021         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32022         this.selectorEl.hide();
32023         
32024         if(this.multiple){
32025             this.selectorEl.attr('multiple', 'multiple');
32026         }
32027         
32028         this.selectorEl.on('change', this.onFileSelected, this);
32029         
32030         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32031         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32032         
32033         this.uploader.on('click', this.onUploaderClick, this);
32034         
32035         this.renderProgressDialog();
32036         
32037         var _this = this;
32038         
32039         window.addEventListener("resize", function() { _this.refresh(); } );
32040         
32041         this.fireEvent('initial', this);
32042     },
32043     
32044     renderProgressDialog : function()
32045     {
32046         var _this = this;
32047         
32048         this.progressDialog = new Roo.bootstrap.Modal({
32049             cls : 'roo-document-manager-progress-dialog',
32050             allow_close : false,
32051             animate : false,
32052             title : '',
32053             buttons : [
32054                 {
32055                     name  :'cancel',
32056                     weight : 'danger',
32057                     html : 'Cancel'
32058                 }
32059             ], 
32060             listeners : { 
32061                 btnclick : function() {
32062                     _this.uploadCancel();
32063                     this.hide();
32064                 }
32065             }
32066         });
32067          
32068         this.progressDialog.render(Roo.get(document.body));
32069          
32070         this.progress = new Roo.bootstrap.Progress({
32071             cls : 'roo-document-manager-progress',
32072             active : true,
32073             striped : true
32074         });
32075         
32076         this.progress.render(this.progressDialog.getChildContainer());
32077         
32078         this.progressBar = new Roo.bootstrap.ProgressBar({
32079             cls : 'roo-document-manager-progress-bar',
32080             aria_valuenow : 0,
32081             aria_valuemin : 0,
32082             aria_valuemax : 12,
32083             panel : 'success'
32084         });
32085         
32086         this.progressBar.render(this.progress.getChildContainer());
32087     },
32088     
32089     onUploaderClick : function(e)
32090     {
32091         e.preventDefault();
32092      
32093         if(this.fireEvent('beforeselectfile', this) != false){
32094             this.selectorEl.dom.click();
32095         }
32096         
32097     },
32098     
32099     onFileSelected : function(e)
32100     {
32101         e.preventDefault();
32102         
32103         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32104             return;
32105         }
32106         
32107         Roo.each(this.selectorEl.dom.files, function(file){
32108             if(this.fireEvent('inspect', this, file) != false){
32109                 this.files.push(file);
32110             }
32111         }, this);
32112         
32113         this.queue();
32114         
32115     },
32116     
32117     queue : function()
32118     {
32119         this.selectorEl.dom.value = '';
32120         
32121         if(!this.files || !this.files.length){
32122             return;
32123         }
32124         
32125         if(this.boxes > 0 && this.files.length > this.boxes){
32126             this.files = this.files.slice(0, this.boxes);
32127         }
32128         
32129         this.uploader.show();
32130         
32131         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32132             this.uploader.hide();
32133         }
32134         
32135         var _this = this;
32136         
32137         var files = [];
32138         
32139         var docs = [];
32140         
32141         Roo.each(this.files, function(file){
32142             
32143             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32144                 var f = this.renderPreview(file);
32145                 files.push(f);
32146                 return;
32147             }
32148             
32149             if(file.type.indexOf('image') != -1){
32150                 this.delegates.push(
32151                     (function(){
32152                         _this.process(file);
32153                     }).createDelegate(this)
32154                 );
32155         
32156                 return;
32157             }
32158             
32159             docs.push(
32160                 (function(){
32161                     _this.process(file);
32162                 }).createDelegate(this)
32163             );
32164             
32165         }, this);
32166         
32167         this.files = files;
32168         
32169         this.delegates = this.delegates.concat(docs);
32170         
32171         if(!this.delegates.length){
32172             this.refresh();
32173             return;
32174         }
32175         
32176         this.progressBar.aria_valuemax = this.delegates.length;
32177         
32178         this.arrange();
32179         
32180         return;
32181     },
32182     
32183     arrange : function()
32184     {
32185         if(!this.delegates.length){
32186             this.progressDialog.hide();
32187             this.refresh();
32188             return;
32189         }
32190         
32191         var delegate = this.delegates.shift();
32192         
32193         this.progressDialog.show();
32194         
32195         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32196         
32197         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32198         
32199         delegate();
32200     },
32201     
32202     refresh : function()
32203     {
32204         this.uploader.show();
32205         
32206         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32207             this.uploader.hide();
32208         }
32209         
32210         Roo.isTouch ? this.closable(false) : this.closable(true);
32211         
32212         this.fireEvent('refresh', this);
32213     },
32214     
32215     onRemove : function(e, el, o)
32216     {
32217         e.preventDefault();
32218         
32219         this.fireEvent('remove', this, o);
32220         
32221     },
32222     
32223     remove : function(o)
32224     {
32225         var files = [];
32226         
32227         Roo.each(this.files, function(file){
32228             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32229                 files.push(file);
32230                 return;
32231             }
32232
32233             o.target.remove();
32234
32235         }, this);
32236         
32237         this.files = files;
32238         
32239         this.refresh();
32240     },
32241     
32242     clear : function()
32243     {
32244         Roo.each(this.files, function(file){
32245             if(!file.target){
32246                 return;
32247             }
32248             
32249             file.target.remove();
32250
32251         }, this);
32252         
32253         this.files = [];
32254         
32255         this.refresh();
32256     },
32257     
32258     onClick : function(e, el, o)
32259     {
32260         e.preventDefault();
32261         
32262         this.fireEvent('click', this, o);
32263         
32264     },
32265     
32266     closable : function(closable)
32267     {
32268         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32269             
32270             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32271             
32272             if(closable){
32273                 el.show();
32274                 return;
32275             }
32276             
32277             el.hide();
32278             
32279         }, this);
32280     },
32281     
32282     xhrOnLoad : function(xhr)
32283     {
32284         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32285             el.remove();
32286         }, this);
32287         
32288         if (xhr.readyState !== 4) {
32289             this.arrange();
32290             this.fireEvent('exception', this, xhr);
32291             return;
32292         }
32293
32294         var response = Roo.decode(xhr.responseText);
32295         
32296         if(!response.success){
32297             this.arrange();
32298             this.fireEvent('exception', this, xhr);
32299             return;
32300         }
32301         
32302         var file = this.renderPreview(response.data);
32303         
32304         this.files.push(file);
32305         
32306         this.arrange();
32307         
32308         this.fireEvent('afterupload', this, xhr);
32309         
32310     },
32311     
32312     xhrOnError : function(xhr)
32313     {
32314         Roo.log('xhr on error');
32315         
32316         var response = Roo.decode(xhr.responseText);
32317           
32318         Roo.log(response);
32319         
32320         this.arrange();
32321     },
32322     
32323     process : function(file)
32324     {
32325         if(this.fireEvent('process', this, file) !== false){
32326             if(this.editable && file.type.indexOf('image') != -1){
32327                 this.fireEvent('edit', this, file);
32328                 return;
32329             }
32330
32331             this.uploadStart(file, false);
32332
32333             return;
32334         }
32335         
32336     },
32337     
32338     uploadStart : function(file, crop)
32339     {
32340         this.xhr = new XMLHttpRequest();
32341         
32342         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32343             this.arrange();
32344             return;
32345         }
32346         
32347         file.xhr = this.xhr;
32348             
32349         this.managerEl.createChild({
32350             tag : 'div',
32351             cls : 'roo-document-manager-loading',
32352             cn : [
32353                 {
32354                     tag : 'div',
32355                     tooltip : file.name,
32356                     cls : 'roo-document-manager-thumb',
32357                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32358                 }
32359             ]
32360
32361         });
32362
32363         this.xhr.open(this.method, this.url, true);
32364         
32365         var headers = {
32366             "Accept": "application/json",
32367             "Cache-Control": "no-cache",
32368             "X-Requested-With": "XMLHttpRequest"
32369         };
32370         
32371         for (var headerName in headers) {
32372             var headerValue = headers[headerName];
32373             if (headerValue) {
32374                 this.xhr.setRequestHeader(headerName, headerValue);
32375             }
32376         }
32377         
32378         var _this = this;
32379         
32380         this.xhr.onload = function()
32381         {
32382             _this.xhrOnLoad(_this.xhr);
32383         }
32384         
32385         this.xhr.onerror = function()
32386         {
32387             _this.xhrOnError(_this.xhr);
32388         }
32389         
32390         var formData = new FormData();
32391
32392         formData.append('returnHTML', 'NO');
32393         
32394         if(crop){
32395             formData.append('crop', crop);
32396         }
32397         
32398         formData.append(this.paramName, file, file.name);
32399         
32400         var options = {
32401             file : file, 
32402             manually : false
32403         };
32404         
32405         if(this.fireEvent('prepare', this, formData, options) != false){
32406             
32407             if(options.manually){
32408                 return;
32409             }
32410             
32411             this.xhr.send(formData);
32412             return;
32413         };
32414         
32415         this.uploadCancel();
32416     },
32417     
32418     uploadCancel : function()
32419     {
32420         if (this.xhr) {
32421             this.xhr.abort();
32422         }
32423         
32424         this.delegates = [];
32425         
32426         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32427             el.remove();
32428         }, this);
32429         
32430         this.arrange();
32431     },
32432     
32433     renderPreview : function(file)
32434     {
32435         if(typeof(file.target) != 'undefined' && file.target){
32436             return file;
32437         }
32438         
32439         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32440         
32441         var previewEl = this.managerEl.createChild({
32442             tag : 'div',
32443             cls : 'roo-document-manager-preview',
32444             cn : [
32445                 {
32446                     tag : 'div',
32447                     tooltip : file[this.toolTipName],
32448                     cls : 'roo-document-manager-thumb',
32449                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32450                 },
32451                 {
32452                     tag : 'button',
32453                     cls : 'close',
32454                     html : '<i class="fa fa-times-circle"></i>'
32455                 }
32456             ]
32457         });
32458
32459         var close = previewEl.select('button.close', true).first();
32460
32461         close.on('click', this.onRemove, this, file);
32462
32463         file.target = previewEl;
32464
32465         var image = previewEl.select('img', true).first();
32466         
32467         var _this = this;
32468         
32469         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32470         
32471         image.on('click', this.onClick, this, file);
32472         
32473         this.fireEvent('previewrendered', this, file);
32474         
32475         return file;
32476         
32477     },
32478     
32479     onPreviewLoad : function(file, image)
32480     {
32481         if(typeof(file.target) == 'undefined' || !file.target){
32482             return;
32483         }
32484         
32485         var width = image.dom.naturalWidth || image.dom.width;
32486         var height = image.dom.naturalHeight || image.dom.height;
32487         
32488         if(!this.previewResize) {
32489             return;
32490         }
32491         
32492         if(width > height){
32493             file.target.addClass('wide');
32494             return;
32495         }
32496         
32497         file.target.addClass('tall');
32498         return;
32499         
32500     },
32501     
32502     uploadFromSource : function(file, crop)
32503     {
32504         this.xhr = new XMLHttpRequest();
32505         
32506         this.managerEl.createChild({
32507             tag : 'div',
32508             cls : 'roo-document-manager-loading',
32509             cn : [
32510                 {
32511                     tag : 'div',
32512                     tooltip : file.name,
32513                     cls : 'roo-document-manager-thumb',
32514                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32515                 }
32516             ]
32517
32518         });
32519
32520         this.xhr.open(this.method, this.url, true);
32521         
32522         var headers = {
32523             "Accept": "application/json",
32524             "Cache-Control": "no-cache",
32525             "X-Requested-With": "XMLHttpRequest"
32526         };
32527         
32528         for (var headerName in headers) {
32529             var headerValue = headers[headerName];
32530             if (headerValue) {
32531                 this.xhr.setRequestHeader(headerName, headerValue);
32532             }
32533         }
32534         
32535         var _this = this;
32536         
32537         this.xhr.onload = function()
32538         {
32539             _this.xhrOnLoad(_this.xhr);
32540         }
32541         
32542         this.xhr.onerror = function()
32543         {
32544             _this.xhrOnError(_this.xhr);
32545         }
32546         
32547         var formData = new FormData();
32548
32549         formData.append('returnHTML', 'NO');
32550         
32551         formData.append('crop', crop);
32552         
32553         if(typeof(file.filename) != 'undefined'){
32554             formData.append('filename', file.filename);
32555         }
32556         
32557         if(typeof(file.mimetype) != 'undefined'){
32558             formData.append('mimetype', file.mimetype);
32559         }
32560         
32561         Roo.log(formData);
32562         
32563         if(this.fireEvent('prepare', this, formData) != false){
32564             this.xhr.send(formData);
32565         };
32566     }
32567 });
32568
32569 /*
32570 * Licence: LGPL
32571 */
32572
32573 /**
32574  * @class Roo.bootstrap.DocumentViewer
32575  * @extends Roo.bootstrap.Component
32576  * Bootstrap DocumentViewer class
32577  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32578  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32579  * 
32580  * @constructor
32581  * Create a new DocumentViewer
32582  * @param {Object} config The config object
32583  */
32584
32585 Roo.bootstrap.DocumentViewer = function(config){
32586     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32587     
32588     this.addEvents({
32589         /**
32590          * @event initial
32591          * Fire after initEvent
32592          * @param {Roo.bootstrap.DocumentViewer} this
32593          */
32594         "initial" : true,
32595         /**
32596          * @event click
32597          * Fire after click
32598          * @param {Roo.bootstrap.DocumentViewer} this
32599          */
32600         "click" : true,
32601         /**
32602          * @event download
32603          * Fire after download button
32604          * @param {Roo.bootstrap.DocumentViewer} this
32605          */
32606         "download" : true,
32607         /**
32608          * @event trash
32609          * Fire after trash button
32610          * @param {Roo.bootstrap.DocumentViewer} this
32611          */
32612         "trash" : true
32613         
32614     });
32615 };
32616
32617 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32618     
32619     showDownload : true,
32620     
32621     showTrash : true,
32622     
32623     getAutoCreate : function()
32624     {
32625         var cfg = {
32626             tag : 'div',
32627             cls : 'roo-document-viewer',
32628             cn : [
32629                 {
32630                     tag : 'div',
32631                     cls : 'roo-document-viewer-body',
32632                     cn : [
32633                         {
32634                             tag : 'div',
32635                             cls : 'roo-document-viewer-thumb',
32636                             cn : [
32637                                 {
32638                                     tag : 'img',
32639                                     cls : 'roo-document-viewer-image'
32640                                 }
32641                             ]
32642                         }
32643                     ]
32644                 },
32645                 {
32646                     tag : 'div',
32647                     cls : 'roo-document-viewer-footer',
32648                     cn : {
32649                         tag : 'div',
32650                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32651                         cn : [
32652                             {
32653                                 tag : 'div',
32654                                 cls : 'btn-group roo-document-viewer-download',
32655                                 cn : [
32656                                     {
32657                                         tag : 'button',
32658                                         cls : 'btn btn-default',
32659                                         html : '<i class="fa fa-download"></i>'
32660                                     }
32661                                 ]
32662                             },
32663                             {
32664                                 tag : 'div',
32665                                 cls : 'btn-group roo-document-viewer-trash',
32666                                 cn : [
32667                                     {
32668                                         tag : 'button',
32669                                         cls : 'btn btn-default',
32670                                         html : '<i class="fa fa-trash"></i>'
32671                                     }
32672                                 ]
32673                             }
32674                         ]
32675                     }
32676                 }
32677             ]
32678         };
32679         
32680         return cfg;
32681     },
32682     
32683     initEvents : function()
32684     {
32685         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32686         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32687         
32688         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32689         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32690         
32691         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32692         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32693         
32694         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32695         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32696         
32697         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32698         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32699         
32700         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32701         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32702         
32703         this.bodyEl.on('click', this.onClick, this);
32704         this.downloadBtn.on('click', this.onDownload, this);
32705         this.trashBtn.on('click', this.onTrash, this);
32706         
32707         this.downloadBtn.hide();
32708         this.trashBtn.hide();
32709         
32710         if(this.showDownload){
32711             this.downloadBtn.show();
32712         }
32713         
32714         if(this.showTrash){
32715             this.trashBtn.show();
32716         }
32717         
32718         if(!this.showDownload && !this.showTrash) {
32719             this.footerEl.hide();
32720         }
32721         
32722     },
32723     
32724     initial : function()
32725     {
32726         this.fireEvent('initial', this);
32727         
32728     },
32729     
32730     onClick : function(e)
32731     {
32732         e.preventDefault();
32733         
32734         this.fireEvent('click', this);
32735     },
32736     
32737     onDownload : function(e)
32738     {
32739         e.preventDefault();
32740         
32741         this.fireEvent('download', this);
32742     },
32743     
32744     onTrash : function(e)
32745     {
32746         e.preventDefault();
32747         
32748         this.fireEvent('trash', this);
32749     }
32750     
32751 });
32752 /*
32753  * - LGPL
32754  *
32755  * nav progress bar
32756  * 
32757  */
32758
32759 /**
32760  * @class Roo.bootstrap.NavProgressBar
32761  * @extends Roo.bootstrap.Component
32762  * Bootstrap NavProgressBar class
32763  * 
32764  * @constructor
32765  * Create a new nav progress bar
32766  * @param {Object} config The config object
32767  */
32768
32769 Roo.bootstrap.NavProgressBar = function(config){
32770     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32771
32772     this.bullets = this.bullets || [];
32773    
32774 //    Roo.bootstrap.NavProgressBar.register(this);
32775      this.addEvents({
32776         /**
32777              * @event changed
32778              * Fires when the active item changes
32779              * @param {Roo.bootstrap.NavProgressBar} this
32780              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32781              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32782          */
32783         'changed': true
32784      });
32785     
32786 };
32787
32788 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32789     
32790     bullets : [],
32791     barItems : [],
32792     
32793     getAutoCreate : function()
32794     {
32795         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32796         
32797         cfg = {
32798             tag : 'div',
32799             cls : 'roo-navigation-bar-group',
32800             cn : [
32801                 {
32802                     tag : 'div',
32803                     cls : 'roo-navigation-top-bar'
32804                 },
32805                 {
32806                     tag : 'div',
32807                     cls : 'roo-navigation-bullets-bar',
32808                     cn : [
32809                         {
32810                             tag : 'ul',
32811                             cls : 'roo-navigation-bar'
32812                         }
32813                     ]
32814                 },
32815                 
32816                 {
32817                     tag : 'div',
32818                     cls : 'roo-navigation-bottom-bar'
32819                 }
32820             ]
32821             
32822         };
32823         
32824         return cfg;
32825         
32826     },
32827     
32828     initEvents: function() 
32829     {
32830         
32831     },
32832     
32833     onRender : function(ct, position) 
32834     {
32835         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32836         
32837         if(this.bullets.length){
32838             Roo.each(this.bullets, function(b){
32839                this.addItem(b);
32840             }, this);
32841         }
32842         
32843         this.format();
32844         
32845     },
32846     
32847     addItem : function(cfg)
32848     {
32849         var item = new Roo.bootstrap.NavProgressItem(cfg);
32850         
32851         item.parentId = this.id;
32852         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32853         
32854         if(cfg.html){
32855             var top = new Roo.bootstrap.Element({
32856                 tag : 'div',
32857                 cls : 'roo-navigation-bar-text'
32858             });
32859             
32860             var bottom = new Roo.bootstrap.Element({
32861                 tag : 'div',
32862                 cls : 'roo-navigation-bar-text'
32863             });
32864             
32865             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32866             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32867             
32868             var topText = new Roo.bootstrap.Element({
32869                 tag : 'span',
32870                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32871             });
32872             
32873             var bottomText = new Roo.bootstrap.Element({
32874                 tag : 'span',
32875                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32876             });
32877             
32878             topText.onRender(top.el, null);
32879             bottomText.onRender(bottom.el, null);
32880             
32881             item.topEl = top;
32882             item.bottomEl = bottom;
32883         }
32884         
32885         this.barItems.push(item);
32886         
32887         return item;
32888     },
32889     
32890     getActive : function()
32891     {
32892         var active = false;
32893         
32894         Roo.each(this.barItems, function(v){
32895             
32896             if (!v.isActive()) {
32897                 return;
32898             }
32899             
32900             active = v;
32901             return false;
32902             
32903         });
32904         
32905         return active;
32906     },
32907     
32908     setActiveItem : function(item)
32909     {
32910         var prev = false;
32911         
32912         Roo.each(this.barItems, function(v){
32913             if (v.rid == item.rid) {
32914                 return ;
32915             }
32916             
32917             if (v.isActive()) {
32918                 v.setActive(false);
32919                 prev = v;
32920             }
32921         });
32922
32923         item.setActive(true);
32924         
32925         this.fireEvent('changed', this, item, prev);
32926     },
32927     
32928     getBarItem: function(rid)
32929     {
32930         var ret = false;
32931         
32932         Roo.each(this.barItems, function(e) {
32933             if (e.rid != rid) {
32934                 return;
32935             }
32936             
32937             ret =  e;
32938             return false;
32939         });
32940         
32941         return ret;
32942     },
32943     
32944     indexOfItem : function(item)
32945     {
32946         var index = false;
32947         
32948         Roo.each(this.barItems, function(v, i){
32949             
32950             if (v.rid != item.rid) {
32951                 return;
32952             }
32953             
32954             index = i;
32955             return false
32956         });
32957         
32958         return index;
32959     },
32960     
32961     setActiveNext : function()
32962     {
32963         var i = this.indexOfItem(this.getActive());
32964         
32965         if (i > this.barItems.length) {
32966             return;
32967         }
32968         
32969         this.setActiveItem(this.barItems[i+1]);
32970     },
32971     
32972     setActivePrev : function()
32973     {
32974         var i = this.indexOfItem(this.getActive());
32975         
32976         if (i  < 1) {
32977             return;
32978         }
32979         
32980         this.setActiveItem(this.barItems[i-1]);
32981     },
32982     
32983     format : function()
32984     {
32985         if(!this.barItems.length){
32986             return;
32987         }
32988      
32989         var width = 100 / this.barItems.length;
32990         
32991         Roo.each(this.barItems, function(i){
32992             i.el.setStyle('width', width + '%');
32993             i.topEl.el.setStyle('width', width + '%');
32994             i.bottomEl.el.setStyle('width', width + '%');
32995         }, this);
32996         
32997     }
32998     
32999 });
33000 /*
33001  * - LGPL
33002  *
33003  * Nav Progress Item
33004  * 
33005  */
33006
33007 /**
33008  * @class Roo.bootstrap.NavProgressItem
33009  * @extends Roo.bootstrap.Component
33010  * Bootstrap NavProgressItem class
33011  * @cfg {String} rid the reference id
33012  * @cfg {Boolean} active (true|false) Is item active default false
33013  * @cfg {Boolean} disabled (true|false) Is item active default false
33014  * @cfg {String} html
33015  * @cfg {String} position (top|bottom) text position default bottom
33016  * @cfg {String} icon show icon instead of number
33017  * 
33018  * @constructor
33019  * Create a new NavProgressItem
33020  * @param {Object} config The config object
33021  */
33022 Roo.bootstrap.NavProgressItem = function(config){
33023     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33024     this.addEvents({
33025         // raw events
33026         /**
33027          * @event click
33028          * The raw click event for the entire grid.
33029          * @param {Roo.bootstrap.NavProgressItem} this
33030          * @param {Roo.EventObject} e
33031          */
33032         "click" : true
33033     });
33034    
33035 };
33036
33037 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33038     
33039     rid : '',
33040     active : false,
33041     disabled : false,
33042     html : '',
33043     position : 'bottom',
33044     icon : false,
33045     
33046     getAutoCreate : function()
33047     {
33048         var iconCls = 'roo-navigation-bar-item-icon';
33049         
33050         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33051         
33052         var cfg = {
33053             tag: 'li',
33054             cls: 'roo-navigation-bar-item',
33055             cn : [
33056                 {
33057                     tag : 'i',
33058                     cls : iconCls
33059                 }
33060             ]
33061         };
33062         
33063         if(this.active){
33064             cfg.cls += ' active';
33065         }
33066         if(this.disabled){
33067             cfg.cls += ' disabled';
33068         }
33069         
33070         return cfg;
33071     },
33072     
33073     disable : function()
33074     {
33075         this.setDisabled(true);
33076     },
33077     
33078     enable : function()
33079     {
33080         this.setDisabled(false);
33081     },
33082     
33083     initEvents: function() 
33084     {
33085         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33086         
33087         this.iconEl.on('click', this.onClick, this);
33088     },
33089     
33090     onClick : function(e)
33091     {
33092         e.preventDefault();
33093         
33094         if(this.disabled){
33095             return;
33096         }
33097         
33098         if(this.fireEvent('click', this, e) === false){
33099             return;
33100         };
33101         
33102         this.parent().setActiveItem(this);
33103     },
33104     
33105     isActive: function () 
33106     {
33107         return this.active;
33108     },
33109     
33110     setActive : function(state)
33111     {
33112         if(this.active == state){
33113             return;
33114         }
33115         
33116         this.active = state;
33117         
33118         if (state) {
33119             this.el.addClass('active');
33120             return;
33121         }
33122         
33123         this.el.removeClass('active');
33124         
33125         return;
33126     },
33127     
33128     setDisabled : function(state)
33129     {
33130         if(this.disabled == state){
33131             return;
33132         }
33133         
33134         this.disabled = state;
33135         
33136         if (state) {
33137             this.el.addClass('disabled');
33138             return;
33139         }
33140         
33141         this.el.removeClass('disabled');
33142     },
33143     
33144     tooltipEl : function()
33145     {
33146         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33147     }
33148 });
33149  
33150
33151  /*
33152  * - LGPL
33153  *
33154  * FieldLabel
33155  * 
33156  */
33157
33158 /**
33159  * @class Roo.bootstrap.FieldLabel
33160  * @extends Roo.bootstrap.Component
33161  * Bootstrap FieldLabel class
33162  * @cfg {String} html contents of the element
33163  * @cfg {String} tag tag of the element default label
33164  * @cfg {String} cls class of the element
33165  * @cfg {String} target label target 
33166  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33167  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33168  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33169  * @cfg {String} iconTooltip default "This field is required"
33170  * @cfg {String} indicatorpos (left|right) default left
33171  * 
33172  * @constructor
33173  * Create a new FieldLabel
33174  * @param {Object} config The config object
33175  */
33176
33177 Roo.bootstrap.FieldLabel = function(config){
33178     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33179     
33180     this.addEvents({
33181             /**
33182              * @event invalid
33183              * Fires after the field has been marked as invalid.
33184              * @param {Roo.form.FieldLabel} this
33185              * @param {String} msg The validation message
33186              */
33187             invalid : true,
33188             /**
33189              * @event valid
33190              * Fires after the field has been validated with no errors.
33191              * @param {Roo.form.FieldLabel} this
33192              */
33193             valid : true
33194         });
33195 };
33196
33197 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33198     
33199     tag: 'label',
33200     cls: '',
33201     html: '',
33202     target: '',
33203     allowBlank : true,
33204     invalidClass : 'has-warning',
33205     validClass : 'has-success',
33206     iconTooltip : 'This field is required',
33207     indicatorpos : 'left',
33208     
33209     getAutoCreate : function(){
33210         
33211         var cls = "";
33212         if (!this.allowBlank) {
33213             cls  = "visible";
33214         }
33215         
33216         var cfg = {
33217             tag : this.tag,
33218             cls : 'roo-bootstrap-field-label ' + this.cls,
33219             for : this.target,
33220             cn : [
33221                 {
33222                     tag : 'i',
33223                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33224                     tooltip : this.iconTooltip
33225                 },
33226                 {
33227                     tag : 'span',
33228                     html : this.html
33229                 }
33230             ] 
33231         };
33232         
33233         if(this.indicatorpos == 'right'){
33234             var cfg = {
33235                 tag : this.tag,
33236                 cls : 'roo-bootstrap-field-label ' + this.cls,
33237                 for : this.target,
33238                 cn : [
33239                     {
33240                         tag : 'span',
33241                         html : this.html
33242                     },
33243                     {
33244                         tag : 'i',
33245                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33246                         tooltip : this.iconTooltip
33247                     }
33248                 ] 
33249             };
33250         }
33251         
33252         return cfg;
33253     },
33254     
33255     initEvents: function() 
33256     {
33257         Roo.bootstrap.Element.superclass.initEvents.call(this);
33258         
33259         this.indicator = this.indicatorEl();
33260         
33261         if(this.indicator){
33262             this.indicator.removeClass('visible');
33263             this.indicator.addClass('invisible');
33264         }
33265         
33266         Roo.bootstrap.FieldLabel.register(this);
33267     },
33268     
33269     indicatorEl : function()
33270     {
33271         var indicator = this.el.select('i.roo-required-indicator',true).first();
33272         
33273         if(!indicator){
33274             return false;
33275         }
33276         
33277         return indicator;
33278         
33279     },
33280     
33281     /**
33282      * Mark this field as valid
33283      */
33284     markValid : function()
33285     {
33286         if(this.indicator){
33287             this.indicator.removeClass('visible');
33288             this.indicator.addClass('invisible');
33289         }
33290         if (Roo.bootstrap.version == 3) {
33291             this.el.removeClass(this.invalidClass);
33292             this.el.addClass(this.validClass);
33293         } else {
33294             this.el.removeClass('is-invalid');
33295             this.el.addClass('is-valid');
33296         }
33297         
33298         
33299         this.fireEvent('valid', this);
33300     },
33301     
33302     /**
33303      * Mark this field as invalid
33304      * @param {String} msg The validation message
33305      */
33306     markInvalid : function(msg)
33307     {
33308         if(this.indicator){
33309             this.indicator.removeClass('invisible');
33310             this.indicator.addClass('visible');
33311         }
33312           if (Roo.bootstrap.version == 3) {
33313             this.el.removeClass(this.validClass);
33314             this.el.addClass(this.invalidClass);
33315         } else {
33316             this.el.removeClass('is-valid');
33317             this.el.addClass('is-invalid');
33318         }
33319         
33320         
33321         this.fireEvent('invalid', this, msg);
33322     }
33323     
33324    
33325 });
33326
33327 Roo.apply(Roo.bootstrap.FieldLabel, {
33328     
33329     groups: {},
33330     
33331      /**
33332     * register a FieldLabel Group
33333     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33334     */
33335     register : function(label)
33336     {
33337         if(this.groups.hasOwnProperty(label.target)){
33338             return;
33339         }
33340      
33341         this.groups[label.target] = label;
33342         
33343     },
33344     /**
33345     * fetch a FieldLabel Group based on the target
33346     * @param {string} target
33347     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33348     */
33349     get: function(target) {
33350         if (typeof(this.groups[target]) == 'undefined') {
33351             return false;
33352         }
33353         
33354         return this.groups[target] ;
33355     }
33356 });
33357
33358  
33359
33360  /*
33361  * - LGPL
33362  *
33363  * page DateSplitField.
33364  * 
33365  */
33366
33367
33368 /**
33369  * @class Roo.bootstrap.DateSplitField
33370  * @extends Roo.bootstrap.Component
33371  * Bootstrap DateSplitField class
33372  * @cfg {string} fieldLabel - the label associated
33373  * @cfg {Number} labelWidth set the width of label (0-12)
33374  * @cfg {String} labelAlign (top|left)
33375  * @cfg {Boolean} dayAllowBlank (true|false) default false
33376  * @cfg {Boolean} monthAllowBlank (true|false) default false
33377  * @cfg {Boolean} yearAllowBlank (true|false) default false
33378  * @cfg {string} dayPlaceholder 
33379  * @cfg {string} monthPlaceholder
33380  * @cfg {string} yearPlaceholder
33381  * @cfg {string} dayFormat default 'd'
33382  * @cfg {string} monthFormat default 'm'
33383  * @cfg {string} yearFormat default 'Y'
33384  * @cfg {Number} labellg set the width of label (1-12)
33385  * @cfg {Number} labelmd set the width of label (1-12)
33386  * @cfg {Number} labelsm set the width of label (1-12)
33387  * @cfg {Number} labelxs set the width of label (1-12)
33388
33389  *     
33390  * @constructor
33391  * Create a new DateSplitField
33392  * @param {Object} config The config object
33393  */
33394
33395 Roo.bootstrap.DateSplitField = function(config){
33396     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33397     
33398     this.addEvents({
33399         // raw events
33400          /**
33401          * @event years
33402          * getting the data of years
33403          * @param {Roo.bootstrap.DateSplitField} this
33404          * @param {Object} years
33405          */
33406         "years" : true,
33407         /**
33408          * @event days
33409          * getting the data of days
33410          * @param {Roo.bootstrap.DateSplitField} this
33411          * @param {Object} days
33412          */
33413         "days" : true,
33414         /**
33415          * @event invalid
33416          * Fires after the field has been marked as invalid.
33417          * @param {Roo.form.Field} this
33418          * @param {String} msg The validation message
33419          */
33420         invalid : true,
33421        /**
33422          * @event valid
33423          * Fires after the field has been validated with no errors.
33424          * @param {Roo.form.Field} this
33425          */
33426         valid : true
33427     });
33428 };
33429
33430 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33431     
33432     fieldLabel : '',
33433     labelAlign : 'top',
33434     labelWidth : 3,
33435     dayAllowBlank : false,
33436     monthAllowBlank : false,
33437     yearAllowBlank : false,
33438     dayPlaceholder : '',
33439     monthPlaceholder : '',
33440     yearPlaceholder : '',
33441     dayFormat : 'd',
33442     monthFormat : 'm',
33443     yearFormat : 'Y',
33444     isFormField : true,
33445     labellg : 0,
33446     labelmd : 0,
33447     labelsm : 0,
33448     labelxs : 0,
33449     
33450     getAutoCreate : function()
33451     {
33452         var cfg = {
33453             tag : 'div',
33454             cls : 'row roo-date-split-field-group',
33455             cn : [
33456                 {
33457                     tag : 'input',
33458                     type : 'hidden',
33459                     cls : 'form-hidden-field roo-date-split-field-group-value',
33460                     name : this.name
33461                 }
33462             ]
33463         };
33464         
33465         var labelCls = 'col-md-12';
33466         var contentCls = 'col-md-4';
33467         
33468         if(this.fieldLabel){
33469             
33470             var label = {
33471                 tag : 'div',
33472                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33473                 cn : [
33474                     {
33475                         tag : 'label',
33476                         html : this.fieldLabel
33477                     }
33478                 ]
33479             };
33480             
33481             if(this.labelAlign == 'left'){
33482             
33483                 if(this.labelWidth > 12){
33484                     label.style = "width: " + this.labelWidth + 'px';
33485                 }
33486
33487                 if(this.labelWidth < 13 && this.labelmd == 0){
33488                     this.labelmd = this.labelWidth;
33489                 }
33490
33491                 if(this.labellg > 0){
33492                     labelCls = ' col-lg-' + this.labellg;
33493                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33494                 }
33495
33496                 if(this.labelmd > 0){
33497                     labelCls = ' col-md-' + this.labelmd;
33498                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33499                 }
33500
33501                 if(this.labelsm > 0){
33502                     labelCls = ' col-sm-' + this.labelsm;
33503                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33504                 }
33505
33506                 if(this.labelxs > 0){
33507                     labelCls = ' col-xs-' + this.labelxs;
33508                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33509                 }
33510             }
33511             
33512             label.cls += ' ' + labelCls;
33513             
33514             cfg.cn.push(label);
33515         }
33516         
33517         Roo.each(['day', 'month', 'year'], function(t){
33518             cfg.cn.push({
33519                 tag : 'div',
33520                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33521             });
33522         }, this);
33523         
33524         return cfg;
33525     },
33526     
33527     inputEl: function ()
33528     {
33529         return this.el.select('.roo-date-split-field-group-value', true).first();
33530     },
33531     
33532     onRender : function(ct, position) 
33533     {
33534         var _this = this;
33535         
33536         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33537         
33538         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33539         
33540         this.dayField = new Roo.bootstrap.ComboBox({
33541             allowBlank : this.dayAllowBlank,
33542             alwaysQuery : true,
33543             displayField : 'value',
33544             editable : false,
33545             fieldLabel : '',
33546             forceSelection : true,
33547             mode : 'local',
33548             placeholder : this.dayPlaceholder,
33549             selectOnFocus : true,
33550             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33551             triggerAction : 'all',
33552             typeAhead : true,
33553             valueField : 'value',
33554             store : new Roo.data.SimpleStore({
33555                 data : (function() {    
33556                     var days = [];
33557                     _this.fireEvent('days', _this, days);
33558                     return days;
33559                 })(),
33560                 fields : [ 'value' ]
33561             }),
33562             listeners : {
33563                 select : function (_self, record, index)
33564                 {
33565                     _this.setValue(_this.getValue());
33566                 }
33567             }
33568         });
33569
33570         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33571         
33572         this.monthField = new Roo.bootstrap.MonthField({
33573             after : '<i class=\"fa fa-calendar\"></i>',
33574             allowBlank : this.monthAllowBlank,
33575             placeholder : this.monthPlaceholder,
33576             readOnly : true,
33577             listeners : {
33578                 render : function (_self)
33579                 {
33580                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33581                         e.preventDefault();
33582                         _self.focus();
33583                     });
33584                 },
33585                 select : function (_self, oldvalue, newvalue)
33586                 {
33587                     _this.setValue(_this.getValue());
33588                 }
33589             }
33590         });
33591         
33592         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33593         
33594         this.yearField = new Roo.bootstrap.ComboBox({
33595             allowBlank : this.yearAllowBlank,
33596             alwaysQuery : true,
33597             displayField : 'value',
33598             editable : false,
33599             fieldLabel : '',
33600             forceSelection : true,
33601             mode : 'local',
33602             placeholder : this.yearPlaceholder,
33603             selectOnFocus : true,
33604             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33605             triggerAction : 'all',
33606             typeAhead : true,
33607             valueField : 'value',
33608             store : new Roo.data.SimpleStore({
33609                 data : (function() {
33610                     var years = [];
33611                     _this.fireEvent('years', _this, years);
33612                     return years;
33613                 })(),
33614                 fields : [ 'value' ]
33615             }),
33616             listeners : {
33617                 select : function (_self, record, index)
33618                 {
33619                     _this.setValue(_this.getValue());
33620                 }
33621             }
33622         });
33623
33624         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33625     },
33626     
33627     setValue : function(v, format)
33628     {
33629         this.inputEl.dom.value = v;
33630         
33631         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33632         
33633         var d = Date.parseDate(v, f);
33634         
33635         if(!d){
33636             this.validate();
33637             return;
33638         }
33639         
33640         this.setDay(d.format(this.dayFormat));
33641         this.setMonth(d.format(this.monthFormat));
33642         this.setYear(d.format(this.yearFormat));
33643         
33644         this.validate();
33645         
33646         return;
33647     },
33648     
33649     setDay : function(v)
33650     {
33651         this.dayField.setValue(v);
33652         this.inputEl.dom.value = this.getValue();
33653         this.validate();
33654         return;
33655     },
33656     
33657     setMonth : function(v)
33658     {
33659         this.monthField.setValue(v, true);
33660         this.inputEl.dom.value = this.getValue();
33661         this.validate();
33662         return;
33663     },
33664     
33665     setYear : function(v)
33666     {
33667         this.yearField.setValue(v);
33668         this.inputEl.dom.value = this.getValue();
33669         this.validate();
33670         return;
33671     },
33672     
33673     getDay : function()
33674     {
33675         return this.dayField.getValue();
33676     },
33677     
33678     getMonth : function()
33679     {
33680         return this.monthField.getValue();
33681     },
33682     
33683     getYear : function()
33684     {
33685         return this.yearField.getValue();
33686     },
33687     
33688     getValue : function()
33689     {
33690         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33691         
33692         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33693         
33694         return date;
33695     },
33696     
33697     reset : function()
33698     {
33699         this.setDay('');
33700         this.setMonth('');
33701         this.setYear('');
33702         this.inputEl.dom.value = '';
33703         this.validate();
33704         return;
33705     },
33706     
33707     validate : function()
33708     {
33709         var d = this.dayField.validate();
33710         var m = this.monthField.validate();
33711         var y = this.yearField.validate();
33712         
33713         var valid = true;
33714         
33715         if(
33716                 (!this.dayAllowBlank && !d) ||
33717                 (!this.monthAllowBlank && !m) ||
33718                 (!this.yearAllowBlank && !y)
33719         ){
33720             valid = false;
33721         }
33722         
33723         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33724             return valid;
33725         }
33726         
33727         if(valid){
33728             this.markValid();
33729             return valid;
33730         }
33731         
33732         this.markInvalid();
33733         
33734         return valid;
33735     },
33736     
33737     markValid : function()
33738     {
33739         
33740         var label = this.el.select('label', true).first();
33741         var icon = this.el.select('i.fa-star', true).first();
33742
33743         if(label && icon){
33744             icon.remove();
33745         }
33746         
33747         this.fireEvent('valid', this);
33748     },
33749     
33750      /**
33751      * Mark this field as invalid
33752      * @param {String} msg The validation message
33753      */
33754     markInvalid : function(msg)
33755     {
33756         
33757         var label = this.el.select('label', true).first();
33758         var icon = this.el.select('i.fa-star', true).first();
33759
33760         if(label && !icon){
33761             this.el.select('.roo-date-split-field-label', true).createChild({
33762                 tag : 'i',
33763                 cls : 'text-danger fa fa-lg fa-star',
33764                 tooltip : 'This field is required',
33765                 style : 'margin-right:5px;'
33766             }, label, true);
33767         }
33768         
33769         this.fireEvent('invalid', this, msg);
33770     },
33771     
33772     clearInvalid : function()
33773     {
33774         var label = this.el.select('label', true).first();
33775         var icon = this.el.select('i.fa-star', true).first();
33776
33777         if(label && icon){
33778             icon.remove();
33779         }
33780         
33781         this.fireEvent('valid', this);
33782     },
33783     
33784     getName: function()
33785     {
33786         return this.name;
33787     }
33788     
33789 });
33790
33791  /**
33792  *
33793  * This is based on 
33794  * http://masonry.desandro.com
33795  *
33796  * The idea is to render all the bricks based on vertical width...
33797  *
33798  * The original code extends 'outlayer' - we might need to use that....
33799  * 
33800  */
33801
33802
33803 /**
33804  * @class Roo.bootstrap.LayoutMasonry
33805  * @extends Roo.bootstrap.Component
33806  * Bootstrap Layout Masonry class
33807  * 
33808  * @constructor
33809  * Create a new Element
33810  * @param {Object} config The config object
33811  */
33812
33813 Roo.bootstrap.LayoutMasonry = function(config){
33814     
33815     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33816     
33817     this.bricks = [];
33818     
33819     Roo.bootstrap.LayoutMasonry.register(this);
33820     
33821     this.addEvents({
33822         // raw events
33823         /**
33824          * @event layout
33825          * Fire after layout the items
33826          * @param {Roo.bootstrap.LayoutMasonry} this
33827          * @param {Roo.EventObject} e
33828          */
33829         "layout" : true
33830     });
33831     
33832 };
33833
33834 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33835     
33836     /**
33837      * @cfg {Boolean} isLayoutInstant = no animation?
33838      */   
33839     isLayoutInstant : false, // needed?
33840    
33841     /**
33842      * @cfg {Number} boxWidth  width of the columns
33843      */   
33844     boxWidth : 450,
33845     
33846       /**
33847      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33848      */   
33849     boxHeight : 0,
33850     
33851     /**
33852      * @cfg {Number} padWidth padding below box..
33853      */   
33854     padWidth : 10, 
33855     
33856     /**
33857      * @cfg {Number} gutter gutter width..
33858      */   
33859     gutter : 10,
33860     
33861      /**
33862      * @cfg {Number} maxCols maximum number of columns
33863      */   
33864     
33865     maxCols: 0,
33866     
33867     /**
33868      * @cfg {Boolean} isAutoInitial defalut true
33869      */   
33870     isAutoInitial : true, 
33871     
33872     containerWidth: 0,
33873     
33874     /**
33875      * @cfg {Boolean} isHorizontal defalut false
33876      */   
33877     isHorizontal : false, 
33878
33879     currentSize : null,
33880     
33881     tag: 'div',
33882     
33883     cls: '',
33884     
33885     bricks: null, //CompositeElement
33886     
33887     cols : 1,
33888     
33889     _isLayoutInited : false,
33890     
33891 //    isAlternative : false, // only use for vertical layout...
33892     
33893     /**
33894      * @cfg {Number} alternativePadWidth padding below box..
33895      */   
33896     alternativePadWidth : 50,
33897     
33898     selectedBrick : [],
33899     
33900     getAutoCreate : function(){
33901         
33902         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33903         
33904         var cfg = {
33905             tag: this.tag,
33906             cls: 'blog-masonary-wrapper ' + this.cls,
33907             cn : {
33908                 cls : 'mas-boxes masonary'
33909             }
33910         };
33911         
33912         return cfg;
33913     },
33914     
33915     getChildContainer: function( )
33916     {
33917         if (this.boxesEl) {
33918             return this.boxesEl;
33919         }
33920         
33921         this.boxesEl = this.el.select('.mas-boxes').first();
33922         
33923         return this.boxesEl;
33924     },
33925     
33926     
33927     initEvents : function()
33928     {
33929         var _this = this;
33930         
33931         if(this.isAutoInitial){
33932             Roo.log('hook children rendered');
33933             this.on('childrenrendered', function() {
33934                 Roo.log('children rendered');
33935                 _this.initial();
33936             } ,this);
33937         }
33938     },
33939     
33940     initial : function()
33941     {
33942         this.selectedBrick = [];
33943         
33944         this.currentSize = this.el.getBox(true);
33945         
33946         Roo.EventManager.onWindowResize(this.resize, this); 
33947
33948         if(!this.isAutoInitial){
33949             this.layout();
33950             return;
33951         }
33952         
33953         this.layout();
33954         
33955         return;
33956         //this.layout.defer(500,this);
33957         
33958     },
33959     
33960     resize : function()
33961     {
33962         var cs = this.el.getBox(true);
33963         
33964         if (
33965                 this.currentSize.width == cs.width && 
33966                 this.currentSize.x == cs.x && 
33967                 this.currentSize.height == cs.height && 
33968                 this.currentSize.y == cs.y 
33969         ) {
33970             Roo.log("no change in with or X or Y");
33971             return;
33972         }
33973         
33974         this.currentSize = cs;
33975         
33976         this.layout();
33977         
33978     },
33979     
33980     layout : function()
33981     {   
33982         this._resetLayout();
33983         
33984         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33985         
33986         this.layoutItems( isInstant );
33987       
33988         this._isLayoutInited = true;
33989         
33990         this.fireEvent('layout', this);
33991         
33992     },
33993     
33994     _resetLayout : function()
33995     {
33996         if(this.isHorizontal){
33997             this.horizontalMeasureColumns();
33998             return;
33999         }
34000         
34001         this.verticalMeasureColumns();
34002         
34003     },
34004     
34005     verticalMeasureColumns : function()
34006     {
34007         this.getContainerWidth();
34008         
34009 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34010 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34011 //            return;
34012 //        }
34013         
34014         var boxWidth = this.boxWidth + this.padWidth;
34015         
34016         if(this.containerWidth < this.boxWidth){
34017             boxWidth = this.containerWidth
34018         }
34019         
34020         var containerWidth = this.containerWidth;
34021         
34022         var cols = Math.floor(containerWidth / boxWidth);
34023         
34024         this.cols = Math.max( cols, 1 );
34025         
34026         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34027         
34028         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34029         
34030         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34031         
34032         this.colWidth = boxWidth + avail - this.padWidth;
34033         
34034         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34035         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34036     },
34037     
34038     horizontalMeasureColumns : function()
34039     {
34040         this.getContainerWidth();
34041         
34042         var boxWidth = this.boxWidth;
34043         
34044         if(this.containerWidth < boxWidth){
34045             boxWidth = this.containerWidth;
34046         }
34047         
34048         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34049         
34050         this.el.setHeight(boxWidth);
34051         
34052     },
34053     
34054     getContainerWidth : function()
34055     {
34056         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34057     },
34058     
34059     layoutItems : function( isInstant )
34060     {
34061         Roo.log(this.bricks);
34062         
34063         var items = Roo.apply([], this.bricks);
34064         
34065         if(this.isHorizontal){
34066             this._horizontalLayoutItems( items , isInstant );
34067             return;
34068         }
34069         
34070 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34071 //            this._verticalAlternativeLayoutItems( items , isInstant );
34072 //            return;
34073 //        }
34074         
34075         this._verticalLayoutItems( items , isInstant );
34076         
34077     },
34078     
34079     _verticalLayoutItems : function ( items , isInstant)
34080     {
34081         if ( !items || !items.length ) {
34082             return;
34083         }
34084         
34085         var standard = [
34086             ['xs', 'xs', 'xs', 'tall'],
34087             ['xs', 'xs', 'tall'],
34088             ['xs', 'xs', 'sm'],
34089             ['xs', 'xs', 'xs'],
34090             ['xs', 'tall'],
34091             ['xs', 'sm'],
34092             ['xs', 'xs'],
34093             ['xs'],
34094             
34095             ['sm', 'xs', 'xs'],
34096             ['sm', 'xs'],
34097             ['sm'],
34098             
34099             ['tall', 'xs', 'xs', 'xs'],
34100             ['tall', 'xs', 'xs'],
34101             ['tall', 'xs'],
34102             ['tall']
34103             
34104         ];
34105         
34106         var queue = [];
34107         
34108         var boxes = [];
34109         
34110         var box = [];
34111         
34112         Roo.each(items, function(item, k){
34113             
34114             switch (item.size) {
34115                 // these layouts take up a full box,
34116                 case 'md' :
34117                 case 'md-left' :
34118                 case 'md-right' :
34119                 case 'wide' :
34120                     
34121                     if(box.length){
34122                         boxes.push(box);
34123                         box = [];
34124                     }
34125                     
34126                     boxes.push([item]);
34127                     
34128                     break;
34129                     
34130                 case 'xs' :
34131                 case 'sm' :
34132                 case 'tall' :
34133                     
34134                     box.push(item);
34135                     
34136                     break;
34137                 default :
34138                     break;
34139                     
34140             }
34141             
34142         }, this);
34143         
34144         if(box.length){
34145             boxes.push(box);
34146             box = [];
34147         }
34148         
34149         var filterPattern = function(box, length)
34150         {
34151             if(!box.length){
34152                 return;
34153             }
34154             
34155             var match = false;
34156             
34157             var pattern = box.slice(0, length);
34158             
34159             var format = [];
34160             
34161             Roo.each(pattern, function(i){
34162                 format.push(i.size);
34163             }, this);
34164             
34165             Roo.each(standard, function(s){
34166                 
34167                 if(String(s) != String(format)){
34168                     return;
34169                 }
34170                 
34171                 match = true;
34172                 return false;
34173                 
34174             }, this);
34175             
34176             if(!match && length == 1){
34177                 return;
34178             }
34179             
34180             if(!match){
34181                 filterPattern(box, length - 1);
34182                 return;
34183             }
34184                 
34185             queue.push(pattern);
34186
34187             box = box.slice(length, box.length);
34188
34189             filterPattern(box, 4);
34190
34191             return;
34192             
34193         }
34194         
34195         Roo.each(boxes, function(box, k){
34196             
34197             if(!box.length){
34198                 return;
34199             }
34200             
34201             if(box.length == 1){
34202                 queue.push(box);
34203                 return;
34204             }
34205             
34206             filterPattern(box, 4);
34207             
34208         }, this);
34209         
34210         this._processVerticalLayoutQueue( queue, isInstant );
34211         
34212     },
34213     
34214 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34215 //    {
34216 //        if ( !items || !items.length ) {
34217 //            return;
34218 //        }
34219 //
34220 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34221 //        
34222 //    },
34223     
34224     _horizontalLayoutItems : function ( items , isInstant)
34225     {
34226         if ( !items || !items.length || items.length < 3) {
34227             return;
34228         }
34229         
34230         items.reverse();
34231         
34232         var eItems = items.slice(0, 3);
34233         
34234         items = items.slice(3, items.length);
34235         
34236         var standard = [
34237             ['xs', 'xs', 'xs', 'wide'],
34238             ['xs', 'xs', 'wide'],
34239             ['xs', 'xs', 'sm'],
34240             ['xs', 'xs', 'xs'],
34241             ['xs', 'wide'],
34242             ['xs', 'sm'],
34243             ['xs', 'xs'],
34244             ['xs'],
34245             
34246             ['sm', 'xs', 'xs'],
34247             ['sm', 'xs'],
34248             ['sm'],
34249             
34250             ['wide', 'xs', 'xs', 'xs'],
34251             ['wide', 'xs', 'xs'],
34252             ['wide', 'xs'],
34253             ['wide'],
34254             
34255             ['wide-thin']
34256         ];
34257         
34258         var queue = [];
34259         
34260         var boxes = [];
34261         
34262         var box = [];
34263         
34264         Roo.each(items, function(item, k){
34265             
34266             switch (item.size) {
34267                 case 'md' :
34268                 case 'md-left' :
34269                 case 'md-right' :
34270                 case 'tall' :
34271                     
34272                     if(box.length){
34273                         boxes.push(box);
34274                         box = [];
34275                     }
34276                     
34277                     boxes.push([item]);
34278                     
34279                     break;
34280                     
34281                 case 'xs' :
34282                 case 'sm' :
34283                 case 'wide' :
34284                 case 'wide-thin' :
34285                     
34286                     box.push(item);
34287                     
34288                     break;
34289                 default :
34290                     break;
34291                     
34292             }
34293             
34294         }, this);
34295         
34296         if(box.length){
34297             boxes.push(box);
34298             box = [];
34299         }
34300         
34301         var filterPattern = function(box, length)
34302         {
34303             if(!box.length){
34304                 return;
34305             }
34306             
34307             var match = false;
34308             
34309             var pattern = box.slice(0, length);
34310             
34311             var format = [];
34312             
34313             Roo.each(pattern, function(i){
34314                 format.push(i.size);
34315             }, this);
34316             
34317             Roo.each(standard, function(s){
34318                 
34319                 if(String(s) != String(format)){
34320                     return;
34321                 }
34322                 
34323                 match = true;
34324                 return false;
34325                 
34326             }, this);
34327             
34328             if(!match && length == 1){
34329                 return;
34330             }
34331             
34332             if(!match){
34333                 filterPattern(box, length - 1);
34334                 return;
34335             }
34336                 
34337             queue.push(pattern);
34338
34339             box = box.slice(length, box.length);
34340
34341             filterPattern(box, 4);
34342
34343             return;
34344             
34345         }
34346         
34347         Roo.each(boxes, function(box, k){
34348             
34349             if(!box.length){
34350                 return;
34351             }
34352             
34353             if(box.length == 1){
34354                 queue.push(box);
34355                 return;
34356             }
34357             
34358             filterPattern(box, 4);
34359             
34360         }, this);
34361         
34362         
34363         var prune = [];
34364         
34365         var pos = this.el.getBox(true);
34366         
34367         var minX = pos.x;
34368         
34369         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34370         
34371         var hit_end = false;
34372         
34373         Roo.each(queue, function(box){
34374             
34375             if(hit_end){
34376                 
34377                 Roo.each(box, function(b){
34378                 
34379                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34380                     b.el.hide();
34381
34382                 }, this);
34383
34384                 return;
34385             }
34386             
34387             var mx = 0;
34388             
34389             Roo.each(box, function(b){
34390                 
34391                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34392                 b.el.show();
34393
34394                 mx = Math.max(mx, b.x);
34395                 
34396             }, this);
34397             
34398             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34399             
34400             if(maxX < minX){
34401                 
34402                 Roo.each(box, function(b){
34403                 
34404                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34405                     b.el.hide();
34406                     
34407                 }, this);
34408                 
34409                 hit_end = true;
34410                 
34411                 return;
34412             }
34413             
34414             prune.push(box);
34415             
34416         }, this);
34417         
34418         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34419     },
34420     
34421     /** Sets position of item in DOM
34422     * @param {Element} item
34423     * @param {Number} x - horizontal position
34424     * @param {Number} y - vertical position
34425     * @param {Boolean} isInstant - disables transitions
34426     */
34427     _processVerticalLayoutQueue : function( queue, isInstant )
34428     {
34429         var pos = this.el.getBox(true);
34430         var x = pos.x;
34431         var y = pos.y;
34432         var maxY = [];
34433         
34434         for (var i = 0; i < this.cols; i++){
34435             maxY[i] = pos.y;
34436         }
34437         
34438         Roo.each(queue, function(box, k){
34439             
34440             var col = k % this.cols;
34441             
34442             Roo.each(box, function(b,kk){
34443                 
34444                 b.el.position('absolute');
34445                 
34446                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34447                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34448                 
34449                 if(b.size == 'md-left' || b.size == 'md-right'){
34450                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34451                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34452                 }
34453                 
34454                 b.el.setWidth(width);
34455                 b.el.setHeight(height);
34456                 // iframe?
34457                 b.el.select('iframe',true).setSize(width,height);
34458                 
34459             }, this);
34460             
34461             for (var i = 0; i < this.cols; i++){
34462                 
34463                 if(maxY[i] < maxY[col]){
34464                     col = i;
34465                     continue;
34466                 }
34467                 
34468                 col = Math.min(col, i);
34469                 
34470             }
34471             
34472             x = pos.x + col * (this.colWidth + this.padWidth);
34473             
34474             y = maxY[col];
34475             
34476             var positions = [];
34477             
34478             switch (box.length){
34479                 case 1 :
34480                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34481                     break;
34482                 case 2 :
34483                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34484                     break;
34485                 case 3 :
34486                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34487                     break;
34488                 case 4 :
34489                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34490                     break;
34491                 default :
34492                     break;
34493             }
34494             
34495             Roo.each(box, function(b,kk){
34496                 
34497                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34498                 
34499                 var sz = b.el.getSize();
34500                 
34501                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34502                 
34503             }, this);
34504             
34505         }, this);
34506         
34507         var mY = 0;
34508         
34509         for (var i = 0; i < this.cols; i++){
34510             mY = Math.max(mY, maxY[i]);
34511         }
34512         
34513         this.el.setHeight(mY - pos.y);
34514         
34515     },
34516     
34517 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34518 //    {
34519 //        var pos = this.el.getBox(true);
34520 //        var x = pos.x;
34521 //        var y = pos.y;
34522 //        var maxX = pos.right;
34523 //        
34524 //        var maxHeight = 0;
34525 //        
34526 //        Roo.each(items, function(item, k){
34527 //            
34528 //            var c = k % 2;
34529 //            
34530 //            item.el.position('absolute');
34531 //                
34532 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34533 //
34534 //            item.el.setWidth(width);
34535 //
34536 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34537 //
34538 //            item.el.setHeight(height);
34539 //            
34540 //            if(c == 0){
34541 //                item.el.setXY([x, y], isInstant ? false : true);
34542 //            } else {
34543 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34544 //            }
34545 //            
34546 //            y = y + height + this.alternativePadWidth;
34547 //            
34548 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34549 //            
34550 //        }, this);
34551 //        
34552 //        this.el.setHeight(maxHeight);
34553 //        
34554 //    },
34555     
34556     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34557     {
34558         var pos = this.el.getBox(true);
34559         
34560         var minX = pos.x;
34561         var minY = pos.y;
34562         
34563         var maxX = pos.right;
34564         
34565         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34566         
34567         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34568         
34569         Roo.each(queue, function(box, k){
34570             
34571             Roo.each(box, function(b, kk){
34572                 
34573                 b.el.position('absolute');
34574                 
34575                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34576                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34577                 
34578                 if(b.size == 'md-left' || b.size == 'md-right'){
34579                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34580                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34581                 }
34582                 
34583                 b.el.setWidth(width);
34584                 b.el.setHeight(height);
34585                 
34586             }, this);
34587             
34588             if(!box.length){
34589                 return;
34590             }
34591             
34592             var positions = [];
34593             
34594             switch (box.length){
34595                 case 1 :
34596                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34597                     break;
34598                 case 2 :
34599                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34600                     break;
34601                 case 3 :
34602                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34603                     break;
34604                 case 4 :
34605                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34606                     break;
34607                 default :
34608                     break;
34609             }
34610             
34611             Roo.each(box, function(b,kk){
34612                 
34613                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34614                 
34615                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34616                 
34617             }, this);
34618             
34619         }, this);
34620         
34621     },
34622     
34623     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34624     {
34625         Roo.each(eItems, function(b,k){
34626             
34627             b.size = (k == 0) ? 'sm' : 'xs';
34628             b.x = (k == 0) ? 2 : 1;
34629             b.y = (k == 0) ? 2 : 1;
34630             
34631             b.el.position('absolute');
34632             
34633             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34634                 
34635             b.el.setWidth(width);
34636             
34637             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34638             
34639             b.el.setHeight(height);
34640             
34641         }, this);
34642
34643         var positions = [];
34644         
34645         positions.push({
34646             x : maxX - this.unitWidth * 2 - this.gutter,
34647             y : minY
34648         });
34649         
34650         positions.push({
34651             x : maxX - this.unitWidth,
34652             y : minY + (this.unitWidth + this.gutter) * 2
34653         });
34654         
34655         positions.push({
34656             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34657             y : minY
34658         });
34659         
34660         Roo.each(eItems, function(b,k){
34661             
34662             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34663
34664         }, this);
34665         
34666     },
34667     
34668     getVerticalOneBoxColPositions : function(x, y, box)
34669     {
34670         var pos = [];
34671         
34672         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34673         
34674         if(box[0].size == 'md-left'){
34675             rand = 0;
34676         }
34677         
34678         if(box[0].size == 'md-right'){
34679             rand = 1;
34680         }
34681         
34682         pos.push({
34683             x : x + (this.unitWidth + this.gutter) * rand,
34684             y : y
34685         });
34686         
34687         return pos;
34688     },
34689     
34690     getVerticalTwoBoxColPositions : function(x, y, box)
34691     {
34692         var pos = [];
34693         
34694         if(box[0].size == 'xs'){
34695             
34696             pos.push({
34697                 x : x,
34698                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34699             });
34700
34701             pos.push({
34702                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34703                 y : y
34704             });
34705             
34706             return pos;
34707             
34708         }
34709         
34710         pos.push({
34711             x : x,
34712             y : y
34713         });
34714
34715         pos.push({
34716             x : x + (this.unitWidth + this.gutter) * 2,
34717             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34718         });
34719         
34720         return pos;
34721         
34722     },
34723     
34724     getVerticalThreeBoxColPositions : function(x, y, box)
34725     {
34726         var pos = [];
34727         
34728         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34729             
34730             pos.push({
34731                 x : x,
34732                 y : y
34733             });
34734
34735             pos.push({
34736                 x : x + (this.unitWidth + this.gutter) * 1,
34737                 y : y
34738             });
34739             
34740             pos.push({
34741                 x : x + (this.unitWidth + this.gutter) * 2,
34742                 y : y
34743             });
34744             
34745             return pos;
34746             
34747         }
34748         
34749         if(box[0].size == 'xs' && box[1].size == 'xs'){
34750             
34751             pos.push({
34752                 x : x,
34753                 y : y
34754             });
34755
34756             pos.push({
34757                 x : x,
34758                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34759             });
34760             
34761             pos.push({
34762                 x : x + (this.unitWidth + this.gutter) * 1,
34763                 y : y
34764             });
34765             
34766             return pos;
34767             
34768         }
34769         
34770         pos.push({
34771             x : x,
34772             y : y
34773         });
34774
34775         pos.push({
34776             x : x + (this.unitWidth + this.gutter) * 2,
34777             y : y
34778         });
34779
34780         pos.push({
34781             x : x + (this.unitWidth + this.gutter) * 2,
34782             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34783         });
34784             
34785         return pos;
34786         
34787     },
34788     
34789     getVerticalFourBoxColPositions : function(x, y, box)
34790     {
34791         var pos = [];
34792         
34793         if(box[0].size == 'xs'){
34794             
34795             pos.push({
34796                 x : x,
34797                 y : y
34798             });
34799
34800             pos.push({
34801                 x : x,
34802                 y : y + (this.unitHeight + this.gutter) * 1
34803             });
34804             
34805             pos.push({
34806                 x : x,
34807                 y : y + (this.unitHeight + this.gutter) * 2
34808             });
34809             
34810             pos.push({
34811                 x : x + (this.unitWidth + this.gutter) * 1,
34812                 y : y
34813             });
34814             
34815             return pos;
34816             
34817         }
34818         
34819         pos.push({
34820             x : x,
34821             y : y
34822         });
34823
34824         pos.push({
34825             x : x + (this.unitWidth + this.gutter) * 2,
34826             y : y
34827         });
34828
34829         pos.push({
34830             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34831             y : y + (this.unitHeight + this.gutter) * 1
34832         });
34833
34834         pos.push({
34835             x : x + (this.unitWidth + this.gutter) * 2,
34836             y : y + (this.unitWidth + this.gutter) * 2
34837         });
34838
34839         return pos;
34840         
34841     },
34842     
34843     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34844     {
34845         var pos = [];
34846         
34847         if(box[0].size == 'md-left'){
34848             pos.push({
34849                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34850                 y : minY
34851             });
34852             
34853             return pos;
34854         }
34855         
34856         if(box[0].size == 'md-right'){
34857             pos.push({
34858                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34859                 y : minY + (this.unitWidth + this.gutter) * 1
34860             });
34861             
34862             return pos;
34863         }
34864         
34865         var rand = Math.floor(Math.random() * (4 - box[0].y));
34866         
34867         pos.push({
34868             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34869             y : minY + (this.unitWidth + this.gutter) * rand
34870         });
34871         
34872         return pos;
34873         
34874     },
34875     
34876     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34877     {
34878         var pos = [];
34879         
34880         if(box[0].size == 'xs'){
34881             
34882             pos.push({
34883                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34884                 y : minY
34885             });
34886
34887             pos.push({
34888                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34889                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34890             });
34891             
34892             return pos;
34893             
34894         }
34895         
34896         pos.push({
34897             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34898             y : minY
34899         });
34900
34901         pos.push({
34902             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34903             y : minY + (this.unitWidth + this.gutter) * 2
34904         });
34905         
34906         return pos;
34907         
34908     },
34909     
34910     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34911     {
34912         var pos = [];
34913         
34914         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34915             
34916             pos.push({
34917                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34918                 y : minY
34919             });
34920
34921             pos.push({
34922                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34923                 y : minY + (this.unitWidth + this.gutter) * 1
34924             });
34925             
34926             pos.push({
34927                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34928                 y : minY + (this.unitWidth + this.gutter) * 2
34929             });
34930             
34931             return pos;
34932             
34933         }
34934         
34935         if(box[0].size == 'xs' && box[1].size == 'xs'){
34936             
34937             pos.push({
34938                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34939                 y : minY
34940             });
34941
34942             pos.push({
34943                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34944                 y : minY
34945             });
34946             
34947             pos.push({
34948                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34949                 y : minY + (this.unitWidth + this.gutter) * 1
34950             });
34951             
34952             return pos;
34953             
34954         }
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[1].x - this.gutter * (box[1].x - 1),
34963             y : minY + (this.unitWidth + this.gutter) * 2
34964         });
34965
34966         pos.push({
34967             x : maxX - 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 + (this.unitWidth + this.gutter) * 2
34969         });
34970             
34971         return pos;
34972         
34973     },
34974     
34975     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34976     {
34977         var pos = [];
34978         
34979         if(box[0].size == 'xs'){
34980             
34981             pos.push({
34982                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34983                 y : minY
34984             });
34985
34986             pos.push({
34987                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34988                 y : minY
34989             });
34990             
34991             pos.push({
34992                 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),
34993                 y : minY
34994             });
34995             
34996             pos.push({
34997                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34998                 y : minY + (this.unitWidth + this.gutter) * 1
34999             });
35000             
35001             return pos;
35002             
35003         }
35004         
35005         pos.push({
35006             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35007             y : minY
35008         });
35009         
35010         pos.push({
35011             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35012             y : minY + (this.unitWidth + this.gutter) * 2
35013         });
35014         
35015         pos.push({
35016             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35017             y : minY + (this.unitWidth + this.gutter) * 2
35018         });
35019         
35020         pos.push({
35021             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),
35022             y : minY + (this.unitWidth + this.gutter) * 2
35023         });
35024
35025         return pos;
35026         
35027     },
35028     
35029     /**
35030     * remove a Masonry Brick
35031     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35032     */
35033     removeBrick : function(brick_id)
35034     {
35035         if (!brick_id) {
35036             return;
35037         }
35038         
35039         for (var i = 0; i<this.bricks.length; i++) {
35040             if (this.bricks[i].id == brick_id) {
35041                 this.bricks.splice(i,1);
35042                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35043                 this.initial();
35044             }
35045         }
35046     },
35047     
35048     /**
35049     * adds a Masonry Brick
35050     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35051     */
35052     addBrick : function(cfg)
35053     {
35054         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35055         //this.register(cn);
35056         cn.parentId = this.id;
35057         cn.render(this.el);
35058         return cn;
35059     },
35060     
35061     /**
35062     * register a Masonry Brick
35063     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35064     */
35065     
35066     register : function(brick)
35067     {
35068         this.bricks.push(brick);
35069         brick.masonryId = this.id;
35070     },
35071     
35072     /**
35073     * clear all the Masonry Brick
35074     */
35075     clearAll : function()
35076     {
35077         this.bricks = [];
35078         //this.getChildContainer().dom.innerHTML = "";
35079         this.el.dom.innerHTML = '';
35080     },
35081     
35082     getSelected : function()
35083     {
35084         if (!this.selectedBrick) {
35085             return false;
35086         }
35087         
35088         return this.selectedBrick;
35089     }
35090 });
35091
35092 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35093     
35094     groups: {},
35095      /**
35096     * register a Masonry Layout
35097     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35098     */
35099     
35100     register : function(layout)
35101     {
35102         this.groups[layout.id] = layout;
35103     },
35104     /**
35105     * fetch a  Masonry Layout based on the masonry layout ID
35106     * @param {string} the masonry layout to add
35107     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35108     */
35109     
35110     get: function(layout_id) {
35111         if (typeof(this.groups[layout_id]) == 'undefined') {
35112             return false;
35113         }
35114         return this.groups[layout_id] ;
35115     }
35116     
35117     
35118     
35119 });
35120
35121  
35122
35123  /**
35124  *
35125  * This is based on 
35126  * http://masonry.desandro.com
35127  *
35128  * The idea is to render all the bricks based on vertical width...
35129  *
35130  * The original code extends 'outlayer' - we might need to use that....
35131  * 
35132  */
35133
35134
35135 /**
35136  * @class Roo.bootstrap.LayoutMasonryAuto
35137  * @extends Roo.bootstrap.Component
35138  * Bootstrap Layout Masonry class
35139  * 
35140  * @constructor
35141  * Create a new Element
35142  * @param {Object} config The config object
35143  */
35144
35145 Roo.bootstrap.LayoutMasonryAuto = function(config){
35146     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35147 };
35148
35149 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35150     
35151       /**
35152      * @cfg {Boolean} isFitWidth  - resize the width..
35153      */   
35154     isFitWidth : false,  // options..
35155     /**
35156      * @cfg {Boolean} isOriginLeft = left align?
35157      */   
35158     isOriginLeft : true,
35159     /**
35160      * @cfg {Boolean} isOriginTop = top align?
35161      */   
35162     isOriginTop : false,
35163     /**
35164      * @cfg {Boolean} isLayoutInstant = no animation?
35165      */   
35166     isLayoutInstant : false, // needed?
35167     /**
35168      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35169      */   
35170     isResizingContainer : true,
35171     /**
35172      * @cfg {Number} columnWidth  width of the columns 
35173      */   
35174     
35175     columnWidth : 0,
35176     
35177     /**
35178      * @cfg {Number} maxCols maximum number of columns
35179      */   
35180     
35181     maxCols: 0,
35182     /**
35183      * @cfg {Number} padHeight padding below box..
35184      */   
35185     
35186     padHeight : 10, 
35187     
35188     /**
35189      * @cfg {Boolean} isAutoInitial defalut true
35190      */   
35191     
35192     isAutoInitial : true, 
35193     
35194     // private?
35195     gutter : 0,
35196     
35197     containerWidth: 0,
35198     initialColumnWidth : 0,
35199     currentSize : null,
35200     
35201     colYs : null, // array.
35202     maxY : 0,
35203     padWidth: 10,
35204     
35205     
35206     tag: 'div',
35207     cls: '',
35208     bricks: null, //CompositeElement
35209     cols : 0, // array?
35210     // element : null, // wrapped now this.el
35211     _isLayoutInited : null, 
35212     
35213     
35214     getAutoCreate : function(){
35215         
35216         var cfg = {
35217             tag: this.tag,
35218             cls: 'blog-masonary-wrapper ' + this.cls,
35219             cn : {
35220                 cls : 'mas-boxes masonary'
35221             }
35222         };
35223         
35224         return cfg;
35225     },
35226     
35227     getChildContainer: function( )
35228     {
35229         if (this.boxesEl) {
35230             return this.boxesEl;
35231         }
35232         
35233         this.boxesEl = this.el.select('.mas-boxes').first();
35234         
35235         return this.boxesEl;
35236     },
35237     
35238     
35239     initEvents : function()
35240     {
35241         var _this = this;
35242         
35243         if(this.isAutoInitial){
35244             Roo.log('hook children rendered');
35245             this.on('childrenrendered', function() {
35246                 Roo.log('children rendered');
35247                 _this.initial();
35248             } ,this);
35249         }
35250         
35251     },
35252     
35253     initial : function()
35254     {
35255         this.reloadItems();
35256
35257         this.currentSize = this.el.getBox(true);
35258
35259         /// was window resize... - let's see if this works..
35260         Roo.EventManager.onWindowResize(this.resize, this); 
35261
35262         if(!this.isAutoInitial){
35263             this.layout();
35264             return;
35265         }
35266         
35267         this.layout.defer(500,this);
35268     },
35269     
35270     reloadItems: function()
35271     {
35272         this.bricks = this.el.select('.masonry-brick', true);
35273         
35274         this.bricks.each(function(b) {
35275             //Roo.log(b.getSize());
35276             if (!b.attr('originalwidth')) {
35277                 b.attr('originalwidth',  b.getSize().width);
35278             }
35279             
35280         });
35281         
35282         Roo.log(this.bricks.elements.length);
35283     },
35284     
35285     resize : function()
35286     {
35287         Roo.log('resize');
35288         var cs = this.el.getBox(true);
35289         
35290         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35291             Roo.log("no change in with or X");
35292             return;
35293         }
35294         this.currentSize = cs;
35295         this.layout();
35296     },
35297     
35298     layout : function()
35299     {
35300          Roo.log('layout');
35301         this._resetLayout();
35302         //this._manageStamps();
35303       
35304         // don't animate first layout
35305         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35306         this.layoutItems( isInstant );
35307       
35308         // flag for initalized
35309         this._isLayoutInited = true;
35310     },
35311     
35312     layoutItems : function( isInstant )
35313     {
35314         //var items = this._getItemsForLayout( this.items );
35315         // original code supports filtering layout items.. we just ignore it..
35316         
35317         this._layoutItems( this.bricks , isInstant );
35318       
35319         this._postLayout();
35320     },
35321     _layoutItems : function ( items , isInstant)
35322     {
35323        //this.fireEvent( 'layout', this, items );
35324     
35325
35326         if ( !items || !items.elements.length ) {
35327           // no items, emit event with empty array
35328             return;
35329         }
35330
35331         var queue = [];
35332         items.each(function(item) {
35333             Roo.log("layout item");
35334             Roo.log(item);
35335             // get x/y object from method
35336             var position = this._getItemLayoutPosition( item );
35337             // enqueue
35338             position.item = item;
35339             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35340             queue.push( position );
35341         }, this);
35342       
35343         this._processLayoutQueue( queue );
35344     },
35345     /** Sets position of item in DOM
35346     * @param {Element} item
35347     * @param {Number} x - horizontal position
35348     * @param {Number} y - vertical position
35349     * @param {Boolean} isInstant - disables transitions
35350     */
35351     _processLayoutQueue : function( queue )
35352     {
35353         for ( var i=0, len = queue.length; i < len; i++ ) {
35354             var obj = queue[i];
35355             obj.item.position('absolute');
35356             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35357         }
35358     },
35359       
35360     
35361     /**
35362     * Any logic you want to do after each layout,
35363     * i.e. size the container
35364     */
35365     _postLayout : function()
35366     {
35367         this.resizeContainer();
35368     },
35369     
35370     resizeContainer : function()
35371     {
35372         if ( !this.isResizingContainer ) {
35373             return;
35374         }
35375         var size = this._getContainerSize();
35376         if ( size ) {
35377             this.el.setSize(size.width,size.height);
35378             this.boxesEl.setSize(size.width,size.height);
35379         }
35380     },
35381     
35382     
35383     
35384     _resetLayout : function()
35385     {
35386         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35387         this.colWidth = this.el.getWidth();
35388         //this.gutter = this.el.getWidth(); 
35389         
35390         this.measureColumns();
35391
35392         // reset column Y
35393         var i = this.cols;
35394         this.colYs = [];
35395         while (i--) {
35396             this.colYs.push( 0 );
35397         }
35398     
35399         this.maxY = 0;
35400     },
35401
35402     measureColumns : function()
35403     {
35404         this.getContainerWidth();
35405       // if columnWidth is 0, default to outerWidth of first item
35406         if ( !this.columnWidth ) {
35407             var firstItem = this.bricks.first();
35408             Roo.log(firstItem);
35409             this.columnWidth  = this.containerWidth;
35410             if (firstItem && firstItem.attr('originalwidth') ) {
35411                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35412             }
35413             // columnWidth fall back to item of first element
35414             Roo.log("set column width?");
35415                         this.initialColumnWidth = this.columnWidth  ;
35416
35417             // if first elem has no width, default to size of container
35418             
35419         }
35420         
35421         
35422         if (this.initialColumnWidth) {
35423             this.columnWidth = this.initialColumnWidth;
35424         }
35425         
35426         
35427             
35428         // column width is fixed at the top - however if container width get's smaller we should
35429         // reduce it...
35430         
35431         // this bit calcs how man columns..
35432             
35433         var columnWidth = this.columnWidth += this.gutter;
35434       
35435         // calculate columns
35436         var containerWidth = this.containerWidth + this.gutter;
35437         
35438         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35439         // fix rounding errors, typically with gutters
35440         var excess = columnWidth - containerWidth % columnWidth;
35441         
35442         
35443         // if overshoot is less than a pixel, round up, otherwise floor it
35444         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35445         cols = Math[ mathMethod ]( cols );
35446         this.cols = Math.max( cols, 1 );
35447         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35448         
35449          // padding positioning..
35450         var totalColWidth = this.cols * this.columnWidth;
35451         var padavail = this.containerWidth - totalColWidth;
35452         // so for 2 columns - we need 3 'pads'
35453         
35454         var padNeeded = (1+this.cols) * this.padWidth;
35455         
35456         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35457         
35458         this.columnWidth += padExtra
35459         //this.padWidth = Math.floor(padavail /  ( this.cols));
35460         
35461         // adjust colum width so that padding is fixed??
35462         
35463         // we have 3 columns ... total = width * 3
35464         // we have X left over... that should be used by 
35465         
35466         //if (this.expandC) {
35467             
35468         //}
35469         
35470         
35471         
35472     },
35473     
35474     getContainerWidth : function()
35475     {
35476        /* // container is parent if fit width
35477         var container = this.isFitWidth ? this.element.parentNode : this.element;
35478         // check that this.size and size are there
35479         // IE8 triggers resize on body size change, so they might not be
35480         
35481         var size = getSize( container );  //FIXME
35482         this.containerWidth = size && size.innerWidth; //FIXME
35483         */
35484          
35485         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35486         
35487     },
35488     
35489     _getItemLayoutPosition : function( item )  // what is item?
35490     {
35491         // we resize the item to our columnWidth..
35492       
35493         item.setWidth(this.columnWidth);
35494         item.autoBoxAdjust  = false;
35495         
35496         var sz = item.getSize();
35497  
35498         // how many columns does this brick span
35499         var remainder = this.containerWidth % this.columnWidth;
35500         
35501         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35502         // round if off by 1 pixel, otherwise use ceil
35503         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35504         colSpan = Math.min( colSpan, this.cols );
35505         
35506         // normally this should be '1' as we dont' currently allow multi width columns..
35507         
35508         var colGroup = this._getColGroup( colSpan );
35509         // get the minimum Y value from the columns
35510         var minimumY = Math.min.apply( Math, colGroup );
35511         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35512         
35513         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35514          
35515         // position the brick
35516         var position = {
35517             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35518             y: this.currentSize.y + minimumY + this.padHeight
35519         };
35520         
35521         Roo.log(position);
35522         // apply setHeight to necessary columns
35523         var setHeight = minimumY + sz.height + this.padHeight;
35524         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35525         
35526         var setSpan = this.cols + 1 - colGroup.length;
35527         for ( var i = 0; i < setSpan; i++ ) {
35528           this.colYs[ shortColIndex + i ] = setHeight ;
35529         }
35530       
35531         return position;
35532     },
35533     
35534     /**
35535      * @param {Number} colSpan - number of columns the element spans
35536      * @returns {Array} colGroup
35537      */
35538     _getColGroup : function( colSpan )
35539     {
35540         if ( colSpan < 2 ) {
35541           // if brick spans only one column, use all the column Ys
35542           return this.colYs;
35543         }
35544       
35545         var colGroup = [];
35546         // how many different places could this brick fit horizontally
35547         var groupCount = this.cols + 1 - colSpan;
35548         // for each group potential horizontal position
35549         for ( var i = 0; i < groupCount; i++ ) {
35550           // make an array of colY values for that one group
35551           var groupColYs = this.colYs.slice( i, i + colSpan );
35552           // and get the max value of the array
35553           colGroup[i] = Math.max.apply( Math, groupColYs );
35554         }
35555         return colGroup;
35556     },
35557     /*
35558     _manageStamp : function( stamp )
35559     {
35560         var stampSize =  stamp.getSize();
35561         var offset = stamp.getBox();
35562         // get the columns that this stamp affects
35563         var firstX = this.isOriginLeft ? offset.x : offset.right;
35564         var lastX = firstX + stampSize.width;
35565         var firstCol = Math.floor( firstX / this.columnWidth );
35566         firstCol = Math.max( 0, firstCol );
35567         
35568         var lastCol = Math.floor( lastX / this.columnWidth );
35569         // lastCol should not go over if multiple of columnWidth #425
35570         lastCol -= lastX % this.columnWidth ? 0 : 1;
35571         lastCol = Math.min( this.cols - 1, lastCol );
35572         
35573         // set colYs to bottom of the stamp
35574         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35575             stampSize.height;
35576             
35577         for ( var i = firstCol; i <= lastCol; i++ ) {
35578           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35579         }
35580     },
35581     */
35582     
35583     _getContainerSize : function()
35584     {
35585         this.maxY = Math.max.apply( Math, this.colYs );
35586         var size = {
35587             height: this.maxY
35588         };
35589       
35590         if ( this.isFitWidth ) {
35591             size.width = this._getContainerFitWidth();
35592         }
35593       
35594         return size;
35595     },
35596     
35597     _getContainerFitWidth : function()
35598     {
35599         var unusedCols = 0;
35600         // count unused columns
35601         var i = this.cols;
35602         while ( --i ) {
35603           if ( this.colYs[i] !== 0 ) {
35604             break;
35605           }
35606           unusedCols++;
35607         }
35608         // fit container to columns that have been used
35609         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35610     },
35611     
35612     needsResizeLayout : function()
35613     {
35614         var previousWidth = this.containerWidth;
35615         this.getContainerWidth();
35616         return previousWidth !== this.containerWidth;
35617     }
35618  
35619 });
35620
35621  
35622
35623  /*
35624  * - LGPL
35625  *
35626  * element
35627  * 
35628  */
35629
35630 /**
35631  * @class Roo.bootstrap.MasonryBrick
35632  * @extends Roo.bootstrap.Component
35633  * Bootstrap MasonryBrick class
35634  * 
35635  * @constructor
35636  * Create a new MasonryBrick
35637  * @param {Object} config The config object
35638  */
35639
35640 Roo.bootstrap.MasonryBrick = function(config){
35641     
35642     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35643     
35644     Roo.bootstrap.MasonryBrick.register(this);
35645     
35646     this.addEvents({
35647         // raw events
35648         /**
35649          * @event click
35650          * When a MasonryBrick is clcik
35651          * @param {Roo.bootstrap.MasonryBrick} this
35652          * @param {Roo.EventObject} e
35653          */
35654         "click" : true
35655     });
35656 };
35657
35658 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35659     
35660     /**
35661      * @cfg {String} title
35662      */   
35663     title : '',
35664     /**
35665      * @cfg {String} html
35666      */   
35667     html : '',
35668     /**
35669      * @cfg {String} bgimage
35670      */   
35671     bgimage : '',
35672     /**
35673      * @cfg {String} videourl
35674      */   
35675     videourl : '',
35676     /**
35677      * @cfg {String} cls
35678      */   
35679     cls : '',
35680     /**
35681      * @cfg {String} href
35682      */   
35683     href : '',
35684     /**
35685      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35686      */   
35687     size : 'xs',
35688     
35689     /**
35690      * @cfg {String} placetitle (center|bottom)
35691      */   
35692     placetitle : '',
35693     
35694     /**
35695      * @cfg {Boolean} isFitContainer defalut true
35696      */   
35697     isFitContainer : true, 
35698     
35699     /**
35700      * @cfg {Boolean} preventDefault defalut false
35701      */   
35702     preventDefault : false, 
35703     
35704     /**
35705      * @cfg {Boolean} inverse defalut false
35706      */   
35707     maskInverse : false, 
35708     
35709     getAutoCreate : function()
35710     {
35711         if(!this.isFitContainer){
35712             return this.getSplitAutoCreate();
35713         }
35714         
35715         var cls = 'masonry-brick masonry-brick-full';
35716         
35717         if(this.href.length){
35718             cls += ' masonry-brick-link';
35719         }
35720         
35721         if(this.bgimage.length){
35722             cls += ' masonry-brick-image';
35723         }
35724         
35725         if(this.maskInverse){
35726             cls += ' mask-inverse';
35727         }
35728         
35729         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35730             cls += ' enable-mask';
35731         }
35732         
35733         if(this.size){
35734             cls += ' masonry-' + this.size + '-brick';
35735         }
35736         
35737         if(this.placetitle.length){
35738             
35739             switch (this.placetitle) {
35740                 case 'center' :
35741                     cls += ' masonry-center-title';
35742                     break;
35743                 case 'bottom' :
35744                     cls += ' masonry-bottom-title';
35745                     break;
35746                 default:
35747                     break;
35748             }
35749             
35750         } else {
35751             if(!this.html.length && !this.bgimage.length){
35752                 cls += ' masonry-center-title';
35753             }
35754
35755             if(!this.html.length && this.bgimage.length){
35756                 cls += ' masonry-bottom-title';
35757             }
35758         }
35759         
35760         if(this.cls){
35761             cls += ' ' + this.cls;
35762         }
35763         
35764         var cfg = {
35765             tag: (this.href.length) ? 'a' : 'div',
35766             cls: cls,
35767             cn: [
35768                 {
35769                     tag: 'div',
35770                     cls: 'masonry-brick-mask'
35771                 },
35772                 {
35773                     tag: 'div',
35774                     cls: 'masonry-brick-paragraph',
35775                     cn: []
35776                 }
35777             ]
35778         };
35779         
35780         if(this.href.length){
35781             cfg.href = this.href;
35782         }
35783         
35784         var cn = cfg.cn[1].cn;
35785         
35786         if(this.title.length){
35787             cn.push({
35788                 tag: 'h4',
35789                 cls: 'masonry-brick-title',
35790                 html: this.title
35791             });
35792         }
35793         
35794         if(this.html.length){
35795             cn.push({
35796                 tag: 'p',
35797                 cls: 'masonry-brick-text',
35798                 html: this.html
35799             });
35800         }
35801         
35802         if (!this.title.length && !this.html.length) {
35803             cfg.cn[1].cls += ' hide';
35804         }
35805         
35806         if(this.bgimage.length){
35807             cfg.cn.push({
35808                 tag: 'img',
35809                 cls: 'masonry-brick-image-view',
35810                 src: this.bgimage
35811             });
35812         }
35813         
35814         if(this.videourl.length){
35815             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35816             // youtube support only?
35817             cfg.cn.push({
35818                 tag: 'iframe',
35819                 cls: 'masonry-brick-image-view',
35820                 src: vurl,
35821                 frameborder : 0,
35822                 allowfullscreen : true
35823             });
35824         }
35825         
35826         return cfg;
35827         
35828     },
35829     
35830     getSplitAutoCreate : function()
35831     {
35832         var cls = 'masonry-brick masonry-brick-split';
35833         
35834         if(this.href.length){
35835             cls += ' masonry-brick-link';
35836         }
35837         
35838         if(this.bgimage.length){
35839             cls += ' masonry-brick-image';
35840         }
35841         
35842         if(this.size){
35843             cls += ' masonry-' + this.size + '-brick';
35844         }
35845         
35846         switch (this.placetitle) {
35847             case 'center' :
35848                 cls += ' masonry-center-title';
35849                 break;
35850             case 'bottom' :
35851                 cls += ' masonry-bottom-title';
35852                 break;
35853             default:
35854                 if(!this.bgimage.length){
35855                     cls += ' masonry-center-title';
35856                 }
35857
35858                 if(this.bgimage.length){
35859                     cls += ' masonry-bottom-title';
35860                 }
35861                 break;
35862         }
35863         
35864         if(this.cls){
35865             cls += ' ' + this.cls;
35866         }
35867         
35868         var cfg = {
35869             tag: (this.href.length) ? 'a' : 'div',
35870             cls: cls,
35871             cn: [
35872                 {
35873                     tag: 'div',
35874                     cls: 'masonry-brick-split-head',
35875                     cn: [
35876                         {
35877                             tag: 'div',
35878                             cls: 'masonry-brick-paragraph',
35879                             cn: []
35880                         }
35881                     ]
35882                 },
35883                 {
35884                     tag: 'div',
35885                     cls: 'masonry-brick-split-body',
35886                     cn: []
35887                 }
35888             ]
35889         };
35890         
35891         if(this.href.length){
35892             cfg.href = this.href;
35893         }
35894         
35895         if(this.title.length){
35896             cfg.cn[0].cn[0].cn.push({
35897                 tag: 'h4',
35898                 cls: 'masonry-brick-title',
35899                 html: this.title
35900             });
35901         }
35902         
35903         if(this.html.length){
35904             cfg.cn[1].cn.push({
35905                 tag: 'p',
35906                 cls: 'masonry-brick-text',
35907                 html: this.html
35908             });
35909         }
35910
35911         if(this.bgimage.length){
35912             cfg.cn[0].cn.push({
35913                 tag: 'img',
35914                 cls: 'masonry-brick-image-view',
35915                 src: this.bgimage
35916             });
35917         }
35918         
35919         if(this.videourl.length){
35920             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35921             // youtube support only?
35922             cfg.cn[0].cn.cn.push({
35923                 tag: 'iframe',
35924                 cls: 'masonry-brick-image-view',
35925                 src: vurl,
35926                 frameborder : 0,
35927                 allowfullscreen : true
35928             });
35929         }
35930         
35931         return cfg;
35932     },
35933     
35934     initEvents: function() 
35935     {
35936         switch (this.size) {
35937             case 'xs' :
35938                 this.x = 1;
35939                 this.y = 1;
35940                 break;
35941             case 'sm' :
35942                 this.x = 2;
35943                 this.y = 2;
35944                 break;
35945             case 'md' :
35946             case 'md-left' :
35947             case 'md-right' :
35948                 this.x = 3;
35949                 this.y = 3;
35950                 break;
35951             case 'tall' :
35952                 this.x = 2;
35953                 this.y = 3;
35954                 break;
35955             case 'wide' :
35956                 this.x = 3;
35957                 this.y = 2;
35958                 break;
35959             case 'wide-thin' :
35960                 this.x = 3;
35961                 this.y = 1;
35962                 break;
35963                         
35964             default :
35965                 break;
35966         }
35967         
35968         if(Roo.isTouch){
35969             this.el.on('touchstart', this.onTouchStart, this);
35970             this.el.on('touchmove', this.onTouchMove, this);
35971             this.el.on('touchend', this.onTouchEnd, this);
35972             this.el.on('contextmenu', this.onContextMenu, this);
35973         } else {
35974             this.el.on('mouseenter'  ,this.enter, this);
35975             this.el.on('mouseleave', this.leave, this);
35976             this.el.on('click', this.onClick, this);
35977         }
35978         
35979         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35980             this.parent().bricks.push(this);   
35981         }
35982         
35983     },
35984     
35985     onClick: function(e, el)
35986     {
35987         var time = this.endTimer - this.startTimer;
35988         // Roo.log(e.preventDefault());
35989         if(Roo.isTouch){
35990             if(time > 1000){
35991                 e.preventDefault();
35992                 return;
35993             }
35994         }
35995         
35996         if(!this.preventDefault){
35997             return;
35998         }
35999         
36000         e.preventDefault();
36001         
36002         if (this.activeClass != '') {
36003             this.selectBrick();
36004         }
36005         
36006         this.fireEvent('click', this, e);
36007     },
36008     
36009     enter: function(e, el)
36010     {
36011         e.preventDefault();
36012         
36013         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36014             return;
36015         }
36016         
36017         if(this.bgimage.length && this.html.length){
36018             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36019         }
36020     },
36021     
36022     leave: function(e, el)
36023     {
36024         e.preventDefault();
36025         
36026         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36027             return;
36028         }
36029         
36030         if(this.bgimage.length && this.html.length){
36031             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36032         }
36033     },
36034     
36035     onTouchStart: function(e, el)
36036     {
36037 //        e.preventDefault();
36038         
36039         this.touchmoved = false;
36040         
36041         if(!this.isFitContainer){
36042             return;
36043         }
36044         
36045         if(!this.bgimage.length || !this.html.length){
36046             return;
36047         }
36048         
36049         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36050         
36051         this.timer = new Date().getTime();
36052         
36053     },
36054     
36055     onTouchMove: function(e, el)
36056     {
36057         this.touchmoved = true;
36058     },
36059     
36060     onContextMenu : function(e,el)
36061     {
36062         e.preventDefault();
36063         e.stopPropagation();
36064         return false;
36065     },
36066     
36067     onTouchEnd: function(e, el)
36068     {
36069 //        e.preventDefault();
36070         
36071         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36072         
36073             this.leave(e,el);
36074             
36075             return;
36076         }
36077         
36078         if(!this.bgimage.length || !this.html.length){
36079             
36080             if(this.href.length){
36081                 window.location.href = this.href;
36082             }
36083             
36084             return;
36085         }
36086         
36087         if(!this.isFitContainer){
36088             return;
36089         }
36090         
36091         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36092         
36093         window.location.href = this.href;
36094     },
36095     
36096     //selection on single brick only
36097     selectBrick : function() {
36098         
36099         if (!this.parentId) {
36100             return;
36101         }
36102         
36103         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36104         var index = m.selectedBrick.indexOf(this.id);
36105         
36106         if ( index > -1) {
36107             m.selectedBrick.splice(index,1);
36108             this.el.removeClass(this.activeClass);
36109             return;
36110         }
36111         
36112         for(var i = 0; i < m.selectedBrick.length; i++) {
36113             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36114             b.el.removeClass(b.activeClass);
36115         }
36116         
36117         m.selectedBrick = [];
36118         
36119         m.selectedBrick.push(this.id);
36120         this.el.addClass(this.activeClass);
36121         return;
36122     },
36123     
36124     isSelected : function(){
36125         return this.el.hasClass(this.activeClass);
36126         
36127     }
36128 });
36129
36130 Roo.apply(Roo.bootstrap.MasonryBrick, {
36131     
36132     //groups: {},
36133     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36134      /**
36135     * register a Masonry Brick
36136     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36137     */
36138     
36139     register : function(brick)
36140     {
36141         //this.groups[brick.id] = brick;
36142         this.groups.add(brick.id, brick);
36143     },
36144     /**
36145     * fetch a  masonry brick based on the masonry brick ID
36146     * @param {string} the masonry brick to add
36147     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36148     */
36149     
36150     get: function(brick_id) 
36151     {
36152         // if (typeof(this.groups[brick_id]) == 'undefined') {
36153         //     return false;
36154         // }
36155         // return this.groups[brick_id] ;
36156         
36157         if(this.groups.key(brick_id)) {
36158             return this.groups.key(brick_id);
36159         }
36160         
36161         return false;
36162     }
36163     
36164     
36165     
36166 });
36167
36168  /*
36169  * - LGPL
36170  *
36171  * element
36172  * 
36173  */
36174
36175 /**
36176  * @class Roo.bootstrap.Brick
36177  * @extends Roo.bootstrap.Component
36178  * Bootstrap Brick class
36179  * 
36180  * @constructor
36181  * Create a new Brick
36182  * @param {Object} config The config object
36183  */
36184
36185 Roo.bootstrap.Brick = function(config){
36186     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36187     
36188     this.addEvents({
36189         // raw events
36190         /**
36191          * @event click
36192          * When a Brick is click
36193          * @param {Roo.bootstrap.Brick} this
36194          * @param {Roo.EventObject} e
36195          */
36196         "click" : true
36197     });
36198 };
36199
36200 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36201     
36202     /**
36203      * @cfg {String} title
36204      */   
36205     title : '',
36206     /**
36207      * @cfg {String} html
36208      */   
36209     html : '',
36210     /**
36211      * @cfg {String} bgimage
36212      */   
36213     bgimage : '',
36214     /**
36215      * @cfg {String} cls
36216      */   
36217     cls : '',
36218     /**
36219      * @cfg {String} href
36220      */   
36221     href : '',
36222     /**
36223      * @cfg {String} video
36224      */   
36225     video : '',
36226     /**
36227      * @cfg {Boolean} square
36228      */   
36229     square : true,
36230     
36231     getAutoCreate : function()
36232     {
36233         var cls = 'roo-brick';
36234         
36235         if(this.href.length){
36236             cls += ' roo-brick-link';
36237         }
36238         
36239         if(this.bgimage.length){
36240             cls += ' roo-brick-image';
36241         }
36242         
36243         if(!this.html.length && !this.bgimage.length){
36244             cls += ' roo-brick-center-title';
36245         }
36246         
36247         if(!this.html.length && this.bgimage.length){
36248             cls += ' roo-brick-bottom-title';
36249         }
36250         
36251         if(this.cls){
36252             cls += ' ' + this.cls;
36253         }
36254         
36255         var cfg = {
36256             tag: (this.href.length) ? 'a' : 'div',
36257             cls: cls,
36258             cn: [
36259                 {
36260                     tag: 'div',
36261                     cls: 'roo-brick-paragraph',
36262                     cn: []
36263                 }
36264             ]
36265         };
36266         
36267         if(this.href.length){
36268             cfg.href = this.href;
36269         }
36270         
36271         var cn = cfg.cn[0].cn;
36272         
36273         if(this.title.length){
36274             cn.push({
36275                 tag: 'h4',
36276                 cls: 'roo-brick-title',
36277                 html: this.title
36278             });
36279         }
36280         
36281         if(this.html.length){
36282             cn.push({
36283                 tag: 'p',
36284                 cls: 'roo-brick-text',
36285                 html: this.html
36286             });
36287         } else {
36288             cn.cls += ' hide';
36289         }
36290         
36291         if(this.bgimage.length){
36292             cfg.cn.push({
36293                 tag: 'img',
36294                 cls: 'roo-brick-image-view',
36295                 src: this.bgimage
36296             });
36297         }
36298         
36299         return cfg;
36300     },
36301     
36302     initEvents: function() 
36303     {
36304         if(this.title.length || this.html.length){
36305             this.el.on('mouseenter'  ,this.enter, this);
36306             this.el.on('mouseleave', this.leave, this);
36307         }
36308         
36309         Roo.EventManager.onWindowResize(this.resize, this); 
36310         
36311         if(this.bgimage.length){
36312             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36313             this.imageEl.on('load', this.onImageLoad, this);
36314             return;
36315         }
36316         
36317         this.resize();
36318     },
36319     
36320     onImageLoad : function()
36321     {
36322         this.resize();
36323     },
36324     
36325     resize : function()
36326     {
36327         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36328         
36329         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36330         
36331         if(this.bgimage.length){
36332             var image = this.el.select('.roo-brick-image-view', true).first();
36333             
36334             image.setWidth(paragraph.getWidth());
36335             
36336             if(this.square){
36337                 image.setHeight(paragraph.getWidth());
36338             }
36339             
36340             this.el.setHeight(image.getHeight());
36341             paragraph.setHeight(image.getHeight());
36342             
36343         }
36344         
36345     },
36346     
36347     enter: function(e, el)
36348     {
36349         e.preventDefault();
36350         
36351         if(this.bgimage.length){
36352             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36353             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36354         }
36355     },
36356     
36357     leave: function(e, el)
36358     {
36359         e.preventDefault();
36360         
36361         if(this.bgimage.length){
36362             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36363             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36364         }
36365     }
36366     
36367 });
36368
36369  
36370
36371  /*
36372  * - LGPL
36373  *
36374  * Number field 
36375  */
36376
36377 /**
36378  * @class Roo.bootstrap.NumberField
36379  * @extends Roo.bootstrap.Input
36380  * Bootstrap NumberField class
36381  * 
36382  * 
36383  * 
36384  * 
36385  * @constructor
36386  * Create a new NumberField
36387  * @param {Object} config The config object
36388  */
36389
36390 Roo.bootstrap.NumberField = function(config){
36391     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36392 };
36393
36394 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36395     
36396     /**
36397      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36398      */
36399     allowDecimals : true,
36400     /**
36401      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36402      */
36403     decimalSeparator : ".",
36404     /**
36405      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36406      */
36407     decimalPrecision : 2,
36408     /**
36409      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36410      */
36411     allowNegative : true,
36412     
36413     /**
36414      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36415      */
36416     allowZero: true,
36417     /**
36418      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36419      */
36420     minValue : Number.NEGATIVE_INFINITY,
36421     /**
36422      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36423      */
36424     maxValue : Number.MAX_VALUE,
36425     /**
36426      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36427      */
36428     minText : "The minimum value for this field is {0}",
36429     /**
36430      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36431      */
36432     maxText : "The maximum value for this field is {0}",
36433     /**
36434      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36435      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36436      */
36437     nanText : "{0} is not a valid number",
36438     /**
36439      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36440      */
36441     thousandsDelimiter : false,
36442     /**
36443      * @cfg {String} valueAlign alignment of value
36444      */
36445     valueAlign : "left",
36446
36447     getAutoCreate : function()
36448     {
36449         var hiddenInput = {
36450             tag: 'input',
36451             type: 'hidden',
36452             id: Roo.id(),
36453             cls: 'hidden-number-input'
36454         };
36455         
36456         if (this.name) {
36457             hiddenInput.name = this.name;
36458         }
36459         
36460         this.name = '';
36461         
36462         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36463         
36464         this.name = hiddenInput.name;
36465         
36466         if(cfg.cn.length > 0) {
36467             cfg.cn.push(hiddenInput);
36468         }
36469         
36470         return cfg;
36471     },
36472
36473     // private
36474     initEvents : function()
36475     {   
36476         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36477         
36478         var allowed = "0123456789";
36479         
36480         if(this.allowDecimals){
36481             allowed += this.decimalSeparator;
36482         }
36483         
36484         if(this.allowNegative){
36485             allowed += "-";
36486         }
36487         
36488         if(this.thousandsDelimiter) {
36489             allowed += ",";
36490         }
36491         
36492         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36493         
36494         var keyPress = function(e){
36495             
36496             var k = e.getKey();
36497             
36498             var c = e.getCharCode();
36499             
36500             if(
36501                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36502                     allowed.indexOf(String.fromCharCode(c)) === -1
36503             ){
36504                 e.stopEvent();
36505                 return;
36506             }
36507             
36508             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36509                 return;
36510             }
36511             
36512             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36513                 e.stopEvent();
36514             }
36515         };
36516         
36517         this.el.on("keypress", keyPress, this);
36518     },
36519     
36520     validateValue : function(value)
36521     {
36522         
36523         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36524             return false;
36525         }
36526         
36527         var num = this.parseValue(value);
36528         
36529         if(isNaN(num)){
36530             this.markInvalid(String.format(this.nanText, value));
36531             return false;
36532         }
36533         
36534         if(num < this.minValue){
36535             this.markInvalid(String.format(this.minText, this.minValue));
36536             return false;
36537         }
36538         
36539         if(num > this.maxValue){
36540             this.markInvalid(String.format(this.maxText, this.maxValue));
36541             return false;
36542         }
36543         
36544         return true;
36545     },
36546
36547     getValue : function()
36548     {
36549         var v = this.hiddenEl().getValue();
36550         
36551         return this.fixPrecision(this.parseValue(v));
36552     },
36553
36554     parseValue : function(value)
36555     {
36556         if(this.thousandsDelimiter) {
36557             value += "";
36558             r = new RegExp(",", "g");
36559             value = value.replace(r, "");
36560         }
36561         
36562         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36563         return isNaN(value) ? '' : value;
36564     },
36565
36566     fixPrecision : function(value)
36567     {
36568         if(this.thousandsDelimiter) {
36569             value += "";
36570             r = new RegExp(",", "g");
36571             value = value.replace(r, "");
36572         }
36573         
36574         var nan = isNaN(value);
36575         
36576         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36577             return nan ? '' : value;
36578         }
36579         return parseFloat(value).toFixed(this.decimalPrecision);
36580     },
36581
36582     setValue : function(v)
36583     {
36584         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36585         
36586         this.value = v;
36587         
36588         if(this.rendered){
36589             
36590             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36591             
36592             this.inputEl().dom.value = (v == '') ? '' :
36593                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36594             
36595             if(!this.allowZero && v === '0') {
36596                 this.hiddenEl().dom.value = '';
36597                 this.inputEl().dom.value = '';
36598             }
36599             
36600             this.validate();
36601         }
36602     },
36603
36604     decimalPrecisionFcn : function(v)
36605     {
36606         return Math.floor(v);
36607     },
36608
36609     beforeBlur : function()
36610     {
36611         var v = this.parseValue(this.getRawValue());
36612         
36613         if(v || v === 0 || v === ''){
36614             this.setValue(v);
36615         }
36616     },
36617     
36618     hiddenEl : function()
36619     {
36620         return this.el.select('input.hidden-number-input',true).first();
36621     }
36622     
36623 });
36624
36625  
36626
36627 /*
36628 * Licence: LGPL
36629 */
36630
36631 /**
36632  * @class Roo.bootstrap.DocumentSlider
36633  * @extends Roo.bootstrap.Component
36634  * Bootstrap DocumentSlider class
36635  * 
36636  * @constructor
36637  * Create a new DocumentViewer
36638  * @param {Object} config The config object
36639  */
36640
36641 Roo.bootstrap.DocumentSlider = function(config){
36642     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36643     
36644     this.files = [];
36645     
36646     this.addEvents({
36647         /**
36648          * @event initial
36649          * Fire after initEvent
36650          * @param {Roo.bootstrap.DocumentSlider} this
36651          */
36652         "initial" : true,
36653         /**
36654          * @event update
36655          * Fire after update
36656          * @param {Roo.bootstrap.DocumentSlider} this
36657          */
36658         "update" : true,
36659         /**
36660          * @event click
36661          * Fire after click
36662          * @param {Roo.bootstrap.DocumentSlider} this
36663          */
36664         "click" : true
36665     });
36666 };
36667
36668 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36669     
36670     files : false,
36671     
36672     indicator : 0,
36673     
36674     getAutoCreate : function()
36675     {
36676         var cfg = {
36677             tag : 'div',
36678             cls : 'roo-document-slider',
36679             cn : [
36680                 {
36681                     tag : 'div',
36682                     cls : 'roo-document-slider-header',
36683                     cn : [
36684                         {
36685                             tag : 'div',
36686                             cls : 'roo-document-slider-header-title'
36687                         }
36688                     ]
36689                 },
36690                 {
36691                     tag : 'div',
36692                     cls : 'roo-document-slider-body',
36693                     cn : [
36694                         {
36695                             tag : 'div',
36696                             cls : 'roo-document-slider-prev',
36697                             cn : [
36698                                 {
36699                                     tag : 'i',
36700                                     cls : 'fa fa-chevron-left'
36701                                 }
36702                             ]
36703                         },
36704                         {
36705                             tag : 'div',
36706                             cls : 'roo-document-slider-thumb',
36707                             cn : [
36708                                 {
36709                                     tag : 'img',
36710                                     cls : 'roo-document-slider-image'
36711                                 }
36712                             ]
36713                         },
36714                         {
36715                             tag : 'div',
36716                             cls : 'roo-document-slider-next',
36717                             cn : [
36718                                 {
36719                                     tag : 'i',
36720                                     cls : 'fa fa-chevron-right'
36721                                 }
36722                             ]
36723                         }
36724                     ]
36725                 }
36726             ]
36727         };
36728         
36729         return cfg;
36730     },
36731     
36732     initEvents : function()
36733     {
36734         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36735         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36736         
36737         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36738         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36739         
36740         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36741         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36742         
36743         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36744         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36745         
36746         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36747         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36748         
36749         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36750         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36751         
36752         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36753         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36754         
36755         this.thumbEl.on('click', this.onClick, this);
36756         
36757         this.prevIndicator.on('click', this.prev, this);
36758         
36759         this.nextIndicator.on('click', this.next, this);
36760         
36761     },
36762     
36763     initial : function()
36764     {
36765         if(this.files.length){
36766             this.indicator = 1;
36767             this.update()
36768         }
36769         
36770         this.fireEvent('initial', this);
36771     },
36772     
36773     update : function()
36774     {
36775         this.imageEl.attr('src', this.files[this.indicator - 1]);
36776         
36777         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36778         
36779         this.prevIndicator.show();
36780         
36781         if(this.indicator == 1){
36782             this.prevIndicator.hide();
36783         }
36784         
36785         this.nextIndicator.show();
36786         
36787         if(this.indicator == this.files.length){
36788             this.nextIndicator.hide();
36789         }
36790         
36791         this.thumbEl.scrollTo('top');
36792         
36793         this.fireEvent('update', this);
36794     },
36795     
36796     onClick : function(e)
36797     {
36798         e.preventDefault();
36799         
36800         this.fireEvent('click', this);
36801     },
36802     
36803     prev : function(e)
36804     {
36805         e.preventDefault();
36806         
36807         this.indicator = Math.max(1, this.indicator - 1);
36808         
36809         this.update();
36810     },
36811     
36812     next : function(e)
36813     {
36814         e.preventDefault();
36815         
36816         this.indicator = Math.min(this.files.length, this.indicator + 1);
36817         
36818         this.update();
36819     }
36820 });
36821 /*
36822  * - LGPL
36823  *
36824  * RadioSet
36825  *
36826  *
36827  */
36828
36829 /**
36830  * @class Roo.bootstrap.RadioSet
36831  * @extends Roo.bootstrap.Input
36832  * Bootstrap RadioSet class
36833  * @cfg {String} indicatorpos (left|right) default left
36834  * @cfg {Boolean} inline (true|false) inline the element (default true)
36835  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36836  * @constructor
36837  * Create a new RadioSet
36838  * @param {Object} config The config object
36839  */
36840
36841 Roo.bootstrap.RadioSet = function(config){
36842     
36843     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36844     
36845     this.radioes = [];
36846     
36847     Roo.bootstrap.RadioSet.register(this);
36848     
36849     this.addEvents({
36850         /**
36851         * @event check
36852         * Fires when the element is checked or unchecked.
36853         * @param {Roo.bootstrap.RadioSet} this This radio
36854         * @param {Roo.bootstrap.Radio} item The checked item
36855         */
36856        check : true,
36857        /**
36858         * @event click
36859         * Fires when the element is click.
36860         * @param {Roo.bootstrap.RadioSet} this This radio set
36861         * @param {Roo.bootstrap.Radio} item The checked item
36862         * @param {Roo.EventObject} e The event object
36863         */
36864        click : true
36865     });
36866     
36867 };
36868
36869 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36870
36871     radioes : false,
36872     
36873     inline : true,
36874     
36875     weight : '',
36876     
36877     indicatorpos : 'left',
36878     
36879     getAutoCreate : function()
36880     {
36881         var label = {
36882             tag : 'label',
36883             cls : 'roo-radio-set-label',
36884             cn : [
36885                 {
36886                     tag : 'span',
36887                     html : this.fieldLabel
36888                 }
36889             ]
36890         };
36891         if (Roo.bootstrap.version == 3) {
36892             
36893             
36894             if(this.indicatorpos == 'left'){
36895                 label.cn.unshift({
36896                     tag : 'i',
36897                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36898                     tooltip : 'This field is required'
36899                 });
36900             } else {
36901                 label.cn.push({
36902                     tag : 'i',
36903                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36904                     tooltip : 'This field is required'
36905                 });
36906             }
36907         }
36908         var items = {
36909             tag : 'div',
36910             cls : 'roo-radio-set-items'
36911         };
36912         
36913         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36914         
36915         if (align === 'left' && this.fieldLabel.length) {
36916             
36917             items = {
36918                 cls : "roo-radio-set-right", 
36919                 cn: [
36920                     items
36921                 ]
36922             };
36923             
36924             if(this.labelWidth > 12){
36925                 label.style = "width: " + this.labelWidth + 'px';
36926             }
36927             
36928             if(this.labelWidth < 13 && this.labelmd == 0){
36929                 this.labelmd = this.labelWidth;
36930             }
36931             
36932             if(this.labellg > 0){
36933                 label.cls += ' col-lg-' + this.labellg;
36934                 items.cls += ' col-lg-' + (12 - this.labellg);
36935             }
36936             
36937             if(this.labelmd > 0){
36938                 label.cls += ' col-md-' + this.labelmd;
36939                 items.cls += ' col-md-' + (12 - this.labelmd);
36940             }
36941             
36942             if(this.labelsm > 0){
36943                 label.cls += ' col-sm-' + this.labelsm;
36944                 items.cls += ' col-sm-' + (12 - this.labelsm);
36945             }
36946             
36947             if(this.labelxs > 0){
36948                 label.cls += ' col-xs-' + this.labelxs;
36949                 items.cls += ' col-xs-' + (12 - this.labelxs);
36950             }
36951         }
36952         
36953         var cfg = {
36954             tag : 'div',
36955             cls : 'roo-radio-set',
36956             cn : [
36957                 {
36958                     tag : 'input',
36959                     cls : 'roo-radio-set-input',
36960                     type : 'hidden',
36961                     name : this.name,
36962                     value : this.value ? this.value :  ''
36963                 },
36964                 label,
36965                 items
36966             ]
36967         };
36968         
36969         if(this.weight.length){
36970             cfg.cls += ' roo-radio-' + this.weight;
36971         }
36972         
36973         if(this.inline) {
36974             cfg.cls += ' roo-radio-set-inline';
36975         }
36976         
36977         var settings=this;
36978         ['xs','sm','md','lg'].map(function(size){
36979             if (settings[size]) {
36980                 cfg.cls += ' col-' + size + '-' + settings[size];
36981             }
36982         });
36983         
36984         return cfg;
36985         
36986     },
36987
36988     initEvents : function()
36989     {
36990         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36991         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36992         
36993         if(!this.fieldLabel.length){
36994             this.labelEl.hide();
36995         }
36996         
36997         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36998         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36999         
37000         this.indicator = this.indicatorEl();
37001         
37002         if(this.indicator){
37003             this.indicator.addClass('invisible');
37004         }
37005         
37006         this.originalValue = this.getValue();
37007         
37008     },
37009     
37010     inputEl: function ()
37011     {
37012         return this.el.select('.roo-radio-set-input', true).first();
37013     },
37014     
37015     getChildContainer : function()
37016     {
37017         return this.itemsEl;
37018     },
37019     
37020     register : function(item)
37021     {
37022         this.radioes.push(item);
37023         
37024     },
37025     
37026     validate : function()
37027     {   
37028         if(this.getVisibilityEl().hasClass('hidden')){
37029             return true;
37030         }
37031         
37032         var valid = false;
37033         
37034         Roo.each(this.radioes, function(i){
37035             if(!i.checked){
37036                 return;
37037             }
37038             
37039             valid = true;
37040             return false;
37041         });
37042         
37043         if(this.allowBlank) {
37044             return true;
37045         }
37046         
37047         if(this.disabled || valid){
37048             this.markValid();
37049             return true;
37050         }
37051         
37052         this.markInvalid();
37053         return false;
37054         
37055     },
37056     
37057     markValid : function()
37058     {
37059         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37060             this.indicatorEl().removeClass('visible');
37061             this.indicatorEl().addClass('invisible');
37062         }
37063         
37064         
37065         if (Roo.bootstrap.version == 3) {
37066             this.el.removeClass([this.invalidClass, this.validClass]);
37067             this.el.addClass(this.validClass);
37068         } else {
37069             this.el.removeClass(['is-invalid','is-valid']);
37070             this.el.addClass(['is-valid']);
37071         }
37072         this.fireEvent('valid', this);
37073     },
37074     
37075     markInvalid : function(msg)
37076     {
37077         if(this.allowBlank || this.disabled){
37078             return;
37079         }
37080         
37081         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37082             this.indicatorEl().removeClass('invisible');
37083             this.indicatorEl().addClass('visible');
37084         }
37085         if (Roo.bootstrap.version == 3) {
37086             this.el.removeClass([this.invalidClass, this.validClass]);
37087             this.el.addClass(this.invalidClass);
37088         } else {
37089             this.el.removeClass(['is-invalid','is-valid']);
37090             this.el.addClass(['is-invalid']);
37091         }
37092         
37093         this.fireEvent('invalid', this, msg);
37094         
37095     },
37096     
37097     setValue : function(v, suppressEvent)
37098     {   
37099         if(this.value === v){
37100             return;
37101         }
37102         
37103         this.value = v;
37104         
37105         if(this.rendered){
37106             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37107         }
37108         
37109         Roo.each(this.radioes, function(i){
37110             i.checked = false;
37111             i.el.removeClass('checked');
37112         });
37113         
37114         Roo.each(this.radioes, function(i){
37115             
37116             if(i.value === v || i.value.toString() === v.toString()){
37117                 i.checked = true;
37118                 i.el.addClass('checked');
37119                 
37120                 if(suppressEvent !== true){
37121                     this.fireEvent('check', this, i);
37122                 }
37123                 
37124                 return false;
37125             }
37126             
37127         }, this);
37128         
37129         this.validate();
37130     },
37131     
37132     clearInvalid : function(){
37133         
37134         if(!this.el || this.preventMark){
37135             return;
37136         }
37137         
37138         this.el.removeClass([this.invalidClass]);
37139         
37140         this.fireEvent('valid', this);
37141     }
37142     
37143 });
37144
37145 Roo.apply(Roo.bootstrap.RadioSet, {
37146     
37147     groups: {},
37148     
37149     register : function(set)
37150     {
37151         this.groups[set.name] = set;
37152     },
37153     
37154     get: function(name) 
37155     {
37156         if (typeof(this.groups[name]) == 'undefined') {
37157             return false;
37158         }
37159         
37160         return this.groups[name] ;
37161     }
37162     
37163 });
37164 /*
37165  * Based on:
37166  * Ext JS Library 1.1.1
37167  * Copyright(c) 2006-2007, Ext JS, LLC.
37168  *
37169  * Originally Released Under LGPL - original licence link has changed is not relivant.
37170  *
37171  * Fork - LGPL
37172  * <script type="text/javascript">
37173  */
37174
37175
37176 /**
37177  * @class Roo.bootstrap.SplitBar
37178  * @extends Roo.util.Observable
37179  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37180  * <br><br>
37181  * Usage:
37182  * <pre><code>
37183 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37184                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37185 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37186 split.minSize = 100;
37187 split.maxSize = 600;
37188 split.animate = true;
37189 split.on('moved', splitterMoved);
37190 </code></pre>
37191  * @constructor
37192  * Create a new SplitBar
37193  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37194  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37195  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37196  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37197                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37198                         position of the SplitBar).
37199  */
37200 Roo.bootstrap.SplitBar = function(cfg){
37201     
37202     /** @private */
37203     
37204     //{
37205     //  dragElement : elm
37206     //  resizingElement: el,
37207         // optional..
37208     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37209     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37210         // existingProxy ???
37211     //}
37212     
37213     this.el = Roo.get(cfg.dragElement, true);
37214     this.el.dom.unselectable = "on";
37215     /** @private */
37216     this.resizingEl = Roo.get(cfg.resizingElement, true);
37217
37218     /**
37219      * @private
37220      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37221      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37222      * @type Number
37223      */
37224     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37225     
37226     /**
37227      * The minimum size of the resizing element. (Defaults to 0)
37228      * @type Number
37229      */
37230     this.minSize = 0;
37231     
37232     /**
37233      * The maximum size of the resizing element. (Defaults to 2000)
37234      * @type Number
37235      */
37236     this.maxSize = 2000;
37237     
37238     /**
37239      * Whether to animate the transition to the new size
37240      * @type Boolean
37241      */
37242     this.animate = false;
37243     
37244     /**
37245      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37246      * @type Boolean
37247      */
37248     this.useShim = false;
37249     
37250     /** @private */
37251     this.shim = null;
37252     
37253     if(!cfg.existingProxy){
37254         /** @private */
37255         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37256     }else{
37257         this.proxy = Roo.get(cfg.existingProxy).dom;
37258     }
37259     /** @private */
37260     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37261     
37262     /** @private */
37263     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37264     
37265     /** @private */
37266     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37267     
37268     /** @private */
37269     this.dragSpecs = {};
37270     
37271     /**
37272      * @private The adapter to use to positon and resize elements
37273      */
37274     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37275     this.adapter.init(this);
37276     
37277     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37278         /** @private */
37279         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37280         this.el.addClass("roo-splitbar-h");
37281     }else{
37282         /** @private */
37283         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37284         this.el.addClass("roo-splitbar-v");
37285     }
37286     
37287     this.addEvents({
37288         /**
37289          * @event resize
37290          * Fires when the splitter is moved (alias for {@link #event-moved})
37291          * @param {Roo.bootstrap.SplitBar} this
37292          * @param {Number} newSize the new width or height
37293          */
37294         "resize" : true,
37295         /**
37296          * @event moved
37297          * Fires when the splitter is moved
37298          * @param {Roo.bootstrap.SplitBar} this
37299          * @param {Number} newSize the new width or height
37300          */
37301         "moved" : true,
37302         /**
37303          * @event beforeresize
37304          * Fires before the splitter is dragged
37305          * @param {Roo.bootstrap.SplitBar} this
37306          */
37307         "beforeresize" : true,
37308
37309         "beforeapply" : true
37310     });
37311
37312     Roo.util.Observable.call(this);
37313 };
37314
37315 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37316     onStartProxyDrag : function(x, y){
37317         this.fireEvent("beforeresize", this);
37318         if(!this.overlay){
37319             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37320             o.unselectable();
37321             o.enableDisplayMode("block");
37322             // all splitbars share the same overlay
37323             Roo.bootstrap.SplitBar.prototype.overlay = o;
37324         }
37325         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37326         this.overlay.show();
37327         Roo.get(this.proxy).setDisplayed("block");
37328         var size = this.adapter.getElementSize(this);
37329         this.activeMinSize = this.getMinimumSize();;
37330         this.activeMaxSize = this.getMaximumSize();;
37331         var c1 = size - this.activeMinSize;
37332         var c2 = Math.max(this.activeMaxSize - size, 0);
37333         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37334             this.dd.resetConstraints();
37335             this.dd.setXConstraint(
37336                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37337                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37338             );
37339             this.dd.setYConstraint(0, 0);
37340         }else{
37341             this.dd.resetConstraints();
37342             this.dd.setXConstraint(0, 0);
37343             this.dd.setYConstraint(
37344                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37345                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37346             );
37347          }
37348         this.dragSpecs.startSize = size;
37349         this.dragSpecs.startPoint = [x, y];
37350         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37351     },
37352     
37353     /** 
37354      * @private Called after the drag operation by the DDProxy
37355      */
37356     onEndProxyDrag : function(e){
37357         Roo.get(this.proxy).setDisplayed(false);
37358         var endPoint = Roo.lib.Event.getXY(e);
37359         if(this.overlay){
37360             this.overlay.hide();
37361         }
37362         var newSize;
37363         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37364             newSize = this.dragSpecs.startSize + 
37365                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37366                     endPoint[0] - this.dragSpecs.startPoint[0] :
37367                     this.dragSpecs.startPoint[0] - endPoint[0]
37368                 );
37369         }else{
37370             newSize = this.dragSpecs.startSize + 
37371                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37372                     endPoint[1] - this.dragSpecs.startPoint[1] :
37373                     this.dragSpecs.startPoint[1] - endPoint[1]
37374                 );
37375         }
37376         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37377         if(newSize != this.dragSpecs.startSize){
37378             if(this.fireEvent('beforeapply', this, newSize) !== false){
37379                 this.adapter.setElementSize(this, newSize);
37380                 this.fireEvent("moved", this, newSize);
37381                 this.fireEvent("resize", this, newSize);
37382             }
37383         }
37384     },
37385     
37386     /**
37387      * Get the adapter this SplitBar uses
37388      * @return The adapter object
37389      */
37390     getAdapter : function(){
37391         return this.adapter;
37392     },
37393     
37394     /**
37395      * Set the adapter this SplitBar uses
37396      * @param {Object} adapter A SplitBar adapter object
37397      */
37398     setAdapter : function(adapter){
37399         this.adapter = adapter;
37400         this.adapter.init(this);
37401     },
37402     
37403     /**
37404      * Gets the minimum size for the resizing element
37405      * @return {Number} The minimum size
37406      */
37407     getMinimumSize : function(){
37408         return this.minSize;
37409     },
37410     
37411     /**
37412      * Sets the minimum size for the resizing element
37413      * @param {Number} minSize The minimum size
37414      */
37415     setMinimumSize : function(minSize){
37416         this.minSize = minSize;
37417     },
37418     
37419     /**
37420      * Gets the maximum size for the resizing element
37421      * @return {Number} The maximum size
37422      */
37423     getMaximumSize : function(){
37424         return this.maxSize;
37425     },
37426     
37427     /**
37428      * Sets the maximum size for the resizing element
37429      * @param {Number} maxSize The maximum size
37430      */
37431     setMaximumSize : function(maxSize){
37432         this.maxSize = maxSize;
37433     },
37434     
37435     /**
37436      * Sets the initialize size for the resizing element
37437      * @param {Number} size The initial size
37438      */
37439     setCurrentSize : function(size){
37440         var oldAnimate = this.animate;
37441         this.animate = false;
37442         this.adapter.setElementSize(this, size);
37443         this.animate = oldAnimate;
37444     },
37445     
37446     /**
37447      * Destroy this splitbar. 
37448      * @param {Boolean} removeEl True to remove the element
37449      */
37450     destroy : function(removeEl){
37451         if(this.shim){
37452             this.shim.remove();
37453         }
37454         this.dd.unreg();
37455         this.proxy.parentNode.removeChild(this.proxy);
37456         if(removeEl){
37457             this.el.remove();
37458         }
37459     }
37460 });
37461
37462 /**
37463  * @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.
37464  */
37465 Roo.bootstrap.SplitBar.createProxy = function(dir){
37466     var proxy = new Roo.Element(document.createElement("div"));
37467     proxy.unselectable();
37468     var cls = 'roo-splitbar-proxy';
37469     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37470     document.body.appendChild(proxy.dom);
37471     return proxy.dom;
37472 };
37473
37474 /** 
37475  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37476  * Default Adapter. It assumes the splitter and resizing element are not positioned
37477  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37478  */
37479 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37480 };
37481
37482 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37483     // do nothing for now
37484     init : function(s){
37485     
37486     },
37487     /**
37488      * Called before drag operations to get the current size of the resizing element. 
37489      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37490      */
37491      getElementSize : function(s){
37492         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37493             return s.resizingEl.getWidth();
37494         }else{
37495             return s.resizingEl.getHeight();
37496         }
37497     },
37498     
37499     /**
37500      * Called after drag operations to set the size of the resizing element.
37501      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37502      * @param {Number} newSize The new size to set
37503      * @param {Function} onComplete A function to be invoked when resizing is complete
37504      */
37505     setElementSize : function(s, newSize, onComplete){
37506         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37507             if(!s.animate){
37508                 s.resizingEl.setWidth(newSize);
37509                 if(onComplete){
37510                     onComplete(s, newSize);
37511                 }
37512             }else{
37513                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37514             }
37515         }else{
37516             
37517             if(!s.animate){
37518                 s.resizingEl.setHeight(newSize);
37519                 if(onComplete){
37520                     onComplete(s, newSize);
37521                 }
37522             }else{
37523                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37524             }
37525         }
37526     }
37527 };
37528
37529 /** 
37530  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37531  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37532  * Adapter that  moves the splitter element to align with the resized sizing element. 
37533  * Used with an absolute positioned SplitBar.
37534  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37535  * document.body, make sure you assign an id to the body element.
37536  */
37537 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37538     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37539     this.container = Roo.get(container);
37540 };
37541
37542 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37543     init : function(s){
37544         this.basic.init(s);
37545     },
37546     
37547     getElementSize : function(s){
37548         return this.basic.getElementSize(s);
37549     },
37550     
37551     setElementSize : function(s, newSize, onComplete){
37552         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37553     },
37554     
37555     moveSplitter : function(s){
37556         var yes = Roo.bootstrap.SplitBar;
37557         switch(s.placement){
37558             case yes.LEFT:
37559                 s.el.setX(s.resizingEl.getRight());
37560                 break;
37561             case yes.RIGHT:
37562                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37563                 break;
37564             case yes.TOP:
37565                 s.el.setY(s.resizingEl.getBottom());
37566                 break;
37567             case yes.BOTTOM:
37568                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37569                 break;
37570         }
37571     }
37572 };
37573
37574 /**
37575  * Orientation constant - Create a vertical SplitBar
37576  * @static
37577  * @type Number
37578  */
37579 Roo.bootstrap.SplitBar.VERTICAL = 1;
37580
37581 /**
37582  * Orientation constant - Create a horizontal SplitBar
37583  * @static
37584  * @type Number
37585  */
37586 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37587
37588 /**
37589  * Placement constant - The resizing element is to the left of the splitter element
37590  * @static
37591  * @type Number
37592  */
37593 Roo.bootstrap.SplitBar.LEFT = 1;
37594
37595 /**
37596  * Placement constant - The resizing element is to the right of the splitter element
37597  * @static
37598  * @type Number
37599  */
37600 Roo.bootstrap.SplitBar.RIGHT = 2;
37601
37602 /**
37603  * Placement constant - The resizing element is positioned above the splitter element
37604  * @static
37605  * @type Number
37606  */
37607 Roo.bootstrap.SplitBar.TOP = 3;
37608
37609 /**
37610  * Placement constant - The resizing element is positioned under splitter element
37611  * @static
37612  * @type Number
37613  */
37614 Roo.bootstrap.SplitBar.BOTTOM = 4;
37615 Roo.namespace("Roo.bootstrap.layout");/*
37616  * Based on:
37617  * Ext JS Library 1.1.1
37618  * Copyright(c) 2006-2007, Ext JS, LLC.
37619  *
37620  * Originally Released Under LGPL - original licence link has changed is not relivant.
37621  *
37622  * Fork - LGPL
37623  * <script type="text/javascript">
37624  */
37625
37626 /**
37627  * @class Roo.bootstrap.layout.Manager
37628  * @extends Roo.bootstrap.Component
37629  * Base class for layout managers.
37630  */
37631 Roo.bootstrap.layout.Manager = function(config)
37632 {
37633     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37634
37635
37636
37637
37638
37639     /** false to disable window resize monitoring @type Boolean */
37640     this.monitorWindowResize = true;
37641     this.regions = {};
37642     this.addEvents({
37643         /**
37644          * @event layout
37645          * Fires when a layout is performed.
37646          * @param {Roo.LayoutManager} this
37647          */
37648         "layout" : true,
37649         /**
37650          * @event regionresized
37651          * Fires when the user resizes a region.
37652          * @param {Roo.LayoutRegion} region The resized region
37653          * @param {Number} newSize The new size (width for east/west, height for north/south)
37654          */
37655         "regionresized" : true,
37656         /**
37657          * @event regioncollapsed
37658          * Fires when a region is collapsed.
37659          * @param {Roo.LayoutRegion} region The collapsed region
37660          */
37661         "regioncollapsed" : true,
37662         /**
37663          * @event regionexpanded
37664          * Fires when a region is expanded.
37665          * @param {Roo.LayoutRegion} region The expanded region
37666          */
37667         "regionexpanded" : true
37668     });
37669     this.updating = false;
37670
37671     if (config.el) {
37672         this.el = Roo.get(config.el);
37673         this.initEvents();
37674     }
37675
37676 };
37677
37678 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37679
37680
37681     regions : null,
37682
37683     monitorWindowResize : true,
37684
37685
37686     updating : false,
37687
37688
37689     onRender : function(ct, position)
37690     {
37691         if(!this.el){
37692             this.el = Roo.get(ct);
37693             this.initEvents();
37694         }
37695         //this.fireEvent('render',this);
37696     },
37697
37698
37699     initEvents: function()
37700     {
37701
37702
37703         // ie scrollbar fix
37704         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37705             document.body.scroll = "no";
37706         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37707             this.el.position('relative');
37708         }
37709         this.id = this.el.id;
37710         this.el.addClass("roo-layout-container");
37711         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37712         if(this.el.dom != document.body ) {
37713             this.el.on('resize', this.layout,this);
37714             this.el.on('show', this.layout,this);
37715         }
37716
37717     },
37718
37719     /**
37720      * Returns true if this layout is currently being updated
37721      * @return {Boolean}
37722      */
37723     isUpdating : function(){
37724         return this.updating;
37725     },
37726
37727     /**
37728      * Suspend the LayoutManager from doing auto-layouts while
37729      * making multiple add or remove calls
37730      */
37731     beginUpdate : function(){
37732         this.updating = true;
37733     },
37734
37735     /**
37736      * Restore auto-layouts and optionally disable the manager from performing a layout
37737      * @param {Boolean} noLayout true to disable a layout update
37738      */
37739     endUpdate : function(noLayout){
37740         this.updating = false;
37741         if(!noLayout){
37742             this.layout();
37743         }
37744     },
37745
37746     layout: function(){
37747         // abstract...
37748     },
37749
37750     onRegionResized : function(region, newSize){
37751         this.fireEvent("regionresized", region, newSize);
37752         this.layout();
37753     },
37754
37755     onRegionCollapsed : function(region){
37756         this.fireEvent("regioncollapsed", region);
37757     },
37758
37759     onRegionExpanded : function(region){
37760         this.fireEvent("regionexpanded", region);
37761     },
37762
37763     /**
37764      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37765      * performs box-model adjustments.
37766      * @return {Object} The size as an object {width: (the width), height: (the height)}
37767      */
37768     getViewSize : function()
37769     {
37770         var size;
37771         if(this.el.dom != document.body){
37772             size = this.el.getSize();
37773         }else{
37774             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37775         }
37776         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37777         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37778         return size;
37779     },
37780
37781     /**
37782      * Returns the Element this layout is bound to.
37783      * @return {Roo.Element}
37784      */
37785     getEl : function(){
37786         return this.el;
37787     },
37788
37789     /**
37790      * Returns the specified region.
37791      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37792      * @return {Roo.LayoutRegion}
37793      */
37794     getRegion : function(target){
37795         return this.regions[target.toLowerCase()];
37796     },
37797
37798     onWindowResize : function(){
37799         if(this.monitorWindowResize){
37800             this.layout();
37801         }
37802     }
37803 });
37804 /*
37805  * Based on:
37806  * Ext JS Library 1.1.1
37807  * Copyright(c) 2006-2007, Ext JS, LLC.
37808  *
37809  * Originally Released Under LGPL - original licence link has changed is not relivant.
37810  *
37811  * Fork - LGPL
37812  * <script type="text/javascript">
37813  */
37814 /**
37815  * @class Roo.bootstrap.layout.Border
37816  * @extends Roo.bootstrap.layout.Manager
37817  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37818  * please see: examples/bootstrap/nested.html<br><br>
37819  
37820 <b>The container the layout is rendered into can be either the body element or any other element.
37821 If it is not the body element, the container needs to either be an absolute positioned element,
37822 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37823 the container size if it is not the body element.</b>
37824
37825 * @constructor
37826 * Create a new Border
37827 * @param {Object} config Configuration options
37828  */
37829 Roo.bootstrap.layout.Border = function(config){
37830     config = config || {};
37831     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37832     
37833     
37834     
37835     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37836         if(config[region]){
37837             config[region].region = region;
37838             this.addRegion(config[region]);
37839         }
37840     },this);
37841     
37842 };
37843
37844 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37845
37846 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37847     
37848     parent : false, // this might point to a 'nest' or a ???
37849     
37850     /**
37851      * Creates and adds a new region if it doesn't already exist.
37852      * @param {String} target The target region key (north, south, east, west or center).
37853      * @param {Object} config The regions config object
37854      * @return {BorderLayoutRegion} The new region
37855      */
37856     addRegion : function(config)
37857     {
37858         if(!this.regions[config.region]){
37859             var r = this.factory(config);
37860             this.bindRegion(r);
37861         }
37862         return this.regions[config.region];
37863     },
37864
37865     // private (kinda)
37866     bindRegion : function(r){
37867         this.regions[r.config.region] = r;
37868         
37869         r.on("visibilitychange",    this.layout, this);
37870         r.on("paneladded",          this.layout, this);
37871         r.on("panelremoved",        this.layout, this);
37872         r.on("invalidated",         this.layout, this);
37873         r.on("resized",             this.onRegionResized, this);
37874         r.on("collapsed",           this.onRegionCollapsed, this);
37875         r.on("expanded",            this.onRegionExpanded, this);
37876     },
37877
37878     /**
37879      * Performs a layout update.
37880      */
37881     layout : function()
37882     {
37883         if(this.updating) {
37884             return;
37885         }
37886         
37887         // render all the rebions if they have not been done alreayd?
37888         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37889             if(this.regions[region] && !this.regions[region].bodyEl){
37890                 this.regions[region].onRender(this.el)
37891             }
37892         },this);
37893         
37894         var size = this.getViewSize();
37895         var w = size.width;
37896         var h = size.height;
37897         var centerW = w;
37898         var centerH = h;
37899         var centerY = 0;
37900         var centerX = 0;
37901         //var x = 0, y = 0;
37902
37903         var rs = this.regions;
37904         var north = rs["north"];
37905         var south = rs["south"]; 
37906         var west = rs["west"];
37907         var east = rs["east"];
37908         var center = rs["center"];
37909         //if(this.hideOnLayout){ // not supported anymore
37910             //c.el.setStyle("display", "none");
37911         //}
37912         if(north && north.isVisible()){
37913             var b = north.getBox();
37914             var m = north.getMargins();
37915             b.width = w - (m.left+m.right);
37916             b.x = m.left;
37917             b.y = m.top;
37918             centerY = b.height + b.y + m.bottom;
37919             centerH -= centerY;
37920             north.updateBox(this.safeBox(b));
37921         }
37922         if(south && south.isVisible()){
37923             var b = south.getBox();
37924             var m = south.getMargins();
37925             b.width = w - (m.left+m.right);
37926             b.x = m.left;
37927             var totalHeight = (b.height + m.top + m.bottom);
37928             b.y = h - totalHeight + m.top;
37929             centerH -= totalHeight;
37930             south.updateBox(this.safeBox(b));
37931         }
37932         if(west && west.isVisible()){
37933             var b = west.getBox();
37934             var m = west.getMargins();
37935             b.height = centerH - (m.top+m.bottom);
37936             b.x = m.left;
37937             b.y = centerY + m.top;
37938             var totalWidth = (b.width + m.left + m.right);
37939             centerX += totalWidth;
37940             centerW -= totalWidth;
37941             west.updateBox(this.safeBox(b));
37942         }
37943         if(east && east.isVisible()){
37944             var b = east.getBox();
37945             var m = east.getMargins();
37946             b.height = centerH - (m.top+m.bottom);
37947             var totalWidth = (b.width + m.left + m.right);
37948             b.x = w - totalWidth + m.left;
37949             b.y = centerY + m.top;
37950             centerW -= totalWidth;
37951             east.updateBox(this.safeBox(b));
37952         }
37953         if(center){
37954             var m = center.getMargins();
37955             var centerBox = {
37956                 x: centerX + m.left,
37957                 y: centerY + m.top,
37958                 width: centerW - (m.left+m.right),
37959                 height: centerH - (m.top+m.bottom)
37960             };
37961             //if(this.hideOnLayout){
37962                 //center.el.setStyle("display", "block");
37963             //}
37964             center.updateBox(this.safeBox(centerBox));
37965         }
37966         this.el.repaint();
37967         this.fireEvent("layout", this);
37968     },
37969
37970     // private
37971     safeBox : function(box){
37972         box.width = Math.max(0, box.width);
37973         box.height = Math.max(0, box.height);
37974         return box;
37975     },
37976
37977     /**
37978      * Adds a ContentPanel (or subclass) to this layout.
37979      * @param {String} target The target region key (north, south, east, west or center).
37980      * @param {Roo.ContentPanel} panel The panel to add
37981      * @return {Roo.ContentPanel} The added panel
37982      */
37983     add : function(target, panel){
37984          
37985         target = target.toLowerCase();
37986         return this.regions[target].add(panel);
37987     },
37988
37989     /**
37990      * Remove a ContentPanel (or subclass) to this layout.
37991      * @param {String} target The target region key (north, south, east, west or center).
37992      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37993      * @return {Roo.ContentPanel} The removed panel
37994      */
37995     remove : function(target, panel){
37996         target = target.toLowerCase();
37997         return this.regions[target].remove(panel);
37998     },
37999
38000     /**
38001      * Searches all regions for a panel with the specified id
38002      * @param {String} panelId
38003      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38004      */
38005     findPanel : function(panelId){
38006         var rs = this.regions;
38007         for(var target in rs){
38008             if(typeof rs[target] != "function"){
38009                 var p = rs[target].getPanel(panelId);
38010                 if(p){
38011                     return p;
38012                 }
38013             }
38014         }
38015         return null;
38016     },
38017
38018     /**
38019      * Searches all regions for a panel with the specified id and activates (shows) it.
38020      * @param {String/ContentPanel} panelId The panels id or the panel itself
38021      * @return {Roo.ContentPanel} The shown panel or null
38022      */
38023     showPanel : function(panelId) {
38024       var rs = this.regions;
38025       for(var target in rs){
38026          var r = rs[target];
38027          if(typeof r != "function"){
38028             if(r.hasPanel(panelId)){
38029                return r.showPanel(panelId);
38030             }
38031          }
38032       }
38033       return null;
38034    },
38035
38036    /**
38037      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38038      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38039      */
38040    /*
38041     restoreState : function(provider){
38042         if(!provider){
38043             provider = Roo.state.Manager;
38044         }
38045         var sm = new Roo.LayoutStateManager();
38046         sm.init(this, provider);
38047     },
38048 */
38049  
38050  
38051     /**
38052      * Adds a xtype elements to the layout.
38053      * <pre><code>
38054
38055 layout.addxtype({
38056        xtype : 'ContentPanel',
38057        region: 'west',
38058        items: [ .... ]
38059    }
38060 );
38061
38062 layout.addxtype({
38063         xtype : 'NestedLayoutPanel',
38064         region: 'west',
38065         layout: {
38066            center: { },
38067            west: { }   
38068         },
38069         items : [ ... list of content panels or nested layout panels.. ]
38070    }
38071 );
38072 </code></pre>
38073      * @param {Object} cfg Xtype definition of item to add.
38074      */
38075     addxtype : function(cfg)
38076     {
38077         // basically accepts a pannel...
38078         // can accept a layout region..!?!?
38079         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38080         
38081         
38082         // theory?  children can only be panels??
38083         
38084         //if (!cfg.xtype.match(/Panel$/)) {
38085         //    return false;
38086         //}
38087         var ret = false;
38088         
38089         if (typeof(cfg.region) == 'undefined') {
38090             Roo.log("Failed to add Panel, region was not set");
38091             Roo.log(cfg);
38092             return false;
38093         }
38094         var region = cfg.region;
38095         delete cfg.region;
38096         
38097           
38098         var xitems = [];
38099         if (cfg.items) {
38100             xitems = cfg.items;
38101             delete cfg.items;
38102         }
38103         var nb = false;
38104         
38105         if ( region == 'center') {
38106             Roo.log("Center: " + cfg.title);
38107         }
38108         
38109         
38110         switch(cfg.xtype) 
38111         {
38112             case 'Content':  // ContentPanel (el, cfg)
38113             case 'Scroll':  // ContentPanel (el, cfg)
38114             case 'View': 
38115                 cfg.autoCreate = cfg.autoCreate || true;
38116                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38117                 //} else {
38118                 //    var el = this.el.createChild();
38119                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38120                 //}
38121                 
38122                 this.add(region, ret);
38123                 break;
38124             
38125             /*
38126             case 'TreePanel': // our new panel!
38127                 cfg.el = this.el.createChild();
38128                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38129                 this.add(region, ret);
38130                 break;
38131             */
38132             
38133             case 'Nest': 
38134                 // create a new Layout (which is  a Border Layout...
38135                 
38136                 var clayout = cfg.layout;
38137                 clayout.el  = this.el.createChild();
38138                 clayout.items   = clayout.items  || [];
38139                 
38140                 delete cfg.layout;
38141                 
38142                 // replace this exitems with the clayout ones..
38143                 xitems = clayout.items;
38144                  
38145                 // force background off if it's in center...
38146                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38147                     cfg.background = false;
38148                 }
38149                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38150                 
38151                 
38152                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38153                 //console.log('adding nested layout panel '  + cfg.toSource());
38154                 this.add(region, ret);
38155                 nb = {}; /// find first...
38156                 break;
38157             
38158             case 'Grid':
38159                 
38160                 // needs grid and region
38161                 
38162                 //var el = this.getRegion(region).el.createChild();
38163                 /*
38164                  *var el = this.el.createChild();
38165                 // create the grid first...
38166                 cfg.grid.container = el;
38167                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38168                 */
38169                 
38170                 if (region == 'center' && this.active ) {
38171                     cfg.background = false;
38172                 }
38173                 
38174                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38175                 
38176                 this.add(region, ret);
38177                 /*
38178                 if (cfg.background) {
38179                     // render grid on panel activation (if panel background)
38180                     ret.on('activate', function(gp) {
38181                         if (!gp.grid.rendered) {
38182                     //        gp.grid.render(el);
38183                         }
38184                     });
38185                 } else {
38186                   //  cfg.grid.render(el);
38187                 }
38188                 */
38189                 break;
38190            
38191            
38192             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38193                 // it was the old xcomponent building that caused this before.
38194                 // espeically if border is the top element in the tree.
38195                 ret = this;
38196                 break; 
38197                 
38198                     
38199                 
38200                 
38201                 
38202             default:
38203                 /*
38204                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38205                     
38206                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38207                     this.add(region, ret);
38208                 } else {
38209                 */
38210                     Roo.log(cfg);
38211                     throw "Can not add '" + cfg.xtype + "' to Border";
38212                     return null;
38213              
38214                                 
38215              
38216         }
38217         this.beginUpdate();
38218         // add children..
38219         var region = '';
38220         var abn = {};
38221         Roo.each(xitems, function(i)  {
38222             region = nb && i.region ? i.region : false;
38223             
38224             var add = ret.addxtype(i);
38225            
38226             if (region) {
38227                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38228                 if (!i.background) {
38229                     abn[region] = nb[region] ;
38230                 }
38231             }
38232             
38233         });
38234         this.endUpdate();
38235
38236         // make the last non-background panel active..
38237         //if (nb) { Roo.log(abn); }
38238         if (nb) {
38239             
38240             for(var r in abn) {
38241                 region = this.getRegion(r);
38242                 if (region) {
38243                     // tried using nb[r], but it does not work..
38244                      
38245                     region.showPanel(abn[r]);
38246                    
38247                 }
38248             }
38249         }
38250         return ret;
38251         
38252     },
38253     
38254     
38255 // private
38256     factory : function(cfg)
38257     {
38258         
38259         var validRegions = Roo.bootstrap.layout.Border.regions;
38260
38261         var target = cfg.region;
38262         cfg.mgr = this;
38263         
38264         var r = Roo.bootstrap.layout;
38265         Roo.log(target);
38266         switch(target){
38267             case "north":
38268                 return new r.North(cfg);
38269             case "south":
38270                 return new r.South(cfg);
38271             case "east":
38272                 return new r.East(cfg);
38273             case "west":
38274                 return new r.West(cfg);
38275             case "center":
38276                 return new r.Center(cfg);
38277         }
38278         throw 'Layout region "'+target+'" not supported.';
38279     }
38280     
38281     
38282 });
38283  /*
38284  * Based on:
38285  * Ext JS Library 1.1.1
38286  * Copyright(c) 2006-2007, Ext JS, LLC.
38287  *
38288  * Originally Released Under LGPL - original licence link has changed is not relivant.
38289  *
38290  * Fork - LGPL
38291  * <script type="text/javascript">
38292  */
38293  
38294 /**
38295  * @class Roo.bootstrap.layout.Basic
38296  * @extends Roo.util.Observable
38297  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38298  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38299  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38300  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38301  * @cfg {string}   region  the region that it inhabits..
38302  * @cfg {bool}   skipConfig skip config?
38303  * 
38304
38305  */
38306 Roo.bootstrap.layout.Basic = function(config){
38307     
38308     this.mgr = config.mgr;
38309     
38310     this.position = config.region;
38311     
38312     var skipConfig = config.skipConfig;
38313     
38314     this.events = {
38315         /**
38316          * @scope Roo.BasicLayoutRegion
38317          */
38318         
38319         /**
38320          * @event beforeremove
38321          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38322          * @param {Roo.LayoutRegion} this
38323          * @param {Roo.ContentPanel} panel The panel
38324          * @param {Object} e The cancel event object
38325          */
38326         "beforeremove" : true,
38327         /**
38328          * @event invalidated
38329          * Fires when the layout for this region is changed.
38330          * @param {Roo.LayoutRegion} this
38331          */
38332         "invalidated" : true,
38333         /**
38334          * @event visibilitychange
38335          * Fires when this region is shown or hidden 
38336          * @param {Roo.LayoutRegion} this
38337          * @param {Boolean} visibility true or false
38338          */
38339         "visibilitychange" : true,
38340         /**
38341          * @event paneladded
38342          * Fires when a panel is added. 
38343          * @param {Roo.LayoutRegion} this
38344          * @param {Roo.ContentPanel} panel The panel
38345          */
38346         "paneladded" : true,
38347         /**
38348          * @event panelremoved
38349          * Fires when a panel is removed. 
38350          * @param {Roo.LayoutRegion} this
38351          * @param {Roo.ContentPanel} panel The panel
38352          */
38353         "panelremoved" : true,
38354         /**
38355          * @event beforecollapse
38356          * Fires when this region before collapse.
38357          * @param {Roo.LayoutRegion} this
38358          */
38359         "beforecollapse" : true,
38360         /**
38361          * @event collapsed
38362          * Fires when this region is collapsed.
38363          * @param {Roo.LayoutRegion} this
38364          */
38365         "collapsed" : true,
38366         /**
38367          * @event expanded
38368          * Fires when this region is expanded.
38369          * @param {Roo.LayoutRegion} this
38370          */
38371         "expanded" : true,
38372         /**
38373          * @event slideshow
38374          * Fires when this region is slid into view.
38375          * @param {Roo.LayoutRegion} this
38376          */
38377         "slideshow" : true,
38378         /**
38379          * @event slidehide
38380          * Fires when this region slides out of view. 
38381          * @param {Roo.LayoutRegion} this
38382          */
38383         "slidehide" : true,
38384         /**
38385          * @event panelactivated
38386          * Fires when a panel is activated. 
38387          * @param {Roo.LayoutRegion} this
38388          * @param {Roo.ContentPanel} panel The activated panel
38389          */
38390         "panelactivated" : true,
38391         /**
38392          * @event resized
38393          * Fires when the user resizes this region. 
38394          * @param {Roo.LayoutRegion} this
38395          * @param {Number} newSize The new size (width for east/west, height for north/south)
38396          */
38397         "resized" : true
38398     };
38399     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38400     this.panels = new Roo.util.MixedCollection();
38401     this.panels.getKey = this.getPanelId.createDelegate(this);
38402     this.box = null;
38403     this.activePanel = null;
38404     // ensure listeners are added...
38405     
38406     if (config.listeners || config.events) {
38407         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38408             listeners : config.listeners || {},
38409             events : config.events || {}
38410         });
38411     }
38412     
38413     if(skipConfig !== true){
38414         this.applyConfig(config);
38415     }
38416 };
38417
38418 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38419 {
38420     getPanelId : function(p){
38421         return p.getId();
38422     },
38423     
38424     applyConfig : function(config){
38425         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38426         this.config = config;
38427         
38428     },
38429     
38430     /**
38431      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38432      * the width, for horizontal (north, south) the height.
38433      * @param {Number} newSize The new width or height
38434      */
38435     resizeTo : function(newSize){
38436         var el = this.el ? this.el :
38437                  (this.activePanel ? this.activePanel.getEl() : null);
38438         if(el){
38439             switch(this.position){
38440                 case "east":
38441                 case "west":
38442                     el.setWidth(newSize);
38443                     this.fireEvent("resized", this, newSize);
38444                 break;
38445                 case "north":
38446                 case "south":
38447                     el.setHeight(newSize);
38448                     this.fireEvent("resized", this, newSize);
38449                 break;                
38450             }
38451         }
38452     },
38453     
38454     getBox : function(){
38455         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38456     },
38457     
38458     getMargins : function(){
38459         return this.margins;
38460     },
38461     
38462     updateBox : function(box){
38463         this.box = box;
38464         var el = this.activePanel.getEl();
38465         el.dom.style.left = box.x + "px";
38466         el.dom.style.top = box.y + "px";
38467         this.activePanel.setSize(box.width, box.height);
38468     },
38469     
38470     /**
38471      * Returns the container element for this region.
38472      * @return {Roo.Element}
38473      */
38474     getEl : function(){
38475         return this.activePanel;
38476     },
38477     
38478     /**
38479      * Returns true if this region is currently visible.
38480      * @return {Boolean}
38481      */
38482     isVisible : function(){
38483         return this.activePanel ? true : false;
38484     },
38485     
38486     setActivePanel : function(panel){
38487         panel = this.getPanel(panel);
38488         if(this.activePanel && this.activePanel != panel){
38489             this.activePanel.setActiveState(false);
38490             this.activePanel.getEl().setLeftTop(-10000,-10000);
38491         }
38492         this.activePanel = panel;
38493         panel.setActiveState(true);
38494         if(this.box){
38495             panel.setSize(this.box.width, this.box.height);
38496         }
38497         this.fireEvent("panelactivated", this, panel);
38498         this.fireEvent("invalidated");
38499     },
38500     
38501     /**
38502      * Show the specified panel.
38503      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38504      * @return {Roo.ContentPanel} The shown panel or null
38505      */
38506     showPanel : function(panel){
38507         panel = this.getPanel(panel);
38508         if(panel){
38509             this.setActivePanel(panel);
38510         }
38511         return panel;
38512     },
38513     
38514     /**
38515      * Get the active panel for this region.
38516      * @return {Roo.ContentPanel} The active panel or null
38517      */
38518     getActivePanel : function(){
38519         return this.activePanel;
38520     },
38521     
38522     /**
38523      * Add the passed ContentPanel(s)
38524      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38525      * @return {Roo.ContentPanel} The panel added (if only one was added)
38526      */
38527     add : function(panel){
38528         if(arguments.length > 1){
38529             for(var i = 0, len = arguments.length; i < len; i++) {
38530                 this.add(arguments[i]);
38531             }
38532             return null;
38533         }
38534         if(this.hasPanel(panel)){
38535             this.showPanel(panel);
38536             return panel;
38537         }
38538         var el = panel.getEl();
38539         if(el.dom.parentNode != this.mgr.el.dom){
38540             this.mgr.el.dom.appendChild(el.dom);
38541         }
38542         if(panel.setRegion){
38543             panel.setRegion(this);
38544         }
38545         this.panels.add(panel);
38546         el.setStyle("position", "absolute");
38547         if(!panel.background){
38548             this.setActivePanel(panel);
38549             if(this.config.initialSize && this.panels.getCount()==1){
38550                 this.resizeTo(this.config.initialSize);
38551             }
38552         }
38553         this.fireEvent("paneladded", this, panel);
38554         return panel;
38555     },
38556     
38557     /**
38558      * Returns true if the panel is in this region.
38559      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38560      * @return {Boolean}
38561      */
38562     hasPanel : function(panel){
38563         if(typeof panel == "object"){ // must be panel obj
38564             panel = panel.getId();
38565         }
38566         return this.getPanel(panel) ? true : false;
38567     },
38568     
38569     /**
38570      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38571      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38572      * @param {Boolean} preservePanel Overrides the config preservePanel option
38573      * @return {Roo.ContentPanel} The panel that was removed
38574      */
38575     remove : function(panel, preservePanel){
38576         panel = this.getPanel(panel);
38577         if(!panel){
38578             return null;
38579         }
38580         var e = {};
38581         this.fireEvent("beforeremove", this, panel, e);
38582         if(e.cancel === true){
38583             return null;
38584         }
38585         var panelId = panel.getId();
38586         this.panels.removeKey(panelId);
38587         return panel;
38588     },
38589     
38590     /**
38591      * Returns the panel specified or null if it's not in this region.
38592      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38593      * @return {Roo.ContentPanel}
38594      */
38595     getPanel : function(id){
38596         if(typeof id == "object"){ // must be panel obj
38597             return id;
38598         }
38599         return this.panels.get(id);
38600     },
38601     
38602     /**
38603      * Returns this regions position (north/south/east/west/center).
38604      * @return {String} 
38605      */
38606     getPosition: function(){
38607         return this.position;    
38608     }
38609 });/*
38610  * Based on:
38611  * Ext JS Library 1.1.1
38612  * Copyright(c) 2006-2007, Ext JS, LLC.
38613  *
38614  * Originally Released Under LGPL - original licence link has changed is not relivant.
38615  *
38616  * Fork - LGPL
38617  * <script type="text/javascript">
38618  */
38619  
38620 /**
38621  * @class Roo.bootstrap.layout.Region
38622  * @extends Roo.bootstrap.layout.Basic
38623  * This class represents a region in a layout manager.
38624  
38625  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38626  * @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})
38627  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38628  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38629  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38630  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38631  * @cfg {String}    title           The title for the region (overrides panel titles)
38632  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38633  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38634  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38635  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38636  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38637  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38638  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38639  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38640  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38641  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38642
38643  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38644  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38645  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38646  * @cfg {Number}    width           For East/West panels
38647  * @cfg {Number}    height          For North/South panels
38648  * @cfg {Boolean}   split           To show the splitter
38649  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38650  * 
38651  * @cfg {string}   cls             Extra CSS classes to add to region
38652  * 
38653  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38654  * @cfg {string}   region  the region that it inhabits..
38655  *
38656
38657  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38658  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38659
38660  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38661  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38662  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38663  */
38664 Roo.bootstrap.layout.Region = function(config)
38665 {
38666     this.applyConfig(config);
38667
38668     var mgr = config.mgr;
38669     var pos = config.region;
38670     config.skipConfig = true;
38671     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38672     
38673     if (mgr.el) {
38674         this.onRender(mgr.el);   
38675     }
38676      
38677     this.visible = true;
38678     this.collapsed = false;
38679     this.unrendered_panels = [];
38680 };
38681
38682 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38683
38684     position: '', // set by wrapper (eg. north/south etc..)
38685     unrendered_panels : null,  // unrendered panels.
38686     
38687     tabPosition : false,
38688     
38689     mgr: false, // points to 'Border'
38690     
38691     
38692     createBody : function(){
38693         /** This region's body element 
38694         * @type Roo.Element */
38695         this.bodyEl = this.el.createChild({
38696                 tag: "div",
38697                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38698         });
38699     },
38700
38701     onRender: function(ctr, pos)
38702     {
38703         var dh = Roo.DomHelper;
38704         /** This region's container element 
38705         * @type Roo.Element */
38706         this.el = dh.append(ctr.dom, {
38707                 tag: "div",
38708                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38709             }, true);
38710         /** This region's title element 
38711         * @type Roo.Element */
38712     
38713         this.titleEl = dh.append(this.el.dom,  {
38714                 tag: "div",
38715                 unselectable: "on",
38716                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38717                 children:[
38718                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38719                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38720                 ]
38721             }, true);
38722         
38723         this.titleEl.enableDisplayMode();
38724         /** This region's title text element 
38725         * @type HTMLElement */
38726         this.titleTextEl = this.titleEl.dom.firstChild;
38727         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38728         /*
38729         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38730         this.closeBtn.enableDisplayMode();
38731         this.closeBtn.on("click", this.closeClicked, this);
38732         this.closeBtn.hide();
38733     */
38734         this.createBody(this.config);
38735         if(this.config.hideWhenEmpty){
38736             this.hide();
38737             this.on("paneladded", this.validateVisibility, this);
38738             this.on("panelremoved", this.validateVisibility, this);
38739         }
38740         if(this.autoScroll){
38741             this.bodyEl.setStyle("overflow", "auto");
38742         }else{
38743             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38744         }
38745         //if(c.titlebar !== false){
38746             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38747                 this.titleEl.hide();
38748             }else{
38749                 this.titleEl.show();
38750                 if(this.config.title){
38751                     this.titleTextEl.innerHTML = this.config.title;
38752                 }
38753             }
38754         //}
38755         if(this.config.collapsed){
38756             this.collapse(true);
38757         }
38758         if(this.config.hidden){
38759             this.hide();
38760         }
38761         
38762         if (this.unrendered_panels && this.unrendered_panels.length) {
38763             for (var i =0;i< this.unrendered_panels.length; i++) {
38764                 this.add(this.unrendered_panels[i]);
38765             }
38766             this.unrendered_panels = null;
38767             
38768         }
38769         
38770     },
38771     
38772     applyConfig : function(c)
38773     {
38774         /*
38775          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38776             var dh = Roo.DomHelper;
38777             if(c.titlebar !== false){
38778                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38779                 this.collapseBtn.on("click", this.collapse, this);
38780                 this.collapseBtn.enableDisplayMode();
38781                 /*
38782                 if(c.showPin === true || this.showPin){
38783                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38784                     this.stickBtn.enableDisplayMode();
38785                     this.stickBtn.on("click", this.expand, this);
38786                     this.stickBtn.hide();
38787                 }
38788                 
38789             }
38790             */
38791             /** This region's collapsed element
38792             * @type Roo.Element */
38793             /*
38794              *
38795             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38796                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38797             ]}, true);
38798             
38799             if(c.floatable !== false){
38800                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38801                this.collapsedEl.on("click", this.collapseClick, this);
38802             }
38803
38804             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38805                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38806                    id: "message", unselectable: "on", style:{"float":"left"}});
38807                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38808              }
38809             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38810             this.expandBtn.on("click", this.expand, this);
38811             
38812         }
38813         
38814         if(this.collapseBtn){
38815             this.collapseBtn.setVisible(c.collapsible == true);
38816         }
38817         
38818         this.cmargins = c.cmargins || this.cmargins ||
38819                          (this.position == "west" || this.position == "east" ?
38820                              {top: 0, left: 2, right:2, bottom: 0} :
38821                              {top: 2, left: 0, right:0, bottom: 2});
38822         */
38823         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38824         
38825         
38826         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38827         
38828         this.autoScroll = c.autoScroll || false;
38829         
38830         
38831        
38832         
38833         this.duration = c.duration || .30;
38834         this.slideDuration = c.slideDuration || .45;
38835         this.config = c;
38836        
38837     },
38838     /**
38839      * Returns true if this region is currently visible.
38840      * @return {Boolean}
38841      */
38842     isVisible : function(){
38843         return this.visible;
38844     },
38845
38846     /**
38847      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38848      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38849      */
38850     //setCollapsedTitle : function(title){
38851     //    title = title || "&#160;";
38852      //   if(this.collapsedTitleTextEl){
38853       //      this.collapsedTitleTextEl.innerHTML = title;
38854        // }
38855     //},
38856
38857     getBox : function(){
38858         var b;
38859       //  if(!this.collapsed){
38860             b = this.el.getBox(false, true);
38861        // }else{
38862           //  b = this.collapsedEl.getBox(false, true);
38863         //}
38864         return b;
38865     },
38866
38867     getMargins : function(){
38868         return this.margins;
38869         //return this.collapsed ? this.cmargins : this.margins;
38870     },
38871 /*
38872     highlight : function(){
38873         this.el.addClass("x-layout-panel-dragover");
38874     },
38875
38876     unhighlight : function(){
38877         this.el.removeClass("x-layout-panel-dragover");
38878     },
38879 */
38880     updateBox : function(box)
38881     {
38882         if (!this.bodyEl) {
38883             return; // not rendered yet..
38884         }
38885         
38886         this.box = box;
38887         if(!this.collapsed){
38888             this.el.dom.style.left = box.x + "px";
38889             this.el.dom.style.top = box.y + "px";
38890             this.updateBody(box.width, box.height);
38891         }else{
38892             this.collapsedEl.dom.style.left = box.x + "px";
38893             this.collapsedEl.dom.style.top = box.y + "px";
38894             this.collapsedEl.setSize(box.width, box.height);
38895         }
38896         if(this.tabs){
38897             this.tabs.autoSizeTabs();
38898         }
38899     },
38900
38901     updateBody : function(w, h)
38902     {
38903         if(w !== null){
38904             this.el.setWidth(w);
38905             w -= this.el.getBorderWidth("rl");
38906             if(this.config.adjustments){
38907                 w += this.config.adjustments[0];
38908             }
38909         }
38910         if(h !== null && h > 0){
38911             this.el.setHeight(h);
38912             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38913             h -= this.el.getBorderWidth("tb");
38914             if(this.config.adjustments){
38915                 h += this.config.adjustments[1];
38916             }
38917             this.bodyEl.setHeight(h);
38918             if(this.tabs){
38919                 h = this.tabs.syncHeight(h);
38920             }
38921         }
38922         if(this.panelSize){
38923             w = w !== null ? w : this.panelSize.width;
38924             h = h !== null ? h : this.panelSize.height;
38925         }
38926         if(this.activePanel){
38927             var el = this.activePanel.getEl();
38928             w = w !== null ? w : el.getWidth();
38929             h = h !== null ? h : el.getHeight();
38930             this.panelSize = {width: w, height: h};
38931             this.activePanel.setSize(w, h);
38932         }
38933         if(Roo.isIE && this.tabs){
38934             this.tabs.el.repaint();
38935         }
38936     },
38937
38938     /**
38939      * Returns the container element for this region.
38940      * @return {Roo.Element}
38941      */
38942     getEl : function(){
38943         return this.el;
38944     },
38945
38946     /**
38947      * Hides this region.
38948      */
38949     hide : function(){
38950         //if(!this.collapsed){
38951             this.el.dom.style.left = "-2000px";
38952             this.el.hide();
38953         //}else{
38954          //   this.collapsedEl.dom.style.left = "-2000px";
38955          //   this.collapsedEl.hide();
38956        // }
38957         this.visible = false;
38958         this.fireEvent("visibilitychange", this, false);
38959     },
38960
38961     /**
38962      * Shows this region if it was previously hidden.
38963      */
38964     show : function(){
38965         //if(!this.collapsed){
38966             this.el.show();
38967         //}else{
38968         //    this.collapsedEl.show();
38969        // }
38970         this.visible = true;
38971         this.fireEvent("visibilitychange", this, true);
38972     },
38973 /*
38974     closeClicked : function(){
38975         if(this.activePanel){
38976             this.remove(this.activePanel);
38977         }
38978     },
38979
38980     collapseClick : function(e){
38981         if(this.isSlid){
38982            e.stopPropagation();
38983            this.slideIn();
38984         }else{
38985            e.stopPropagation();
38986            this.slideOut();
38987         }
38988     },
38989 */
38990     /**
38991      * Collapses this region.
38992      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38993      */
38994     /*
38995     collapse : function(skipAnim, skipCheck = false){
38996         if(this.collapsed) {
38997             return;
38998         }
38999         
39000         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39001             
39002             this.collapsed = true;
39003             if(this.split){
39004                 this.split.el.hide();
39005             }
39006             if(this.config.animate && skipAnim !== true){
39007                 this.fireEvent("invalidated", this);
39008                 this.animateCollapse();
39009             }else{
39010                 this.el.setLocation(-20000,-20000);
39011                 this.el.hide();
39012                 this.collapsedEl.show();
39013                 this.fireEvent("collapsed", this);
39014                 this.fireEvent("invalidated", this);
39015             }
39016         }
39017         
39018     },
39019 */
39020     animateCollapse : function(){
39021         // overridden
39022     },
39023
39024     /**
39025      * Expands this region if it was previously collapsed.
39026      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39027      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39028      */
39029     /*
39030     expand : function(e, skipAnim){
39031         if(e) {
39032             e.stopPropagation();
39033         }
39034         if(!this.collapsed || this.el.hasActiveFx()) {
39035             return;
39036         }
39037         if(this.isSlid){
39038             this.afterSlideIn();
39039             skipAnim = true;
39040         }
39041         this.collapsed = false;
39042         if(this.config.animate && skipAnim !== true){
39043             this.animateExpand();
39044         }else{
39045             this.el.show();
39046             if(this.split){
39047                 this.split.el.show();
39048             }
39049             this.collapsedEl.setLocation(-2000,-2000);
39050             this.collapsedEl.hide();
39051             this.fireEvent("invalidated", this);
39052             this.fireEvent("expanded", this);
39053         }
39054     },
39055 */
39056     animateExpand : function(){
39057         // overridden
39058     },
39059
39060     initTabs : function()
39061     {
39062         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39063         
39064         var ts = new Roo.bootstrap.panel.Tabs({
39065             el: this.bodyEl.dom,
39066             region : this,
39067             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39068             disableTooltips: this.config.disableTabTips,
39069             toolbar : this.config.toolbar
39070         });
39071         
39072         if(this.config.hideTabs){
39073             ts.stripWrap.setDisplayed(false);
39074         }
39075         this.tabs = ts;
39076         ts.resizeTabs = this.config.resizeTabs === true;
39077         ts.minTabWidth = this.config.minTabWidth || 40;
39078         ts.maxTabWidth = this.config.maxTabWidth || 250;
39079         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39080         ts.monitorResize = false;
39081         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39082         ts.bodyEl.addClass('roo-layout-tabs-body');
39083         this.panels.each(this.initPanelAsTab, this);
39084     },
39085
39086     initPanelAsTab : function(panel){
39087         var ti = this.tabs.addTab(
39088             panel.getEl().id,
39089             panel.getTitle(),
39090             null,
39091             this.config.closeOnTab && panel.isClosable(),
39092             panel.tpl
39093         );
39094         if(panel.tabTip !== undefined){
39095             ti.setTooltip(panel.tabTip);
39096         }
39097         ti.on("activate", function(){
39098               this.setActivePanel(panel);
39099         }, this);
39100         
39101         if(this.config.closeOnTab){
39102             ti.on("beforeclose", function(t, e){
39103                 e.cancel = true;
39104                 this.remove(panel);
39105             }, this);
39106         }
39107         
39108         panel.tabItem = ti;
39109         
39110         return ti;
39111     },
39112
39113     updatePanelTitle : function(panel, title)
39114     {
39115         if(this.activePanel == panel){
39116             this.updateTitle(title);
39117         }
39118         if(this.tabs){
39119             var ti = this.tabs.getTab(panel.getEl().id);
39120             ti.setText(title);
39121             if(panel.tabTip !== undefined){
39122                 ti.setTooltip(panel.tabTip);
39123             }
39124         }
39125     },
39126
39127     updateTitle : function(title){
39128         if(this.titleTextEl && !this.config.title){
39129             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39130         }
39131     },
39132
39133     setActivePanel : function(panel)
39134     {
39135         panel = this.getPanel(panel);
39136         if(this.activePanel && this.activePanel != panel){
39137             if(this.activePanel.setActiveState(false) === false){
39138                 return;
39139             }
39140         }
39141         this.activePanel = panel;
39142         panel.setActiveState(true);
39143         if(this.panelSize){
39144             panel.setSize(this.panelSize.width, this.panelSize.height);
39145         }
39146         if(this.closeBtn){
39147             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39148         }
39149         this.updateTitle(panel.getTitle());
39150         if(this.tabs){
39151             this.fireEvent("invalidated", this);
39152         }
39153         this.fireEvent("panelactivated", this, panel);
39154     },
39155
39156     /**
39157      * Shows the specified panel.
39158      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39159      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39160      */
39161     showPanel : function(panel)
39162     {
39163         panel = this.getPanel(panel);
39164         if(panel){
39165             if(this.tabs){
39166                 var tab = this.tabs.getTab(panel.getEl().id);
39167                 if(tab.isHidden()){
39168                     this.tabs.unhideTab(tab.id);
39169                 }
39170                 tab.activate();
39171             }else{
39172                 this.setActivePanel(panel);
39173             }
39174         }
39175         return panel;
39176     },
39177
39178     /**
39179      * Get the active panel for this region.
39180      * @return {Roo.ContentPanel} The active panel or null
39181      */
39182     getActivePanel : function(){
39183         return this.activePanel;
39184     },
39185
39186     validateVisibility : function(){
39187         if(this.panels.getCount() < 1){
39188             this.updateTitle("&#160;");
39189             this.closeBtn.hide();
39190             this.hide();
39191         }else{
39192             if(!this.isVisible()){
39193                 this.show();
39194             }
39195         }
39196     },
39197
39198     /**
39199      * Adds the passed ContentPanel(s) to this region.
39200      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39201      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39202      */
39203     add : function(panel)
39204     {
39205         if(arguments.length > 1){
39206             for(var i = 0, len = arguments.length; i < len; i++) {
39207                 this.add(arguments[i]);
39208             }
39209             return null;
39210         }
39211         
39212         // if we have not been rendered yet, then we can not really do much of this..
39213         if (!this.bodyEl) {
39214             this.unrendered_panels.push(panel);
39215             return panel;
39216         }
39217         
39218         
39219         
39220         
39221         if(this.hasPanel(panel)){
39222             this.showPanel(panel);
39223             return panel;
39224         }
39225         panel.setRegion(this);
39226         this.panels.add(panel);
39227        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39228             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39229             // and hide them... ???
39230             this.bodyEl.dom.appendChild(panel.getEl().dom);
39231             if(panel.background !== true){
39232                 this.setActivePanel(panel);
39233             }
39234             this.fireEvent("paneladded", this, panel);
39235             return panel;
39236         }
39237         */
39238         if(!this.tabs){
39239             this.initTabs();
39240         }else{
39241             this.initPanelAsTab(panel);
39242         }
39243         
39244         
39245         if(panel.background !== true){
39246             this.tabs.activate(panel.getEl().id);
39247         }
39248         this.fireEvent("paneladded", this, panel);
39249         return panel;
39250     },
39251
39252     /**
39253      * Hides the tab for the specified panel.
39254      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39255      */
39256     hidePanel : function(panel){
39257         if(this.tabs && (panel = this.getPanel(panel))){
39258             this.tabs.hideTab(panel.getEl().id);
39259         }
39260     },
39261
39262     /**
39263      * Unhides the tab for a previously hidden panel.
39264      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39265      */
39266     unhidePanel : function(panel){
39267         if(this.tabs && (panel = this.getPanel(panel))){
39268             this.tabs.unhideTab(panel.getEl().id);
39269         }
39270     },
39271
39272     clearPanels : function(){
39273         while(this.panels.getCount() > 0){
39274              this.remove(this.panels.first());
39275         }
39276     },
39277
39278     /**
39279      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39280      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39281      * @param {Boolean} preservePanel Overrides the config preservePanel option
39282      * @return {Roo.ContentPanel} The panel that was removed
39283      */
39284     remove : function(panel, preservePanel)
39285     {
39286         panel = this.getPanel(panel);
39287         if(!panel){
39288             return null;
39289         }
39290         var e = {};
39291         this.fireEvent("beforeremove", this, panel, e);
39292         if(e.cancel === true){
39293             return null;
39294         }
39295         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39296         var panelId = panel.getId();
39297         this.panels.removeKey(panelId);
39298         if(preservePanel){
39299             document.body.appendChild(panel.getEl().dom);
39300         }
39301         if(this.tabs){
39302             this.tabs.removeTab(panel.getEl().id);
39303         }else if (!preservePanel){
39304             this.bodyEl.dom.removeChild(panel.getEl().dom);
39305         }
39306         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39307             var p = this.panels.first();
39308             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39309             tempEl.appendChild(p.getEl().dom);
39310             this.bodyEl.update("");
39311             this.bodyEl.dom.appendChild(p.getEl().dom);
39312             tempEl = null;
39313             this.updateTitle(p.getTitle());
39314             this.tabs = null;
39315             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39316             this.setActivePanel(p);
39317         }
39318         panel.setRegion(null);
39319         if(this.activePanel == panel){
39320             this.activePanel = null;
39321         }
39322         if(this.config.autoDestroy !== false && preservePanel !== true){
39323             try{panel.destroy();}catch(e){}
39324         }
39325         this.fireEvent("panelremoved", this, panel);
39326         return panel;
39327     },
39328
39329     /**
39330      * Returns the TabPanel component used by this region
39331      * @return {Roo.TabPanel}
39332      */
39333     getTabs : function(){
39334         return this.tabs;
39335     },
39336
39337     createTool : function(parentEl, className){
39338         var btn = Roo.DomHelper.append(parentEl, {
39339             tag: "div",
39340             cls: "x-layout-tools-button",
39341             children: [ {
39342                 tag: "div",
39343                 cls: "roo-layout-tools-button-inner " + className,
39344                 html: "&#160;"
39345             }]
39346         }, true);
39347         btn.addClassOnOver("roo-layout-tools-button-over");
39348         return btn;
39349     }
39350 });/*
39351  * Based on:
39352  * Ext JS Library 1.1.1
39353  * Copyright(c) 2006-2007, Ext JS, LLC.
39354  *
39355  * Originally Released Under LGPL - original licence link has changed is not relivant.
39356  *
39357  * Fork - LGPL
39358  * <script type="text/javascript">
39359  */
39360  
39361
39362
39363 /**
39364  * @class Roo.SplitLayoutRegion
39365  * @extends Roo.LayoutRegion
39366  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39367  */
39368 Roo.bootstrap.layout.Split = function(config){
39369     this.cursor = config.cursor;
39370     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39371 };
39372
39373 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39374 {
39375     splitTip : "Drag to resize.",
39376     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39377     useSplitTips : false,
39378
39379     applyConfig : function(config){
39380         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39381     },
39382     
39383     onRender : function(ctr,pos) {
39384         
39385         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39386         if(!this.config.split){
39387             return;
39388         }
39389         if(!this.split){
39390             
39391             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39392                             tag: "div",
39393                             id: this.el.id + "-split",
39394                             cls: "roo-layout-split roo-layout-split-"+this.position,
39395                             html: "&#160;"
39396             });
39397             /** The SplitBar for this region 
39398             * @type Roo.SplitBar */
39399             // does not exist yet...
39400             Roo.log([this.position, this.orientation]);
39401             
39402             this.split = new Roo.bootstrap.SplitBar({
39403                 dragElement : splitEl,
39404                 resizingElement: this.el,
39405                 orientation : this.orientation
39406             });
39407             
39408             this.split.on("moved", this.onSplitMove, this);
39409             this.split.useShim = this.config.useShim === true;
39410             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39411             if(this.useSplitTips){
39412                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39413             }
39414             //if(config.collapsible){
39415             //    this.split.el.on("dblclick", this.collapse,  this);
39416             //}
39417         }
39418         if(typeof this.config.minSize != "undefined"){
39419             this.split.minSize = this.config.minSize;
39420         }
39421         if(typeof this.config.maxSize != "undefined"){
39422             this.split.maxSize = this.config.maxSize;
39423         }
39424         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39425             this.hideSplitter();
39426         }
39427         
39428     },
39429
39430     getHMaxSize : function(){
39431          var cmax = this.config.maxSize || 10000;
39432          var center = this.mgr.getRegion("center");
39433          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39434     },
39435
39436     getVMaxSize : function(){
39437          var cmax = this.config.maxSize || 10000;
39438          var center = this.mgr.getRegion("center");
39439          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39440     },
39441
39442     onSplitMove : function(split, newSize){
39443         this.fireEvent("resized", this, newSize);
39444     },
39445     
39446     /** 
39447      * Returns the {@link Roo.SplitBar} for this region.
39448      * @return {Roo.SplitBar}
39449      */
39450     getSplitBar : function(){
39451         return this.split;
39452     },
39453     
39454     hide : function(){
39455         this.hideSplitter();
39456         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39457     },
39458
39459     hideSplitter : function(){
39460         if(this.split){
39461             this.split.el.setLocation(-2000,-2000);
39462             this.split.el.hide();
39463         }
39464     },
39465
39466     show : function(){
39467         if(this.split){
39468             this.split.el.show();
39469         }
39470         Roo.bootstrap.layout.Split.superclass.show.call(this);
39471     },
39472     
39473     beforeSlide: function(){
39474         if(Roo.isGecko){// firefox overflow auto bug workaround
39475             this.bodyEl.clip();
39476             if(this.tabs) {
39477                 this.tabs.bodyEl.clip();
39478             }
39479             if(this.activePanel){
39480                 this.activePanel.getEl().clip();
39481                 
39482                 if(this.activePanel.beforeSlide){
39483                     this.activePanel.beforeSlide();
39484                 }
39485             }
39486         }
39487     },
39488     
39489     afterSlide : function(){
39490         if(Roo.isGecko){// firefox overflow auto bug workaround
39491             this.bodyEl.unclip();
39492             if(this.tabs) {
39493                 this.tabs.bodyEl.unclip();
39494             }
39495             if(this.activePanel){
39496                 this.activePanel.getEl().unclip();
39497                 if(this.activePanel.afterSlide){
39498                     this.activePanel.afterSlide();
39499                 }
39500             }
39501         }
39502     },
39503
39504     initAutoHide : function(){
39505         if(this.autoHide !== false){
39506             if(!this.autoHideHd){
39507                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39508                 this.autoHideHd = {
39509                     "mouseout": function(e){
39510                         if(!e.within(this.el, true)){
39511                             st.delay(500);
39512                         }
39513                     },
39514                     "mouseover" : function(e){
39515                         st.cancel();
39516                     },
39517                     scope : this
39518                 };
39519             }
39520             this.el.on(this.autoHideHd);
39521         }
39522     },
39523
39524     clearAutoHide : function(){
39525         if(this.autoHide !== false){
39526             this.el.un("mouseout", this.autoHideHd.mouseout);
39527             this.el.un("mouseover", this.autoHideHd.mouseover);
39528         }
39529     },
39530
39531     clearMonitor : function(){
39532         Roo.get(document).un("click", this.slideInIf, this);
39533     },
39534
39535     // these names are backwards but not changed for compat
39536     slideOut : function(){
39537         if(this.isSlid || this.el.hasActiveFx()){
39538             return;
39539         }
39540         this.isSlid = true;
39541         if(this.collapseBtn){
39542             this.collapseBtn.hide();
39543         }
39544         this.closeBtnState = this.closeBtn.getStyle('display');
39545         this.closeBtn.hide();
39546         if(this.stickBtn){
39547             this.stickBtn.show();
39548         }
39549         this.el.show();
39550         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39551         this.beforeSlide();
39552         this.el.setStyle("z-index", 10001);
39553         this.el.slideIn(this.getSlideAnchor(), {
39554             callback: function(){
39555                 this.afterSlide();
39556                 this.initAutoHide();
39557                 Roo.get(document).on("click", this.slideInIf, this);
39558                 this.fireEvent("slideshow", this);
39559             },
39560             scope: this,
39561             block: true
39562         });
39563     },
39564
39565     afterSlideIn : function(){
39566         this.clearAutoHide();
39567         this.isSlid = false;
39568         this.clearMonitor();
39569         this.el.setStyle("z-index", "");
39570         if(this.collapseBtn){
39571             this.collapseBtn.show();
39572         }
39573         this.closeBtn.setStyle('display', this.closeBtnState);
39574         if(this.stickBtn){
39575             this.stickBtn.hide();
39576         }
39577         this.fireEvent("slidehide", this);
39578     },
39579
39580     slideIn : function(cb){
39581         if(!this.isSlid || this.el.hasActiveFx()){
39582             Roo.callback(cb);
39583             return;
39584         }
39585         this.isSlid = false;
39586         this.beforeSlide();
39587         this.el.slideOut(this.getSlideAnchor(), {
39588             callback: function(){
39589                 this.el.setLeftTop(-10000, -10000);
39590                 this.afterSlide();
39591                 this.afterSlideIn();
39592                 Roo.callback(cb);
39593             },
39594             scope: this,
39595             block: true
39596         });
39597     },
39598     
39599     slideInIf : function(e){
39600         if(!e.within(this.el)){
39601             this.slideIn();
39602         }
39603     },
39604
39605     animateCollapse : function(){
39606         this.beforeSlide();
39607         this.el.setStyle("z-index", 20000);
39608         var anchor = this.getSlideAnchor();
39609         this.el.slideOut(anchor, {
39610             callback : function(){
39611                 this.el.setStyle("z-index", "");
39612                 this.collapsedEl.slideIn(anchor, {duration:.3});
39613                 this.afterSlide();
39614                 this.el.setLocation(-10000,-10000);
39615                 this.el.hide();
39616                 this.fireEvent("collapsed", this);
39617             },
39618             scope: this,
39619             block: true
39620         });
39621     },
39622
39623     animateExpand : function(){
39624         this.beforeSlide();
39625         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39626         this.el.setStyle("z-index", 20000);
39627         this.collapsedEl.hide({
39628             duration:.1
39629         });
39630         this.el.slideIn(this.getSlideAnchor(), {
39631             callback : function(){
39632                 this.el.setStyle("z-index", "");
39633                 this.afterSlide();
39634                 if(this.split){
39635                     this.split.el.show();
39636                 }
39637                 this.fireEvent("invalidated", this);
39638                 this.fireEvent("expanded", this);
39639             },
39640             scope: this,
39641             block: true
39642         });
39643     },
39644
39645     anchors : {
39646         "west" : "left",
39647         "east" : "right",
39648         "north" : "top",
39649         "south" : "bottom"
39650     },
39651
39652     sanchors : {
39653         "west" : "l",
39654         "east" : "r",
39655         "north" : "t",
39656         "south" : "b"
39657     },
39658
39659     canchors : {
39660         "west" : "tl-tr",
39661         "east" : "tr-tl",
39662         "north" : "tl-bl",
39663         "south" : "bl-tl"
39664     },
39665
39666     getAnchor : function(){
39667         return this.anchors[this.position];
39668     },
39669
39670     getCollapseAnchor : function(){
39671         return this.canchors[this.position];
39672     },
39673
39674     getSlideAnchor : function(){
39675         return this.sanchors[this.position];
39676     },
39677
39678     getAlignAdj : function(){
39679         var cm = this.cmargins;
39680         switch(this.position){
39681             case "west":
39682                 return [0, 0];
39683             break;
39684             case "east":
39685                 return [0, 0];
39686             break;
39687             case "north":
39688                 return [0, 0];
39689             break;
39690             case "south":
39691                 return [0, 0];
39692             break;
39693         }
39694     },
39695
39696     getExpandAdj : function(){
39697         var c = this.collapsedEl, cm = this.cmargins;
39698         switch(this.position){
39699             case "west":
39700                 return [-(cm.right+c.getWidth()+cm.left), 0];
39701             break;
39702             case "east":
39703                 return [cm.right+c.getWidth()+cm.left, 0];
39704             break;
39705             case "north":
39706                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39707             break;
39708             case "south":
39709                 return [0, cm.top+cm.bottom+c.getHeight()];
39710             break;
39711         }
39712     }
39713 });/*
39714  * Based on:
39715  * Ext JS Library 1.1.1
39716  * Copyright(c) 2006-2007, Ext JS, LLC.
39717  *
39718  * Originally Released Under LGPL - original licence link has changed is not relivant.
39719  *
39720  * Fork - LGPL
39721  * <script type="text/javascript">
39722  */
39723 /*
39724  * These classes are private internal classes
39725  */
39726 Roo.bootstrap.layout.Center = function(config){
39727     config.region = "center";
39728     Roo.bootstrap.layout.Region.call(this, config);
39729     this.visible = true;
39730     this.minWidth = config.minWidth || 20;
39731     this.minHeight = config.minHeight || 20;
39732 };
39733
39734 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39735     hide : function(){
39736         // center panel can't be hidden
39737     },
39738     
39739     show : function(){
39740         // center panel can't be hidden
39741     },
39742     
39743     getMinWidth: function(){
39744         return this.minWidth;
39745     },
39746     
39747     getMinHeight: function(){
39748         return this.minHeight;
39749     }
39750 });
39751
39752
39753
39754
39755  
39756
39757
39758
39759
39760
39761
39762 Roo.bootstrap.layout.North = function(config)
39763 {
39764     config.region = 'north';
39765     config.cursor = 'n-resize';
39766     
39767     Roo.bootstrap.layout.Split.call(this, config);
39768     
39769     
39770     if(this.split){
39771         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39772         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39773         this.split.el.addClass("roo-layout-split-v");
39774     }
39775     //var size = config.initialSize || config.height;
39776     //if(this.el && typeof size != "undefined"){
39777     //    this.el.setHeight(size);
39778     //}
39779 };
39780 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39781 {
39782     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39783      
39784      
39785     onRender : function(ctr, pos)
39786     {
39787         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39788         var size = this.config.initialSize || this.config.height;
39789         if(this.el && typeof size != "undefined"){
39790             this.el.setHeight(size);
39791         }
39792     
39793     },
39794     
39795     getBox : function(){
39796         if(this.collapsed){
39797             return this.collapsedEl.getBox();
39798         }
39799         var box = this.el.getBox();
39800         if(this.split){
39801             box.height += this.split.el.getHeight();
39802         }
39803         return box;
39804     },
39805     
39806     updateBox : function(box){
39807         if(this.split && !this.collapsed){
39808             box.height -= this.split.el.getHeight();
39809             this.split.el.setLeft(box.x);
39810             this.split.el.setTop(box.y+box.height);
39811             this.split.el.setWidth(box.width);
39812         }
39813         if(this.collapsed){
39814             this.updateBody(box.width, null);
39815         }
39816         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39817     }
39818 });
39819
39820
39821
39822
39823
39824 Roo.bootstrap.layout.South = function(config){
39825     config.region = 'south';
39826     config.cursor = 's-resize';
39827     Roo.bootstrap.layout.Split.call(this, config);
39828     if(this.split){
39829         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39830         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39831         this.split.el.addClass("roo-layout-split-v");
39832     }
39833     
39834 };
39835
39836 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39837     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39838     
39839     onRender : function(ctr, pos)
39840     {
39841         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39842         var size = this.config.initialSize || this.config.height;
39843         if(this.el && typeof size != "undefined"){
39844             this.el.setHeight(size);
39845         }
39846     
39847     },
39848     
39849     getBox : function(){
39850         if(this.collapsed){
39851             return this.collapsedEl.getBox();
39852         }
39853         var box = this.el.getBox();
39854         if(this.split){
39855             var sh = this.split.el.getHeight();
39856             box.height += sh;
39857             box.y -= sh;
39858         }
39859         return box;
39860     },
39861     
39862     updateBox : function(box){
39863         if(this.split && !this.collapsed){
39864             var sh = this.split.el.getHeight();
39865             box.height -= sh;
39866             box.y += sh;
39867             this.split.el.setLeft(box.x);
39868             this.split.el.setTop(box.y-sh);
39869             this.split.el.setWidth(box.width);
39870         }
39871         if(this.collapsed){
39872             this.updateBody(box.width, null);
39873         }
39874         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39875     }
39876 });
39877
39878 Roo.bootstrap.layout.East = function(config){
39879     config.region = "east";
39880     config.cursor = "e-resize";
39881     Roo.bootstrap.layout.Split.call(this, config);
39882     if(this.split){
39883         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39884         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39885         this.split.el.addClass("roo-layout-split-h");
39886     }
39887     
39888 };
39889 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39890     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39891     
39892     onRender : function(ctr, pos)
39893     {
39894         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39895         var size = this.config.initialSize || this.config.width;
39896         if(this.el && typeof size != "undefined"){
39897             this.el.setWidth(size);
39898         }
39899     
39900     },
39901     
39902     getBox : function(){
39903         if(this.collapsed){
39904             return this.collapsedEl.getBox();
39905         }
39906         var box = this.el.getBox();
39907         if(this.split){
39908             var sw = this.split.el.getWidth();
39909             box.width += sw;
39910             box.x -= sw;
39911         }
39912         return box;
39913     },
39914
39915     updateBox : function(box){
39916         if(this.split && !this.collapsed){
39917             var sw = this.split.el.getWidth();
39918             box.width -= sw;
39919             this.split.el.setLeft(box.x);
39920             this.split.el.setTop(box.y);
39921             this.split.el.setHeight(box.height);
39922             box.x += sw;
39923         }
39924         if(this.collapsed){
39925             this.updateBody(null, box.height);
39926         }
39927         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39928     }
39929 });
39930
39931 Roo.bootstrap.layout.West = function(config){
39932     config.region = "west";
39933     config.cursor = "w-resize";
39934     
39935     Roo.bootstrap.layout.Split.call(this, config);
39936     if(this.split){
39937         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39938         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39939         this.split.el.addClass("roo-layout-split-h");
39940     }
39941     
39942 };
39943 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39944     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39945     
39946     onRender: function(ctr, pos)
39947     {
39948         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39949         var size = this.config.initialSize || this.config.width;
39950         if(typeof size != "undefined"){
39951             this.el.setWidth(size);
39952         }
39953     },
39954     
39955     getBox : function(){
39956         if(this.collapsed){
39957             return this.collapsedEl.getBox();
39958         }
39959         var box = this.el.getBox();
39960         if (box.width == 0) {
39961             box.width = this.config.width; // kludge?
39962         }
39963         if(this.split){
39964             box.width += this.split.el.getWidth();
39965         }
39966         return box;
39967     },
39968     
39969     updateBox : function(box){
39970         if(this.split && !this.collapsed){
39971             var sw = this.split.el.getWidth();
39972             box.width -= sw;
39973             this.split.el.setLeft(box.x+box.width);
39974             this.split.el.setTop(box.y);
39975             this.split.el.setHeight(box.height);
39976         }
39977         if(this.collapsed){
39978             this.updateBody(null, box.height);
39979         }
39980         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39981     }
39982 });Roo.namespace("Roo.bootstrap.panel");/*
39983  * Based on:
39984  * Ext JS Library 1.1.1
39985  * Copyright(c) 2006-2007, Ext JS, LLC.
39986  *
39987  * Originally Released Under LGPL - original licence link has changed is not relivant.
39988  *
39989  * Fork - LGPL
39990  * <script type="text/javascript">
39991  */
39992 /**
39993  * @class Roo.ContentPanel
39994  * @extends Roo.util.Observable
39995  * A basic ContentPanel element.
39996  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39997  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39998  * @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
39999  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40000  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40001  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40002  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40003  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40004  * @cfg {String} title          The title for this panel
40005  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40006  * @cfg {String} url            Calls {@link #setUrl} with this value
40007  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40008  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40009  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40010  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40011  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40012  * @cfg {Boolean} badges render the badges
40013  * @cfg {String} cls  extra classes to use  
40014  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40015
40016  * @constructor
40017  * Create a new ContentPanel.
40018  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40019  * @param {String/Object} config A string to set only the title or a config object
40020  * @param {String} content (optional) Set the HTML content for this panel
40021  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40022  */
40023 Roo.bootstrap.panel.Content = function( config){
40024     
40025     this.tpl = config.tpl || false;
40026     
40027     var el = config.el;
40028     var content = config.content;
40029
40030     if(config.autoCreate){ // xtype is available if this is called from factory
40031         el = Roo.id();
40032     }
40033     this.el = Roo.get(el);
40034     if(!this.el && config && config.autoCreate){
40035         if(typeof config.autoCreate == "object"){
40036             if(!config.autoCreate.id){
40037                 config.autoCreate.id = config.id||el;
40038             }
40039             this.el = Roo.DomHelper.append(document.body,
40040                         config.autoCreate, true);
40041         }else{
40042             var elcfg =  {
40043                 tag: "div",
40044                 cls: (config.cls || '') +
40045                     (config.background ? ' bg-' + config.background : '') +
40046                     " roo-layout-inactive-content",
40047                 id: config.id||el
40048             };
40049             if (config.iframe) {
40050                 elcfg.cn = [
40051                     {
40052                         tag : 'iframe',
40053                         style : 'border: 0px',
40054                         src : 'about:blank'
40055                     }
40056                 ];
40057             }
40058               
40059             if (config.html) {
40060                 elcfg.html = config.html;
40061                 
40062             }
40063                         
40064             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40065             if (config.iframe) {
40066                 this.iframeEl = this.el.select('iframe',true).first();
40067             }
40068             
40069         }
40070     } 
40071     this.closable = false;
40072     this.loaded = false;
40073     this.active = false;
40074    
40075       
40076     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40077         
40078         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40079         
40080         this.wrapEl = this.el; //this.el.wrap();
40081         var ti = [];
40082         if (config.toolbar.items) {
40083             ti = config.toolbar.items ;
40084             delete config.toolbar.items ;
40085         }
40086         
40087         var nitems = [];
40088         this.toolbar.render(this.wrapEl, 'before');
40089         for(var i =0;i < ti.length;i++) {
40090           //  Roo.log(['add child', items[i]]);
40091             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40092         }
40093         this.toolbar.items = nitems;
40094         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40095         delete config.toolbar;
40096         
40097     }
40098     /*
40099     // xtype created footer. - not sure if will work as we normally have to render first..
40100     if (this.footer && !this.footer.el && this.footer.xtype) {
40101         if (!this.wrapEl) {
40102             this.wrapEl = this.el.wrap();
40103         }
40104     
40105         this.footer.container = this.wrapEl.createChild();
40106          
40107         this.footer = Roo.factory(this.footer, Roo);
40108         
40109     }
40110     */
40111     
40112      if(typeof config == "string"){
40113         this.title = config;
40114     }else{
40115         Roo.apply(this, config);
40116     }
40117     
40118     if(this.resizeEl){
40119         this.resizeEl = Roo.get(this.resizeEl, true);
40120     }else{
40121         this.resizeEl = this.el;
40122     }
40123     // handle view.xtype
40124     
40125  
40126     
40127     
40128     this.addEvents({
40129         /**
40130          * @event activate
40131          * Fires when this panel is activated. 
40132          * @param {Roo.ContentPanel} this
40133          */
40134         "activate" : true,
40135         /**
40136          * @event deactivate
40137          * Fires when this panel is activated. 
40138          * @param {Roo.ContentPanel} this
40139          */
40140         "deactivate" : true,
40141
40142         /**
40143          * @event resize
40144          * Fires when this panel is resized if fitToFrame is true.
40145          * @param {Roo.ContentPanel} this
40146          * @param {Number} width The width after any component adjustments
40147          * @param {Number} height The height after any component adjustments
40148          */
40149         "resize" : true,
40150         
40151          /**
40152          * @event render
40153          * Fires when this tab is created
40154          * @param {Roo.ContentPanel} this
40155          */
40156         "render" : true
40157         
40158         
40159         
40160     });
40161     
40162
40163     
40164     
40165     if(this.autoScroll && !this.iframe){
40166         this.resizeEl.setStyle("overflow", "auto");
40167     } else {
40168         // fix randome scrolling
40169         //this.el.on('scroll', function() {
40170         //    Roo.log('fix random scolling');
40171         //    this.scrollTo('top',0); 
40172         //});
40173     }
40174     content = content || this.content;
40175     if(content){
40176         this.setContent(content);
40177     }
40178     if(config && config.url){
40179         this.setUrl(this.url, this.params, this.loadOnce);
40180     }
40181     
40182     
40183     
40184     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40185     
40186     if (this.view && typeof(this.view.xtype) != 'undefined') {
40187         this.view.el = this.el.appendChild(document.createElement("div"));
40188         this.view = Roo.factory(this.view); 
40189         this.view.render  &&  this.view.render(false, '');  
40190     }
40191     
40192     
40193     this.fireEvent('render', this);
40194 };
40195
40196 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40197     
40198     cls : '',
40199     background : '',
40200     
40201     tabTip : '',
40202     
40203     iframe : false,
40204     iframeEl : false,
40205     
40206     setRegion : function(region){
40207         this.region = region;
40208         this.setActiveClass(region && !this.background);
40209     },
40210     
40211     
40212     setActiveClass: function(state)
40213     {
40214         if(state){
40215            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40216            this.el.setStyle('position','relative');
40217         }else{
40218            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40219            this.el.setStyle('position', 'absolute');
40220         } 
40221     },
40222     
40223     /**
40224      * Returns the toolbar for this Panel if one was configured. 
40225      * @return {Roo.Toolbar} 
40226      */
40227     getToolbar : function(){
40228         return this.toolbar;
40229     },
40230     
40231     setActiveState : function(active)
40232     {
40233         this.active = active;
40234         this.setActiveClass(active);
40235         if(!active){
40236             if(this.fireEvent("deactivate", this) === false){
40237                 return false;
40238             }
40239             return true;
40240         }
40241         this.fireEvent("activate", this);
40242         return true;
40243     },
40244     /**
40245      * Updates this panel's element (not for iframe)
40246      * @param {String} content The new content
40247      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40248     */
40249     setContent : function(content, loadScripts){
40250         if (this.iframe) {
40251             return;
40252         }
40253         
40254         this.el.update(content, loadScripts);
40255     },
40256
40257     ignoreResize : function(w, h){
40258         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40259             return true;
40260         }else{
40261             this.lastSize = {width: w, height: h};
40262             return false;
40263         }
40264     },
40265     /**
40266      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40267      * @return {Roo.UpdateManager} The UpdateManager
40268      */
40269     getUpdateManager : function(){
40270         if (this.iframe) {
40271             return false;
40272         }
40273         return this.el.getUpdateManager();
40274     },
40275      /**
40276      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40277      * Does not work with IFRAME contents
40278      * @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:
40279 <pre><code>
40280 panel.load({
40281     url: "your-url.php",
40282     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40283     callback: yourFunction,
40284     scope: yourObject, //(optional scope)
40285     discardUrl: false,
40286     nocache: false,
40287     text: "Loading...",
40288     timeout: 30,
40289     scripts: false
40290 });
40291 </code></pre>
40292      
40293      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40294      * 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.
40295      * @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}
40296      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40297      * @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.
40298      * @return {Roo.ContentPanel} this
40299      */
40300     load : function(){
40301         
40302         if (this.iframe) {
40303             return this;
40304         }
40305         
40306         var um = this.el.getUpdateManager();
40307         um.update.apply(um, arguments);
40308         return this;
40309     },
40310
40311
40312     /**
40313      * 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.
40314      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40315      * @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)
40316      * @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)
40317      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40318      */
40319     setUrl : function(url, params, loadOnce){
40320         if (this.iframe) {
40321             this.iframeEl.dom.src = url;
40322             return false;
40323         }
40324         
40325         if(this.refreshDelegate){
40326             this.removeListener("activate", this.refreshDelegate);
40327         }
40328         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40329         this.on("activate", this.refreshDelegate);
40330         return this.el.getUpdateManager();
40331     },
40332     
40333     _handleRefresh : function(url, params, loadOnce){
40334         if(!loadOnce || !this.loaded){
40335             var updater = this.el.getUpdateManager();
40336             updater.update(url, params, this._setLoaded.createDelegate(this));
40337         }
40338     },
40339     
40340     _setLoaded : function(){
40341         this.loaded = true;
40342     }, 
40343     
40344     /**
40345      * Returns this panel's id
40346      * @return {String} 
40347      */
40348     getId : function(){
40349         return this.el.id;
40350     },
40351     
40352     /** 
40353      * Returns this panel's element - used by regiosn to add.
40354      * @return {Roo.Element} 
40355      */
40356     getEl : function(){
40357         return this.wrapEl || this.el;
40358     },
40359     
40360    
40361     
40362     adjustForComponents : function(width, height)
40363     {
40364         //Roo.log('adjustForComponents ');
40365         if(this.resizeEl != this.el){
40366             width -= this.el.getFrameWidth('lr');
40367             height -= this.el.getFrameWidth('tb');
40368         }
40369         if(this.toolbar){
40370             var te = this.toolbar.getEl();
40371             te.setWidth(width);
40372             height -= te.getHeight();
40373         }
40374         if(this.footer){
40375             var te = this.footer.getEl();
40376             te.setWidth(width);
40377             height -= te.getHeight();
40378         }
40379         
40380         
40381         if(this.adjustments){
40382             width += this.adjustments[0];
40383             height += this.adjustments[1];
40384         }
40385         return {"width": width, "height": height};
40386     },
40387     
40388     setSize : function(width, height){
40389         if(this.fitToFrame && !this.ignoreResize(width, height)){
40390             if(this.fitContainer && this.resizeEl != this.el){
40391                 this.el.setSize(width, height);
40392             }
40393             var size = this.adjustForComponents(width, height);
40394             if (this.iframe) {
40395                 this.iframeEl.setSize(width,height);
40396             }
40397             
40398             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40399             this.fireEvent('resize', this, size.width, size.height);
40400             
40401             
40402         }
40403     },
40404     
40405     /**
40406      * Returns this panel's title
40407      * @return {String} 
40408      */
40409     getTitle : function(){
40410         
40411         if (typeof(this.title) != 'object') {
40412             return this.title;
40413         }
40414         
40415         var t = '';
40416         for (var k in this.title) {
40417             if (!this.title.hasOwnProperty(k)) {
40418                 continue;
40419             }
40420             
40421             if (k.indexOf('-') >= 0) {
40422                 var s = k.split('-');
40423                 for (var i = 0; i<s.length; i++) {
40424                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40425                 }
40426             } else {
40427                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40428             }
40429         }
40430         return t;
40431     },
40432     
40433     /**
40434      * Set this panel's title
40435      * @param {String} title
40436      */
40437     setTitle : function(title){
40438         this.title = title;
40439         if(this.region){
40440             this.region.updatePanelTitle(this, title);
40441         }
40442     },
40443     
40444     /**
40445      * Returns true is this panel was configured to be closable
40446      * @return {Boolean} 
40447      */
40448     isClosable : function(){
40449         return this.closable;
40450     },
40451     
40452     beforeSlide : function(){
40453         this.el.clip();
40454         this.resizeEl.clip();
40455     },
40456     
40457     afterSlide : function(){
40458         this.el.unclip();
40459         this.resizeEl.unclip();
40460     },
40461     
40462     /**
40463      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40464      *   Will fail silently if the {@link #setUrl} method has not been called.
40465      *   This does not activate the panel, just updates its content.
40466      */
40467     refresh : function(){
40468         if(this.refreshDelegate){
40469            this.loaded = false;
40470            this.refreshDelegate();
40471         }
40472     },
40473     
40474     /**
40475      * Destroys this panel
40476      */
40477     destroy : function(){
40478         this.el.removeAllListeners();
40479         var tempEl = document.createElement("span");
40480         tempEl.appendChild(this.el.dom);
40481         tempEl.innerHTML = "";
40482         this.el.remove();
40483         this.el = null;
40484     },
40485     
40486     /**
40487      * form - if the content panel contains a form - this is a reference to it.
40488      * @type {Roo.form.Form}
40489      */
40490     form : false,
40491     /**
40492      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40493      *    This contains a reference to it.
40494      * @type {Roo.View}
40495      */
40496     view : false,
40497     
40498       /**
40499      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40500      * <pre><code>
40501
40502 layout.addxtype({
40503        xtype : 'Form',
40504        items: [ .... ]
40505    }
40506 );
40507
40508 </code></pre>
40509      * @param {Object} cfg Xtype definition of item to add.
40510      */
40511     
40512     
40513     getChildContainer: function () {
40514         return this.getEl();
40515     }
40516     
40517     
40518     /*
40519         var  ret = new Roo.factory(cfg);
40520         return ret;
40521         
40522         
40523         // add form..
40524         if (cfg.xtype.match(/^Form$/)) {
40525             
40526             var el;
40527             //if (this.footer) {
40528             //    el = this.footer.container.insertSibling(false, 'before');
40529             //} else {
40530                 el = this.el.createChild();
40531             //}
40532
40533             this.form = new  Roo.form.Form(cfg);
40534             
40535             
40536             if ( this.form.allItems.length) {
40537                 this.form.render(el.dom);
40538             }
40539             return this.form;
40540         }
40541         // should only have one of theses..
40542         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40543             // views.. should not be just added - used named prop 'view''
40544             
40545             cfg.el = this.el.appendChild(document.createElement("div"));
40546             // factory?
40547             
40548             var ret = new Roo.factory(cfg);
40549              
40550              ret.render && ret.render(false, ''); // render blank..
40551             this.view = ret;
40552             return ret;
40553         }
40554         return false;
40555     }
40556     \*/
40557 });
40558  
40559 /**
40560  * @class Roo.bootstrap.panel.Grid
40561  * @extends Roo.bootstrap.panel.Content
40562  * @constructor
40563  * Create a new GridPanel.
40564  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40565  * @param {Object} config A the config object
40566   
40567  */
40568
40569
40570
40571 Roo.bootstrap.panel.Grid = function(config)
40572 {
40573     
40574       
40575     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40576         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40577
40578     config.el = this.wrapper;
40579     //this.el = this.wrapper;
40580     
40581       if (config.container) {
40582         // ctor'ed from a Border/panel.grid
40583         
40584         
40585         this.wrapper.setStyle("overflow", "hidden");
40586         this.wrapper.addClass('roo-grid-container');
40587
40588     }
40589     
40590     
40591     if(config.toolbar){
40592         var tool_el = this.wrapper.createChild();    
40593         this.toolbar = Roo.factory(config.toolbar);
40594         var ti = [];
40595         if (config.toolbar.items) {
40596             ti = config.toolbar.items ;
40597             delete config.toolbar.items ;
40598         }
40599         
40600         var nitems = [];
40601         this.toolbar.render(tool_el);
40602         for(var i =0;i < ti.length;i++) {
40603           //  Roo.log(['add child', items[i]]);
40604             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40605         }
40606         this.toolbar.items = nitems;
40607         
40608         delete config.toolbar;
40609     }
40610     
40611     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40612     config.grid.scrollBody = true;;
40613     config.grid.monitorWindowResize = false; // turn off autosizing
40614     config.grid.autoHeight = false;
40615     config.grid.autoWidth = false;
40616     
40617     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40618     
40619     if (config.background) {
40620         // render grid on panel activation (if panel background)
40621         this.on('activate', function(gp) {
40622             if (!gp.grid.rendered) {
40623                 gp.grid.render(this.wrapper);
40624                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40625             }
40626         });
40627             
40628     } else {
40629         this.grid.render(this.wrapper);
40630         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40631
40632     }
40633     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40634     // ??? needed ??? config.el = this.wrapper;
40635     
40636     
40637     
40638   
40639     // xtype created footer. - not sure if will work as we normally have to render first..
40640     if (this.footer && !this.footer.el && this.footer.xtype) {
40641         
40642         var ctr = this.grid.getView().getFooterPanel(true);
40643         this.footer.dataSource = this.grid.dataSource;
40644         this.footer = Roo.factory(this.footer, Roo);
40645         this.footer.render(ctr);
40646         
40647     }
40648     
40649     
40650     
40651     
40652      
40653 };
40654
40655 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40656     getId : function(){
40657         return this.grid.id;
40658     },
40659     
40660     /**
40661      * Returns the grid for this panel
40662      * @return {Roo.bootstrap.Table} 
40663      */
40664     getGrid : function(){
40665         return this.grid;    
40666     },
40667     
40668     setSize : function(width, height){
40669         if(!this.ignoreResize(width, height)){
40670             var grid = this.grid;
40671             var size = this.adjustForComponents(width, height);
40672             // tfoot is not a footer?
40673           
40674             
40675             var gridel = grid.getGridEl();
40676             gridel.setSize(size.width, size.height);
40677             
40678             var tbd = grid.getGridEl().select('tbody', true).first();
40679             var thd = grid.getGridEl().select('thead',true).first();
40680             var tbf= grid.getGridEl().select('tfoot', true).first();
40681
40682             if (tbf) {
40683                 size.height -= tbf.getHeight();
40684             }
40685             if (thd) {
40686                 size.height -= thd.getHeight();
40687             }
40688             
40689             tbd.setSize(size.width, size.height );
40690             // this is for the account management tab -seems to work there.
40691             var thd = grid.getGridEl().select('thead',true).first();
40692             //if (tbd) {
40693             //    tbd.setSize(size.width, size.height - thd.getHeight());
40694             //}
40695              
40696             grid.autoSize();
40697         }
40698     },
40699      
40700     
40701     
40702     beforeSlide : function(){
40703         this.grid.getView().scroller.clip();
40704     },
40705     
40706     afterSlide : function(){
40707         this.grid.getView().scroller.unclip();
40708     },
40709     
40710     destroy : function(){
40711         this.grid.destroy();
40712         delete this.grid;
40713         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40714     }
40715 });
40716
40717 /**
40718  * @class Roo.bootstrap.panel.Nest
40719  * @extends Roo.bootstrap.panel.Content
40720  * @constructor
40721  * Create a new Panel, that can contain a layout.Border.
40722  * 
40723  * 
40724  * @param {Roo.BorderLayout} layout The layout for this panel
40725  * @param {String/Object} config A string to set only the title or a config object
40726  */
40727 Roo.bootstrap.panel.Nest = function(config)
40728 {
40729     // construct with only one argument..
40730     /* FIXME - implement nicer consturctors
40731     if (layout.layout) {
40732         config = layout;
40733         layout = config.layout;
40734         delete config.layout;
40735     }
40736     if (layout.xtype && !layout.getEl) {
40737         // then layout needs constructing..
40738         layout = Roo.factory(layout, Roo);
40739     }
40740     */
40741     
40742     config.el =  config.layout.getEl();
40743     
40744     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40745     
40746     config.layout.monitorWindowResize = false; // turn off autosizing
40747     this.layout = config.layout;
40748     this.layout.getEl().addClass("roo-layout-nested-layout");
40749     this.layout.parent = this;
40750     
40751     
40752     
40753     
40754 };
40755
40756 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40757
40758     setSize : function(width, height){
40759         if(!this.ignoreResize(width, height)){
40760             var size = this.adjustForComponents(width, height);
40761             var el = this.layout.getEl();
40762             if (size.height < 1) {
40763                 el.setWidth(size.width);   
40764             } else {
40765                 el.setSize(size.width, size.height);
40766             }
40767             var touch = el.dom.offsetWidth;
40768             this.layout.layout();
40769             // ie requires a double layout on the first pass
40770             if(Roo.isIE && !this.initialized){
40771                 this.initialized = true;
40772                 this.layout.layout();
40773             }
40774         }
40775     },
40776     
40777     // activate all subpanels if not currently active..
40778     
40779     setActiveState : function(active){
40780         this.active = active;
40781         this.setActiveClass(active);
40782         
40783         if(!active){
40784             this.fireEvent("deactivate", this);
40785             return;
40786         }
40787         
40788         this.fireEvent("activate", this);
40789         // not sure if this should happen before or after..
40790         if (!this.layout) {
40791             return; // should not happen..
40792         }
40793         var reg = false;
40794         for (var r in this.layout.regions) {
40795             reg = this.layout.getRegion(r);
40796             if (reg.getActivePanel()) {
40797                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40798                 reg.setActivePanel(reg.getActivePanel());
40799                 continue;
40800             }
40801             if (!reg.panels.length) {
40802                 continue;
40803             }
40804             reg.showPanel(reg.getPanel(0));
40805         }
40806         
40807         
40808         
40809         
40810     },
40811     
40812     /**
40813      * Returns the nested BorderLayout for this panel
40814      * @return {Roo.BorderLayout} 
40815      */
40816     getLayout : function(){
40817         return this.layout;
40818     },
40819     
40820      /**
40821      * Adds a xtype elements to the layout of the nested panel
40822      * <pre><code>
40823
40824 panel.addxtype({
40825        xtype : 'ContentPanel',
40826        region: 'west',
40827        items: [ .... ]
40828    }
40829 );
40830
40831 panel.addxtype({
40832         xtype : 'NestedLayoutPanel',
40833         region: 'west',
40834         layout: {
40835            center: { },
40836            west: { }   
40837         },
40838         items : [ ... list of content panels or nested layout panels.. ]
40839    }
40840 );
40841 </code></pre>
40842      * @param {Object} cfg Xtype definition of item to add.
40843      */
40844     addxtype : function(cfg) {
40845         return this.layout.addxtype(cfg);
40846     
40847     }
40848 });/*
40849  * Based on:
40850  * Ext JS Library 1.1.1
40851  * Copyright(c) 2006-2007, Ext JS, LLC.
40852  *
40853  * Originally Released Under LGPL - original licence link has changed is not relivant.
40854  *
40855  * Fork - LGPL
40856  * <script type="text/javascript">
40857  */
40858 /**
40859  * @class Roo.TabPanel
40860  * @extends Roo.util.Observable
40861  * A lightweight tab container.
40862  * <br><br>
40863  * Usage:
40864  * <pre><code>
40865 // basic tabs 1, built from existing content
40866 var tabs = new Roo.TabPanel("tabs1");
40867 tabs.addTab("script", "View Script");
40868 tabs.addTab("markup", "View Markup");
40869 tabs.activate("script");
40870
40871 // more advanced tabs, built from javascript
40872 var jtabs = new Roo.TabPanel("jtabs");
40873 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40874
40875 // set up the UpdateManager
40876 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40877 var updater = tab2.getUpdateManager();
40878 updater.setDefaultUrl("ajax1.htm");
40879 tab2.on('activate', updater.refresh, updater, true);
40880
40881 // Use setUrl for Ajax loading
40882 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40883 tab3.setUrl("ajax2.htm", null, true);
40884
40885 // Disabled tab
40886 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40887 tab4.disable();
40888
40889 jtabs.activate("jtabs-1");
40890  * </code></pre>
40891  * @constructor
40892  * Create a new TabPanel.
40893  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40894  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40895  */
40896 Roo.bootstrap.panel.Tabs = function(config){
40897     /**
40898     * The container element for this TabPanel.
40899     * @type Roo.Element
40900     */
40901     this.el = Roo.get(config.el);
40902     delete config.el;
40903     if(config){
40904         if(typeof config == "boolean"){
40905             this.tabPosition = config ? "bottom" : "top";
40906         }else{
40907             Roo.apply(this, config);
40908         }
40909     }
40910     
40911     if(this.tabPosition == "bottom"){
40912         // if tabs are at the bottom = create the body first.
40913         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40914         this.el.addClass("roo-tabs-bottom");
40915     }
40916     // next create the tabs holders
40917     
40918     if (this.tabPosition == "west"){
40919         
40920         var reg = this.region; // fake it..
40921         while (reg) {
40922             if (!reg.mgr.parent) {
40923                 break;
40924             }
40925             reg = reg.mgr.parent.region;
40926         }
40927         Roo.log("got nest?");
40928         Roo.log(reg);
40929         if (reg.mgr.getRegion('west')) {
40930             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40931             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40932             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40933             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40934             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40935         
40936             
40937         }
40938         
40939         
40940     } else {
40941      
40942         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40943         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40944         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40945         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40946     }
40947     
40948     
40949     if(Roo.isIE){
40950         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40951     }
40952     
40953     // finally - if tabs are at the top, then create the body last..
40954     if(this.tabPosition != "bottom"){
40955         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40956          * @type Roo.Element
40957          */
40958         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40959         this.el.addClass("roo-tabs-top");
40960     }
40961     this.items = [];
40962
40963     this.bodyEl.setStyle("position", "relative");
40964
40965     this.active = null;
40966     this.activateDelegate = this.activate.createDelegate(this);
40967
40968     this.addEvents({
40969         /**
40970          * @event tabchange
40971          * Fires when the active tab changes
40972          * @param {Roo.TabPanel} this
40973          * @param {Roo.TabPanelItem} activePanel The new active tab
40974          */
40975         "tabchange": true,
40976         /**
40977          * @event beforetabchange
40978          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40979          * @param {Roo.TabPanel} this
40980          * @param {Object} e Set cancel to true on this object to cancel the tab change
40981          * @param {Roo.TabPanelItem} tab The tab being changed to
40982          */
40983         "beforetabchange" : true
40984     });
40985
40986     Roo.EventManager.onWindowResize(this.onResize, this);
40987     this.cpad = this.el.getPadding("lr");
40988     this.hiddenCount = 0;
40989
40990
40991     // toolbar on the tabbar support...
40992     if (this.toolbar) {
40993         alert("no toolbar support yet");
40994         this.toolbar  = false;
40995         /*
40996         var tcfg = this.toolbar;
40997         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40998         this.toolbar = new Roo.Toolbar(tcfg);
40999         if (Roo.isSafari) {
41000             var tbl = tcfg.container.child('table', true);
41001             tbl.setAttribute('width', '100%');
41002         }
41003         */
41004         
41005     }
41006    
41007
41008
41009     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41010 };
41011
41012 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41013     /*
41014      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41015      */
41016     tabPosition : "top",
41017     /*
41018      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41019      */
41020     currentTabWidth : 0,
41021     /*
41022      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41023      */
41024     minTabWidth : 40,
41025     /*
41026      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41027      */
41028     maxTabWidth : 250,
41029     /*
41030      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41031      */
41032     preferredTabWidth : 175,
41033     /*
41034      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41035      */
41036     resizeTabs : false,
41037     /*
41038      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41039      */
41040     monitorResize : true,
41041     /*
41042      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41043      */
41044     toolbar : false,  // set by caller..
41045     
41046     region : false, /// set by caller
41047     
41048     disableTooltips : true, // not used yet...
41049
41050     /**
41051      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41052      * @param {String} id The id of the div to use <b>or create</b>
41053      * @param {String} text The text for the tab
41054      * @param {String} content (optional) Content to put in the TabPanelItem body
41055      * @param {Boolean} closable (optional) True to create a close icon on the tab
41056      * @return {Roo.TabPanelItem} The created TabPanelItem
41057      */
41058     addTab : function(id, text, content, closable, tpl)
41059     {
41060         var item = new Roo.bootstrap.panel.TabItem({
41061             panel: this,
41062             id : id,
41063             text : text,
41064             closable : closable,
41065             tpl : tpl
41066         });
41067         this.addTabItem(item);
41068         if(content){
41069             item.setContent(content);
41070         }
41071         return item;
41072     },
41073
41074     /**
41075      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41076      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41077      * @return {Roo.TabPanelItem}
41078      */
41079     getTab : function(id){
41080         return this.items[id];
41081     },
41082
41083     /**
41084      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41085      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41086      */
41087     hideTab : function(id){
41088         var t = this.items[id];
41089         if(!t.isHidden()){
41090            t.setHidden(true);
41091            this.hiddenCount++;
41092            this.autoSizeTabs();
41093         }
41094     },
41095
41096     /**
41097      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41098      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41099      */
41100     unhideTab : function(id){
41101         var t = this.items[id];
41102         if(t.isHidden()){
41103            t.setHidden(false);
41104            this.hiddenCount--;
41105            this.autoSizeTabs();
41106         }
41107     },
41108
41109     /**
41110      * Adds an existing {@link Roo.TabPanelItem}.
41111      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41112      */
41113     addTabItem : function(item)
41114     {
41115         this.items[item.id] = item;
41116         this.items.push(item);
41117         this.autoSizeTabs();
41118       //  if(this.resizeTabs){
41119     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41120   //         this.autoSizeTabs();
41121 //        }else{
41122 //            item.autoSize();
41123        // }
41124     },
41125
41126     /**
41127      * Removes a {@link Roo.TabPanelItem}.
41128      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41129      */
41130     removeTab : function(id){
41131         var items = this.items;
41132         var tab = items[id];
41133         if(!tab) { return; }
41134         var index = items.indexOf(tab);
41135         if(this.active == tab && items.length > 1){
41136             var newTab = this.getNextAvailable(index);
41137             if(newTab) {
41138                 newTab.activate();
41139             }
41140         }
41141         this.stripEl.dom.removeChild(tab.pnode.dom);
41142         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41143             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41144         }
41145         items.splice(index, 1);
41146         delete this.items[tab.id];
41147         tab.fireEvent("close", tab);
41148         tab.purgeListeners();
41149         this.autoSizeTabs();
41150     },
41151
41152     getNextAvailable : function(start){
41153         var items = this.items;
41154         var index = start;
41155         // look for a next tab that will slide over to
41156         // replace the one being removed
41157         while(index < items.length){
41158             var item = items[++index];
41159             if(item && !item.isHidden()){
41160                 return item;
41161             }
41162         }
41163         // if one isn't found select the previous tab (on the left)
41164         index = start;
41165         while(index >= 0){
41166             var item = items[--index];
41167             if(item && !item.isHidden()){
41168                 return item;
41169             }
41170         }
41171         return null;
41172     },
41173
41174     /**
41175      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41176      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41177      */
41178     disableTab : function(id){
41179         var tab = this.items[id];
41180         if(tab && this.active != tab){
41181             tab.disable();
41182         }
41183     },
41184
41185     /**
41186      * Enables a {@link Roo.TabPanelItem} that is disabled.
41187      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41188      */
41189     enableTab : function(id){
41190         var tab = this.items[id];
41191         tab.enable();
41192     },
41193
41194     /**
41195      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41196      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41197      * @return {Roo.TabPanelItem} The TabPanelItem.
41198      */
41199     activate : function(id)
41200     {
41201         //Roo.log('activite:'  + id);
41202         
41203         var tab = this.items[id];
41204         if(!tab){
41205             return null;
41206         }
41207         if(tab == this.active || tab.disabled){
41208             return tab;
41209         }
41210         var e = {};
41211         this.fireEvent("beforetabchange", this, e, tab);
41212         if(e.cancel !== true && !tab.disabled){
41213             if(this.active){
41214                 this.active.hide();
41215             }
41216             this.active = this.items[id];
41217             this.active.show();
41218             this.fireEvent("tabchange", this, this.active);
41219         }
41220         return tab;
41221     },
41222
41223     /**
41224      * Gets the active {@link Roo.TabPanelItem}.
41225      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41226      */
41227     getActiveTab : function(){
41228         return this.active;
41229     },
41230
41231     /**
41232      * Updates the tab body element to fit the height of the container element
41233      * for overflow scrolling
41234      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41235      */
41236     syncHeight : function(targetHeight){
41237         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41238         var bm = this.bodyEl.getMargins();
41239         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41240         this.bodyEl.setHeight(newHeight);
41241         return newHeight;
41242     },
41243
41244     onResize : function(){
41245         if(this.monitorResize){
41246             this.autoSizeTabs();
41247         }
41248     },
41249
41250     /**
41251      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41252      */
41253     beginUpdate : function(){
41254         this.updating = true;
41255     },
41256
41257     /**
41258      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41259      */
41260     endUpdate : function(){
41261         this.updating = false;
41262         this.autoSizeTabs();
41263     },
41264
41265     /**
41266      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41267      */
41268     autoSizeTabs : function()
41269     {
41270         var count = this.items.length;
41271         var vcount = count - this.hiddenCount;
41272         
41273         if (vcount < 2) {
41274             this.stripEl.hide();
41275         } else {
41276             this.stripEl.show();
41277         }
41278         
41279         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41280             return;
41281         }
41282         
41283         
41284         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41285         var availWidth = Math.floor(w / vcount);
41286         var b = this.stripBody;
41287         if(b.getWidth() > w){
41288             var tabs = this.items;
41289             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41290             if(availWidth < this.minTabWidth){
41291                 /*if(!this.sleft){    // incomplete scrolling code
41292                     this.createScrollButtons();
41293                 }
41294                 this.showScroll();
41295                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41296             }
41297         }else{
41298             if(this.currentTabWidth < this.preferredTabWidth){
41299                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41300             }
41301         }
41302     },
41303
41304     /**
41305      * Returns the number of tabs in this TabPanel.
41306      * @return {Number}
41307      */
41308      getCount : function(){
41309          return this.items.length;
41310      },
41311
41312     /**
41313      * Resizes all the tabs to the passed width
41314      * @param {Number} The new width
41315      */
41316     setTabWidth : function(width){
41317         this.currentTabWidth = width;
41318         for(var i = 0, len = this.items.length; i < len; i++) {
41319                 if(!this.items[i].isHidden()) {
41320                 this.items[i].setWidth(width);
41321             }
41322         }
41323     },
41324
41325     /**
41326      * Destroys this TabPanel
41327      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41328      */
41329     destroy : function(removeEl){
41330         Roo.EventManager.removeResizeListener(this.onResize, this);
41331         for(var i = 0, len = this.items.length; i < len; i++){
41332             this.items[i].purgeListeners();
41333         }
41334         if(removeEl === true){
41335             this.el.update("");
41336             this.el.remove();
41337         }
41338     },
41339     
41340     createStrip : function(container)
41341     {
41342         var strip = document.createElement("nav");
41343         strip.className = Roo.bootstrap.version == 4 ?
41344             "navbar-light bg-light" : 
41345             "navbar navbar-default"; //"x-tabs-wrap";
41346         container.appendChild(strip);
41347         return strip;
41348     },
41349     
41350     createStripList : function(strip)
41351     {
41352         // div wrapper for retard IE
41353         // returns the "tr" element.
41354         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41355         //'<div class="x-tabs-strip-wrap">'+
41356           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41357           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41358         return strip.firstChild; //.firstChild.firstChild.firstChild;
41359     },
41360     createBody : function(container)
41361     {
41362         var body = document.createElement("div");
41363         Roo.id(body, "tab-body");
41364         //Roo.fly(body).addClass("x-tabs-body");
41365         Roo.fly(body).addClass("tab-content");
41366         container.appendChild(body);
41367         return body;
41368     },
41369     createItemBody :function(bodyEl, id){
41370         var body = Roo.getDom(id);
41371         if(!body){
41372             body = document.createElement("div");
41373             body.id = id;
41374         }
41375         //Roo.fly(body).addClass("x-tabs-item-body");
41376         Roo.fly(body).addClass("tab-pane");
41377          bodyEl.insertBefore(body, bodyEl.firstChild);
41378         return body;
41379     },
41380     /** @private */
41381     createStripElements :  function(stripEl, text, closable, tpl)
41382     {
41383         var td = document.createElement("li"); // was td..
41384         td.className = 'nav-item';
41385         
41386         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41387         
41388         
41389         stripEl.appendChild(td);
41390         /*if(closable){
41391             td.className = "x-tabs-closable";
41392             if(!this.closeTpl){
41393                 this.closeTpl = new Roo.Template(
41394                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41395                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41396                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41397                 );
41398             }
41399             var el = this.closeTpl.overwrite(td, {"text": text});
41400             var close = el.getElementsByTagName("div")[0];
41401             var inner = el.getElementsByTagName("em")[0];
41402             return {"el": el, "close": close, "inner": inner};
41403         } else {
41404         */
41405         // not sure what this is..
41406 //            if(!this.tabTpl){
41407                 //this.tabTpl = new Roo.Template(
41408                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41409                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41410                 //);
41411 //                this.tabTpl = new Roo.Template(
41412 //                   '<a href="#">' +
41413 //                   '<span unselectable="on"' +
41414 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41415 //                            ' >{text}</span></a>'
41416 //                );
41417 //                
41418 //            }
41419
41420
41421             var template = tpl || this.tabTpl || false;
41422             
41423             if(!template){
41424                 template =  new Roo.Template(
41425                         Roo.bootstrap.version == 4 ? 
41426                             (
41427                                 '<a class="nav-link" href="#" unselectable="on"' +
41428                                      (this.disableTooltips ? '' : ' title="{text}"') +
41429                                      ' >{text}</a>'
41430                             ) : (
41431                                 '<a class="nav-link" href="#">' +
41432                                 '<span unselectable="on"' +
41433                                          (this.disableTooltips ? '' : ' title="{text}"') +
41434                                     ' >{text}</span></a>'
41435                             )
41436                 );
41437             }
41438             
41439             switch (typeof(template)) {
41440                 case 'object' :
41441                     break;
41442                 case 'string' :
41443                     template = new Roo.Template(template);
41444                     break;
41445                 default :
41446                     break;
41447             }
41448             
41449             var el = template.overwrite(td, {"text": text});
41450             
41451             var inner = el.getElementsByTagName("span")[0];
41452             
41453             return {"el": el, "inner": inner};
41454             
41455     }
41456         
41457     
41458 });
41459
41460 /**
41461  * @class Roo.TabPanelItem
41462  * @extends Roo.util.Observable
41463  * Represents an individual item (tab plus body) in a TabPanel.
41464  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41465  * @param {String} id The id of this TabPanelItem
41466  * @param {String} text The text for the tab of this TabPanelItem
41467  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41468  */
41469 Roo.bootstrap.panel.TabItem = function(config){
41470     /**
41471      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41472      * @type Roo.TabPanel
41473      */
41474     this.tabPanel = config.panel;
41475     /**
41476      * The id for this TabPanelItem
41477      * @type String
41478      */
41479     this.id = config.id;
41480     /** @private */
41481     this.disabled = false;
41482     /** @private */
41483     this.text = config.text;
41484     /** @private */
41485     this.loaded = false;
41486     this.closable = config.closable;
41487
41488     /**
41489      * The body element for this TabPanelItem.
41490      * @type Roo.Element
41491      */
41492     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41493     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41494     this.bodyEl.setStyle("display", "block");
41495     this.bodyEl.setStyle("zoom", "1");
41496     //this.hideAction();
41497
41498     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41499     /** @private */
41500     this.el = Roo.get(els.el);
41501     this.inner = Roo.get(els.inner, true);
41502      this.textEl = Roo.bootstrap.version == 4 ?
41503         this.el : Roo.get(this.el.dom.firstChild, true);
41504
41505     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41506     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41507
41508     
41509 //    this.el.on("mousedown", this.onTabMouseDown, this);
41510     this.el.on("click", this.onTabClick, this);
41511     /** @private */
41512     if(config.closable){
41513         var c = Roo.get(els.close, true);
41514         c.dom.title = this.closeText;
41515         c.addClassOnOver("close-over");
41516         c.on("click", this.closeClick, this);
41517      }
41518
41519     this.addEvents({
41520          /**
41521          * @event activate
41522          * Fires when this tab becomes the active tab.
41523          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41524          * @param {Roo.TabPanelItem} this
41525          */
41526         "activate": true,
41527         /**
41528          * @event beforeclose
41529          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41530          * @param {Roo.TabPanelItem} this
41531          * @param {Object} e Set cancel to true on this object to cancel the close.
41532          */
41533         "beforeclose": true,
41534         /**
41535          * @event close
41536          * Fires when this tab is closed.
41537          * @param {Roo.TabPanelItem} this
41538          */
41539          "close": true,
41540         /**
41541          * @event deactivate
41542          * Fires when this tab is no longer the active tab.
41543          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41544          * @param {Roo.TabPanelItem} this
41545          */
41546          "deactivate" : true
41547     });
41548     this.hidden = false;
41549
41550     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41551 };
41552
41553 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41554            {
41555     purgeListeners : function(){
41556        Roo.util.Observable.prototype.purgeListeners.call(this);
41557        this.el.removeAllListeners();
41558     },
41559     /**
41560      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41561      */
41562     show : function(){
41563         this.status_node.addClass("active");
41564         this.showAction();
41565         if(Roo.isOpera){
41566             this.tabPanel.stripWrap.repaint();
41567         }
41568         this.fireEvent("activate", this.tabPanel, this);
41569     },
41570
41571     /**
41572      * Returns true if this tab is the active tab.
41573      * @return {Boolean}
41574      */
41575     isActive : function(){
41576         return this.tabPanel.getActiveTab() == this;
41577     },
41578
41579     /**
41580      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41581      */
41582     hide : function(){
41583         this.status_node.removeClass("active");
41584         this.hideAction();
41585         this.fireEvent("deactivate", this.tabPanel, this);
41586     },
41587
41588     hideAction : function(){
41589         this.bodyEl.hide();
41590         this.bodyEl.setStyle("position", "absolute");
41591         this.bodyEl.setLeft("-20000px");
41592         this.bodyEl.setTop("-20000px");
41593     },
41594
41595     showAction : function(){
41596         this.bodyEl.setStyle("position", "relative");
41597         this.bodyEl.setTop("");
41598         this.bodyEl.setLeft("");
41599         this.bodyEl.show();
41600     },
41601
41602     /**
41603      * Set the tooltip for the tab.
41604      * @param {String} tooltip The tab's tooltip
41605      */
41606     setTooltip : function(text){
41607         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41608             this.textEl.dom.qtip = text;
41609             this.textEl.dom.removeAttribute('title');
41610         }else{
41611             this.textEl.dom.title = text;
41612         }
41613     },
41614
41615     onTabClick : function(e){
41616         e.preventDefault();
41617         this.tabPanel.activate(this.id);
41618     },
41619
41620     onTabMouseDown : function(e){
41621         e.preventDefault();
41622         this.tabPanel.activate(this.id);
41623     },
41624 /*
41625     getWidth : function(){
41626         return this.inner.getWidth();
41627     },
41628
41629     setWidth : function(width){
41630         var iwidth = width - this.linode.getPadding("lr");
41631         this.inner.setWidth(iwidth);
41632         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41633         this.linode.setWidth(width);
41634     },
41635 */
41636     /**
41637      * Show or hide the tab
41638      * @param {Boolean} hidden True to hide or false to show.
41639      */
41640     setHidden : function(hidden){
41641         this.hidden = hidden;
41642         this.linode.setStyle("display", hidden ? "none" : "");
41643     },
41644
41645     /**
41646      * Returns true if this tab is "hidden"
41647      * @return {Boolean}
41648      */
41649     isHidden : function(){
41650         return this.hidden;
41651     },
41652
41653     /**
41654      * Returns the text for this tab
41655      * @return {String}
41656      */
41657     getText : function(){
41658         return this.text;
41659     },
41660     /*
41661     autoSize : function(){
41662         //this.el.beginMeasure();
41663         this.textEl.setWidth(1);
41664         /*
41665          *  #2804 [new] Tabs in Roojs
41666          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41667          */
41668         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41669         //this.el.endMeasure();
41670     //},
41671
41672     /**
41673      * Sets the text for the tab (Note: this also sets the tooltip text)
41674      * @param {String} text The tab's text and tooltip
41675      */
41676     setText : function(text){
41677         this.text = text;
41678         this.textEl.update(text);
41679         this.setTooltip(text);
41680         //if(!this.tabPanel.resizeTabs){
41681         //    this.autoSize();
41682         //}
41683     },
41684     /**
41685      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41686      */
41687     activate : function(){
41688         this.tabPanel.activate(this.id);
41689     },
41690
41691     /**
41692      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41693      */
41694     disable : function(){
41695         if(this.tabPanel.active != this){
41696             this.disabled = true;
41697             this.status_node.addClass("disabled");
41698         }
41699     },
41700
41701     /**
41702      * Enables this TabPanelItem if it was previously disabled.
41703      */
41704     enable : function(){
41705         this.disabled = false;
41706         this.status_node.removeClass("disabled");
41707     },
41708
41709     /**
41710      * Sets the content for this TabPanelItem.
41711      * @param {String} content The content
41712      * @param {Boolean} loadScripts true to look for and load scripts
41713      */
41714     setContent : function(content, loadScripts){
41715         this.bodyEl.update(content, loadScripts);
41716     },
41717
41718     /**
41719      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41720      * @return {Roo.UpdateManager} The UpdateManager
41721      */
41722     getUpdateManager : function(){
41723         return this.bodyEl.getUpdateManager();
41724     },
41725
41726     /**
41727      * Set a URL to be used to load the content for this TabPanelItem.
41728      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41729      * @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)
41730      * @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)
41731      * @return {Roo.UpdateManager} The UpdateManager
41732      */
41733     setUrl : function(url, params, loadOnce){
41734         if(this.refreshDelegate){
41735             this.un('activate', this.refreshDelegate);
41736         }
41737         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41738         this.on("activate", this.refreshDelegate);
41739         return this.bodyEl.getUpdateManager();
41740     },
41741
41742     /** @private */
41743     _handleRefresh : function(url, params, loadOnce){
41744         if(!loadOnce || !this.loaded){
41745             var updater = this.bodyEl.getUpdateManager();
41746             updater.update(url, params, this._setLoaded.createDelegate(this));
41747         }
41748     },
41749
41750     /**
41751      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41752      *   Will fail silently if the setUrl method has not been called.
41753      *   This does not activate the panel, just updates its content.
41754      */
41755     refresh : function(){
41756         if(this.refreshDelegate){
41757            this.loaded = false;
41758            this.refreshDelegate();
41759         }
41760     },
41761
41762     /** @private */
41763     _setLoaded : function(){
41764         this.loaded = true;
41765     },
41766
41767     /** @private */
41768     closeClick : function(e){
41769         var o = {};
41770         e.stopEvent();
41771         this.fireEvent("beforeclose", this, o);
41772         if(o.cancel !== true){
41773             this.tabPanel.removeTab(this.id);
41774         }
41775     },
41776     /**
41777      * The text displayed in the tooltip for the close icon.
41778      * @type String
41779      */
41780     closeText : "Close this tab"
41781 });
41782 /**
41783 *    This script refer to:
41784 *    Title: International Telephone Input
41785 *    Author: Jack O'Connor
41786 *    Code version:  v12.1.12
41787 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41788 **/
41789
41790 Roo.bootstrap.PhoneInputData = function() {
41791     var d = [
41792       [
41793         "Afghanistan (‫افغانستان‬‎)",
41794         "af",
41795         "93"
41796       ],
41797       [
41798         "Albania (Shqipëri)",
41799         "al",
41800         "355"
41801       ],
41802       [
41803         "Algeria (‫الجزائر‬‎)",
41804         "dz",
41805         "213"
41806       ],
41807       [
41808         "American Samoa",
41809         "as",
41810         "1684"
41811       ],
41812       [
41813         "Andorra",
41814         "ad",
41815         "376"
41816       ],
41817       [
41818         "Angola",
41819         "ao",
41820         "244"
41821       ],
41822       [
41823         "Anguilla",
41824         "ai",
41825         "1264"
41826       ],
41827       [
41828         "Antigua and Barbuda",
41829         "ag",
41830         "1268"
41831       ],
41832       [
41833         "Argentina",
41834         "ar",
41835         "54"
41836       ],
41837       [
41838         "Armenia (Հայաստան)",
41839         "am",
41840         "374"
41841       ],
41842       [
41843         "Aruba",
41844         "aw",
41845         "297"
41846       ],
41847       [
41848         "Australia",
41849         "au",
41850         "61",
41851         0
41852       ],
41853       [
41854         "Austria (Österreich)",
41855         "at",
41856         "43"
41857       ],
41858       [
41859         "Azerbaijan (Azərbaycan)",
41860         "az",
41861         "994"
41862       ],
41863       [
41864         "Bahamas",
41865         "bs",
41866         "1242"
41867       ],
41868       [
41869         "Bahrain (‫البحرين‬‎)",
41870         "bh",
41871         "973"
41872       ],
41873       [
41874         "Bangladesh (বাংলাদেশ)",
41875         "bd",
41876         "880"
41877       ],
41878       [
41879         "Barbados",
41880         "bb",
41881         "1246"
41882       ],
41883       [
41884         "Belarus (Беларусь)",
41885         "by",
41886         "375"
41887       ],
41888       [
41889         "Belgium (België)",
41890         "be",
41891         "32"
41892       ],
41893       [
41894         "Belize",
41895         "bz",
41896         "501"
41897       ],
41898       [
41899         "Benin (Bénin)",
41900         "bj",
41901         "229"
41902       ],
41903       [
41904         "Bermuda",
41905         "bm",
41906         "1441"
41907       ],
41908       [
41909         "Bhutan (འབྲུག)",
41910         "bt",
41911         "975"
41912       ],
41913       [
41914         "Bolivia",
41915         "bo",
41916         "591"
41917       ],
41918       [
41919         "Bosnia and Herzegovina (Босна и Херцеговина)",
41920         "ba",
41921         "387"
41922       ],
41923       [
41924         "Botswana",
41925         "bw",
41926         "267"
41927       ],
41928       [
41929         "Brazil (Brasil)",
41930         "br",
41931         "55"
41932       ],
41933       [
41934         "British Indian Ocean Territory",
41935         "io",
41936         "246"
41937       ],
41938       [
41939         "British Virgin Islands",
41940         "vg",
41941         "1284"
41942       ],
41943       [
41944         "Brunei",
41945         "bn",
41946         "673"
41947       ],
41948       [
41949         "Bulgaria (България)",
41950         "bg",
41951         "359"
41952       ],
41953       [
41954         "Burkina Faso",
41955         "bf",
41956         "226"
41957       ],
41958       [
41959         "Burundi (Uburundi)",
41960         "bi",
41961         "257"
41962       ],
41963       [
41964         "Cambodia (កម្ពុជា)",
41965         "kh",
41966         "855"
41967       ],
41968       [
41969         "Cameroon (Cameroun)",
41970         "cm",
41971         "237"
41972       ],
41973       [
41974         "Canada",
41975         "ca",
41976         "1",
41977         1,
41978         ["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"]
41979       ],
41980       [
41981         "Cape Verde (Kabu Verdi)",
41982         "cv",
41983         "238"
41984       ],
41985       [
41986         "Caribbean Netherlands",
41987         "bq",
41988         "599",
41989         1
41990       ],
41991       [
41992         "Cayman Islands",
41993         "ky",
41994         "1345"
41995       ],
41996       [
41997         "Central African Republic (République centrafricaine)",
41998         "cf",
41999         "236"
42000       ],
42001       [
42002         "Chad (Tchad)",
42003         "td",
42004         "235"
42005       ],
42006       [
42007         "Chile",
42008         "cl",
42009         "56"
42010       ],
42011       [
42012         "China (中国)",
42013         "cn",
42014         "86"
42015       ],
42016       [
42017         "Christmas Island",
42018         "cx",
42019         "61",
42020         2
42021       ],
42022       [
42023         "Cocos (Keeling) Islands",
42024         "cc",
42025         "61",
42026         1
42027       ],
42028       [
42029         "Colombia",
42030         "co",
42031         "57"
42032       ],
42033       [
42034         "Comoros (‫جزر القمر‬‎)",
42035         "km",
42036         "269"
42037       ],
42038       [
42039         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42040         "cd",
42041         "243"
42042       ],
42043       [
42044         "Congo (Republic) (Congo-Brazzaville)",
42045         "cg",
42046         "242"
42047       ],
42048       [
42049         "Cook Islands",
42050         "ck",
42051         "682"
42052       ],
42053       [
42054         "Costa Rica",
42055         "cr",
42056         "506"
42057       ],
42058       [
42059         "Côte d’Ivoire",
42060         "ci",
42061         "225"
42062       ],
42063       [
42064         "Croatia (Hrvatska)",
42065         "hr",
42066         "385"
42067       ],
42068       [
42069         "Cuba",
42070         "cu",
42071         "53"
42072       ],
42073       [
42074         "Curaçao",
42075         "cw",
42076         "599",
42077         0
42078       ],
42079       [
42080         "Cyprus (Κύπρος)",
42081         "cy",
42082         "357"
42083       ],
42084       [
42085         "Czech Republic (Česká republika)",
42086         "cz",
42087         "420"
42088       ],
42089       [
42090         "Denmark (Danmark)",
42091         "dk",
42092         "45"
42093       ],
42094       [
42095         "Djibouti",
42096         "dj",
42097         "253"
42098       ],
42099       [
42100         "Dominica",
42101         "dm",
42102         "1767"
42103       ],
42104       [
42105         "Dominican Republic (República Dominicana)",
42106         "do",
42107         "1",
42108         2,
42109         ["809", "829", "849"]
42110       ],
42111       [
42112         "Ecuador",
42113         "ec",
42114         "593"
42115       ],
42116       [
42117         "Egypt (‫مصر‬‎)",
42118         "eg",
42119         "20"
42120       ],
42121       [
42122         "El Salvador",
42123         "sv",
42124         "503"
42125       ],
42126       [
42127         "Equatorial Guinea (Guinea Ecuatorial)",
42128         "gq",
42129         "240"
42130       ],
42131       [
42132         "Eritrea",
42133         "er",
42134         "291"
42135       ],
42136       [
42137         "Estonia (Eesti)",
42138         "ee",
42139         "372"
42140       ],
42141       [
42142         "Ethiopia",
42143         "et",
42144         "251"
42145       ],
42146       [
42147         "Falkland Islands (Islas Malvinas)",
42148         "fk",
42149         "500"
42150       ],
42151       [
42152         "Faroe Islands (Føroyar)",
42153         "fo",
42154         "298"
42155       ],
42156       [
42157         "Fiji",
42158         "fj",
42159         "679"
42160       ],
42161       [
42162         "Finland (Suomi)",
42163         "fi",
42164         "358",
42165         0
42166       ],
42167       [
42168         "France",
42169         "fr",
42170         "33"
42171       ],
42172       [
42173         "French Guiana (Guyane française)",
42174         "gf",
42175         "594"
42176       ],
42177       [
42178         "French Polynesia (Polynésie française)",
42179         "pf",
42180         "689"
42181       ],
42182       [
42183         "Gabon",
42184         "ga",
42185         "241"
42186       ],
42187       [
42188         "Gambia",
42189         "gm",
42190         "220"
42191       ],
42192       [
42193         "Georgia (საქართველო)",
42194         "ge",
42195         "995"
42196       ],
42197       [
42198         "Germany (Deutschland)",
42199         "de",
42200         "49"
42201       ],
42202       [
42203         "Ghana (Gaana)",
42204         "gh",
42205         "233"
42206       ],
42207       [
42208         "Gibraltar",
42209         "gi",
42210         "350"
42211       ],
42212       [
42213         "Greece (Ελλάδα)",
42214         "gr",
42215         "30"
42216       ],
42217       [
42218         "Greenland (Kalaallit Nunaat)",
42219         "gl",
42220         "299"
42221       ],
42222       [
42223         "Grenada",
42224         "gd",
42225         "1473"
42226       ],
42227       [
42228         "Guadeloupe",
42229         "gp",
42230         "590",
42231         0
42232       ],
42233       [
42234         "Guam",
42235         "gu",
42236         "1671"
42237       ],
42238       [
42239         "Guatemala",
42240         "gt",
42241         "502"
42242       ],
42243       [
42244         "Guernsey",
42245         "gg",
42246         "44",
42247         1
42248       ],
42249       [
42250         "Guinea (Guinée)",
42251         "gn",
42252         "224"
42253       ],
42254       [
42255         "Guinea-Bissau (Guiné Bissau)",
42256         "gw",
42257         "245"
42258       ],
42259       [
42260         "Guyana",
42261         "gy",
42262         "592"
42263       ],
42264       [
42265         "Haiti",
42266         "ht",
42267         "509"
42268       ],
42269       [
42270         "Honduras",
42271         "hn",
42272         "504"
42273       ],
42274       [
42275         "Hong Kong (香港)",
42276         "hk",
42277         "852"
42278       ],
42279       [
42280         "Hungary (Magyarország)",
42281         "hu",
42282         "36"
42283       ],
42284       [
42285         "Iceland (Ísland)",
42286         "is",
42287         "354"
42288       ],
42289       [
42290         "India (भारत)",
42291         "in",
42292         "91"
42293       ],
42294       [
42295         "Indonesia",
42296         "id",
42297         "62"
42298       ],
42299       [
42300         "Iran (‫ایران‬‎)",
42301         "ir",
42302         "98"
42303       ],
42304       [
42305         "Iraq (‫العراق‬‎)",
42306         "iq",
42307         "964"
42308       ],
42309       [
42310         "Ireland",
42311         "ie",
42312         "353"
42313       ],
42314       [
42315         "Isle of Man",
42316         "im",
42317         "44",
42318         2
42319       ],
42320       [
42321         "Israel (‫ישראל‬‎)",
42322         "il",
42323         "972"
42324       ],
42325       [
42326         "Italy (Italia)",
42327         "it",
42328         "39",
42329         0
42330       ],
42331       [
42332         "Jamaica",
42333         "jm",
42334         "1876"
42335       ],
42336       [
42337         "Japan (日本)",
42338         "jp",
42339         "81"
42340       ],
42341       [
42342         "Jersey",
42343         "je",
42344         "44",
42345         3
42346       ],
42347       [
42348         "Jordan (‫الأردن‬‎)",
42349         "jo",
42350         "962"
42351       ],
42352       [
42353         "Kazakhstan (Казахстан)",
42354         "kz",
42355         "7",
42356         1
42357       ],
42358       [
42359         "Kenya",
42360         "ke",
42361         "254"
42362       ],
42363       [
42364         "Kiribati",
42365         "ki",
42366         "686"
42367       ],
42368       [
42369         "Kosovo",
42370         "xk",
42371         "383"
42372       ],
42373       [
42374         "Kuwait (‫الكويت‬‎)",
42375         "kw",
42376         "965"
42377       ],
42378       [
42379         "Kyrgyzstan (Кыргызстан)",
42380         "kg",
42381         "996"
42382       ],
42383       [
42384         "Laos (ລາວ)",
42385         "la",
42386         "856"
42387       ],
42388       [
42389         "Latvia (Latvija)",
42390         "lv",
42391         "371"
42392       ],
42393       [
42394         "Lebanon (‫لبنان‬‎)",
42395         "lb",
42396         "961"
42397       ],
42398       [
42399         "Lesotho",
42400         "ls",
42401         "266"
42402       ],
42403       [
42404         "Liberia",
42405         "lr",
42406         "231"
42407       ],
42408       [
42409         "Libya (‫ليبيا‬‎)",
42410         "ly",
42411         "218"
42412       ],
42413       [
42414         "Liechtenstein",
42415         "li",
42416         "423"
42417       ],
42418       [
42419         "Lithuania (Lietuva)",
42420         "lt",
42421         "370"
42422       ],
42423       [
42424         "Luxembourg",
42425         "lu",
42426         "352"
42427       ],
42428       [
42429         "Macau (澳門)",
42430         "mo",
42431         "853"
42432       ],
42433       [
42434         "Macedonia (FYROM) (Македонија)",
42435         "mk",
42436         "389"
42437       ],
42438       [
42439         "Madagascar (Madagasikara)",
42440         "mg",
42441         "261"
42442       ],
42443       [
42444         "Malawi",
42445         "mw",
42446         "265"
42447       ],
42448       [
42449         "Malaysia",
42450         "my",
42451         "60"
42452       ],
42453       [
42454         "Maldives",
42455         "mv",
42456         "960"
42457       ],
42458       [
42459         "Mali",
42460         "ml",
42461         "223"
42462       ],
42463       [
42464         "Malta",
42465         "mt",
42466         "356"
42467       ],
42468       [
42469         "Marshall Islands",
42470         "mh",
42471         "692"
42472       ],
42473       [
42474         "Martinique",
42475         "mq",
42476         "596"
42477       ],
42478       [
42479         "Mauritania (‫موريتانيا‬‎)",
42480         "mr",
42481         "222"
42482       ],
42483       [
42484         "Mauritius (Moris)",
42485         "mu",
42486         "230"
42487       ],
42488       [
42489         "Mayotte",
42490         "yt",
42491         "262",
42492         1
42493       ],
42494       [
42495         "Mexico (México)",
42496         "mx",
42497         "52"
42498       ],
42499       [
42500         "Micronesia",
42501         "fm",
42502         "691"
42503       ],
42504       [
42505         "Moldova (Republica Moldova)",
42506         "md",
42507         "373"
42508       ],
42509       [
42510         "Monaco",
42511         "mc",
42512         "377"
42513       ],
42514       [
42515         "Mongolia (Монгол)",
42516         "mn",
42517         "976"
42518       ],
42519       [
42520         "Montenegro (Crna Gora)",
42521         "me",
42522         "382"
42523       ],
42524       [
42525         "Montserrat",
42526         "ms",
42527         "1664"
42528       ],
42529       [
42530         "Morocco (‫المغرب‬‎)",
42531         "ma",
42532         "212",
42533         0
42534       ],
42535       [
42536         "Mozambique (Moçambique)",
42537         "mz",
42538         "258"
42539       ],
42540       [
42541         "Myanmar (Burma) (မြန်မာ)",
42542         "mm",
42543         "95"
42544       ],
42545       [
42546         "Namibia (Namibië)",
42547         "na",
42548         "264"
42549       ],
42550       [
42551         "Nauru",
42552         "nr",
42553         "674"
42554       ],
42555       [
42556         "Nepal (नेपाल)",
42557         "np",
42558         "977"
42559       ],
42560       [
42561         "Netherlands (Nederland)",
42562         "nl",
42563         "31"
42564       ],
42565       [
42566         "New Caledonia (Nouvelle-Calédonie)",
42567         "nc",
42568         "687"
42569       ],
42570       [
42571         "New Zealand",
42572         "nz",
42573         "64"
42574       ],
42575       [
42576         "Nicaragua",
42577         "ni",
42578         "505"
42579       ],
42580       [
42581         "Niger (Nijar)",
42582         "ne",
42583         "227"
42584       ],
42585       [
42586         "Nigeria",
42587         "ng",
42588         "234"
42589       ],
42590       [
42591         "Niue",
42592         "nu",
42593         "683"
42594       ],
42595       [
42596         "Norfolk Island",
42597         "nf",
42598         "672"
42599       ],
42600       [
42601         "North Korea (조선 민주주의 인민 공화국)",
42602         "kp",
42603         "850"
42604       ],
42605       [
42606         "Northern Mariana Islands",
42607         "mp",
42608         "1670"
42609       ],
42610       [
42611         "Norway (Norge)",
42612         "no",
42613         "47",
42614         0
42615       ],
42616       [
42617         "Oman (‫عُمان‬‎)",
42618         "om",
42619         "968"
42620       ],
42621       [
42622         "Pakistan (‫پاکستان‬‎)",
42623         "pk",
42624         "92"
42625       ],
42626       [
42627         "Palau",
42628         "pw",
42629         "680"
42630       ],
42631       [
42632         "Palestine (‫فلسطين‬‎)",
42633         "ps",
42634         "970"
42635       ],
42636       [
42637         "Panama (Panamá)",
42638         "pa",
42639         "507"
42640       ],
42641       [
42642         "Papua New Guinea",
42643         "pg",
42644         "675"
42645       ],
42646       [
42647         "Paraguay",
42648         "py",
42649         "595"
42650       ],
42651       [
42652         "Peru (Perú)",
42653         "pe",
42654         "51"
42655       ],
42656       [
42657         "Philippines",
42658         "ph",
42659         "63"
42660       ],
42661       [
42662         "Poland (Polska)",
42663         "pl",
42664         "48"
42665       ],
42666       [
42667         "Portugal",
42668         "pt",
42669         "351"
42670       ],
42671       [
42672         "Puerto Rico",
42673         "pr",
42674         "1",
42675         3,
42676         ["787", "939"]
42677       ],
42678       [
42679         "Qatar (‫قطر‬‎)",
42680         "qa",
42681         "974"
42682       ],
42683       [
42684         "Réunion (La Réunion)",
42685         "re",
42686         "262",
42687         0
42688       ],
42689       [
42690         "Romania (România)",
42691         "ro",
42692         "40"
42693       ],
42694       [
42695         "Russia (Россия)",
42696         "ru",
42697         "7",
42698         0
42699       ],
42700       [
42701         "Rwanda",
42702         "rw",
42703         "250"
42704       ],
42705       [
42706         "Saint Barthélemy",
42707         "bl",
42708         "590",
42709         1
42710       ],
42711       [
42712         "Saint Helena",
42713         "sh",
42714         "290"
42715       ],
42716       [
42717         "Saint Kitts and Nevis",
42718         "kn",
42719         "1869"
42720       ],
42721       [
42722         "Saint Lucia",
42723         "lc",
42724         "1758"
42725       ],
42726       [
42727         "Saint Martin (Saint-Martin (partie française))",
42728         "mf",
42729         "590",
42730         2
42731       ],
42732       [
42733         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42734         "pm",
42735         "508"
42736       ],
42737       [
42738         "Saint Vincent and the Grenadines",
42739         "vc",
42740         "1784"
42741       ],
42742       [
42743         "Samoa",
42744         "ws",
42745         "685"
42746       ],
42747       [
42748         "San Marino",
42749         "sm",
42750         "378"
42751       ],
42752       [
42753         "São Tomé and Príncipe (São Tomé e Príncipe)",
42754         "st",
42755         "239"
42756       ],
42757       [
42758         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42759         "sa",
42760         "966"
42761       ],
42762       [
42763         "Senegal (Sénégal)",
42764         "sn",
42765         "221"
42766       ],
42767       [
42768         "Serbia (Србија)",
42769         "rs",
42770         "381"
42771       ],
42772       [
42773         "Seychelles",
42774         "sc",
42775         "248"
42776       ],
42777       [
42778         "Sierra Leone",
42779         "sl",
42780         "232"
42781       ],
42782       [
42783         "Singapore",
42784         "sg",
42785         "65"
42786       ],
42787       [
42788         "Sint Maarten",
42789         "sx",
42790         "1721"
42791       ],
42792       [
42793         "Slovakia (Slovensko)",
42794         "sk",
42795         "421"
42796       ],
42797       [
42798         "Slovenia (Slovenija)",
42799         "si",
42800         "386"
42801       ],
42802       [
42803         "Solomon Islands",
42804         "sb",
42805         "677"
42806       ],
42807       [
42808         "Somalia (Soomaaliya)",
42809         "so",
42810         "252"
42811       ],
42812       [
42813         "South Africa",
42814         "za",
42815         "27"
42816       ],
42817       [
42818         "South Korea (대한민국)",
42819         "kr",
42820         "82"
42821       ],
42822       [
42823         "South Sudan (‫جنوب السودان‬‎)",
42824         "ss",
42825         "211"
42826       ],
42827       [
42828         "Spain (España)",
42829         "es",
42830         "34"
42831       ],
42832       [
42833         "Sri Lanka (ශ්‍රී ලංකාව)",
42834         "lk",
42835         "94"
42836       ],
42837       [
42838         "Sudan (‫السودان‬‎)",
42839         "sd",
42840         "249"
42841       ],
42842       [
42843         "Suriname",
42844         "sr",
42845         "597"
42846       ],
42847       [
42848         "Svalbard and Jan Mayen",
42849         "sj",
42850         "47",
42851         1
42852       ],
42853       [
42854         "Swaziland",
42855         "sz",
42856         "268"
42857       ],
42858       [
42859         "Sweden (Sverige)",
42860         "se",
42861         "46"
42862       ],
42863       [
42864         "Switzerland (Schweiz)",
42865         "ch",
42866         "41"
42867       ],
42868       [
42869         "Syria (‫سوريا‬‎)",
42870         "sy",
42871         "963"
42872       ],
42873       [
42874         "Taiwan (台灣)",
42875         "tw",
42876         "886"
42877       ],
42878       [
42879         "Tajikistan",
42880         "tj",
42881         "992"
42882       ],
42883       [
42884         "Tanzania",
42885         "tz",
42886         "255"
42887       ],
42888       [
42889         "Thailand (ไทย)",
42890         "th",
42891         "66"
42892       ],
42893       [
42894         "Timor-Leste",
42895         "tl",
42896         "670"
42897       ],
42898       [
42899         "Togo",
42900         "tg",
42901         "228"
42902       ],
42903       [
42904         "Tokelau",
42905         "tk",
42906         "690"
42907       ],
42908       [
42909         "Tonga",
42910         "to",
42911         "676"
42912       ],
42913       [
42914         "Trinidad and Tobago",
42915         "tt",
42916         "1868"
42917       ],
42918       [
42919         "Tunisia (‫تونس‬‎)",
42920         "tn",
42921         "216"
42922       ],
42923       [
42924         "Turkey (Türkiye)",
42925         "tr",
42926         "90"
42927       ],
42928       [
42929         "Turkmenistan",
42930         "tm",
42931         "993"
42932       ],
42933       [
42934         "Turks and Caicos Islands",
42935         "tc",
42936         "1649"
42937       ],
42938       [
42939         "Tuvalu",
42940         "tv",
42941         "688"
42942       ],
42943       [
42944         "U.S. Virgin Islands",
42945         "vi",
42946         "1340"
42947       ],
42948       [
42949         "Uganda",
42950         "ug",
42951         "256"
42952       ],
42953       [
42954         "Ukraine (Україна)",
42955         "ua",
42956         "380"
42957       ],
42958       [
42959         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42960         "ae",
42961         "971"
42962       ],
42963       [
42964         "United Kingdom",
42965         "gb",
42966         "44",
42967         0
42968       ],
42969       [
42970         "United States",
42971         "us",
42972         "1",
42973         0
42974       ],
42975       [
42976         "Uruguay",
42977         "uy",
42978         "598"
42979       ],
42980       [
42981         "Uzbekistan (Oʻzbekiston)",
42982         "uz",
42983         "998"
42984       ],
42985       [
42986         "Vanuatu",
42987         "vu",
42988         "678"
42989       ],
42990       [
42991         "Vatican City (Città del Vaticano)",
42992         "va",
42993         "39",
42994         1
42995       ],
42996       [
42997         "Venezuela",
42998         "ve",
42999         "58"
43000       ],
43001       [
43002         "Vietnam (Việt Nam)",
43003         "vn",
43004         "84"
43005       ],
43006       [
43007         "Wallis and Futuna (Wallis-et-Futuna)",
43008         "wf",
43009         "681"
43010       ],
43011       [
43012         "Western Sahara (‫الصحراء الغربية‬‎)",
43013         "eh",
43014         "212",
43015         1
43016       ],
43017       [
43018         "Yemen (‫اليمن‬‎)",
43019         "ye",
43020         "967"
43021       ],
43022       [
43023         "Zambia",
43024         "zm",
43025         "260"
43026       ],
43027       [
43028         "Zimbabwe",
43029         "zw",
43030         "263"
43031       ],
43032       [
43033         "Åland Islands",
43034         "ax",
43035         "358",
43036         1
43037       ]
43038   ];
43039   
43040   return d;
43041 }/**
43042 *    This script refer to:
43043 *    Title: International Telephone Input
43044 *    Author: Jack O'Connor
43045 *    Code version:  v12.1.12
43046 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43047 **/
43048
43049 /**
43050  * @class Roo.bootstrap.PhoneInput
43051  * @extends Roo.bootstrap.TriggerField
43052  * An input with International dial-code selection
43053  
43054  * @cfg {String} defaultDialCode default '+852'
43055  * @cfg {Array} preferedCountries default []
43056   
43057  * @constructor
43058  * Create a new PhoneInput.
43059  * @param {Object} config Configuration options
43060  */
43061
43062 Roo.bootstrap.PhoneInput = function(config) {
43063     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43064 };
43065
43066 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43067         
43068         listWidth: undefined,
43069         
43070         selectedClass: 'active',
43071         
43072         invalidClass : "has-warning",
43073         
43074         validClass: 'has-success',
43075         
43076         allowed: '0123456789',
43077         
43078         max_length: 15,
43079         
43080         /**
43081          * @cfg {String} defaultDialCode The default dial code when initializing the input
43082          */
43083         defaultDialCode: '+852',
43084         
43085         /**
43086          * @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
43087          */
43088         preferedCountries: false,
43089         
43090         getAutoCreate : function()
43091         {
43092             var data = Roo.bootstrap.PhoneInputData();
43093             var align = this.labelAlign || this.parentLabelAlign();
43094             var id = Roo.id();
43095             
43096             this.allCountries = [];
43097             this.dialCodeMapping = [];
43098             
43099             for (var i = 0; i < data.length; i++) {
43100               var c = data[i];
43101               this.allCountries[i] = {
43102                 name: c[0],
43103                 iso2: c[1],
43104                 dialCode: c[2],
43105                 priority: c[3] || 0,
43106                 areaCodes: c[4] || null
43107               };
43108               this.dialCodeMapping[c[2]] = {
43109                   name: c[0],
43110                   iso2: c[1],
43111                   priority: c[3] || 0,
43112                   areaCodes: c[4] || null
43113               };
43114             }
43115             
43116             var cfg = {
43117                 cls: 'form-group',
43118                 cn: []
43119             };
43120             
43121             var input =  {
43122                 tag: 'input',
43123                 id : id,
43124                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43125                 maxlength: this.max_length,
43126                 cls : 'form-control tel-input',
43127                 autocomplete: 'new-password'
43128             };
43129             
43130             var hiddenInput = {
43131                 tag: 'input',
43132                 type: 'hidden',
43133                 cls: 'hidden-tel-input'
43134             };
43135             
43136             if (this.name) {
43137                 hiddenInput.name = this.name;
43138             }
43139             
43140             if (this.disabled) {
43141                 input.disabled = true;
43142             }
43143             
43144             var flag_container = {
43145                 tag: 'div',
43146                 cls: 'flag-box',
43147                 cn: [
43148                     {
43149                         tag: 'div',
43150                         cls: 'flag'
43151                     },
43152                     {
43153                         tag: 'div',
43154                         cls: 'caret'
43155                     }
43156                 ]
43157             };
43158             
43159             var box = {
43160                 tag: 'div',
43161                 cls: this.hasFeedback ? 'has-feedback' : '',
43162                 cn: [
43163                     hiddenInput,
43164                     input,
43165                     {
43166                         tag: 'input',
43167                         cls: 'dial-code-holder',
43168                         disabled: true
43169                     }
43170                 ]
43171             };
43172             
43173             var container = {
43174                 cls: 'roo-select2-container input-group',
43175                 cn: [
43176                     flag_container,
43177                     box
43178                 ]
43179             };
43180             
43181             if (this.fieldLabel.length) {
43182                 var indicator = {
43183                     tag: 'i',
43184                     tooltip: 'This field is required'
43185                 };
43186                 
43187                 var label = {
43188                     tag: 'label',
43189                     'for':  id,
43190                     cls: 'control-label',
43191                     cn: []
43192                 };
43193                 
43194                 var label_text = {
43195                     tag: 'span',
43196                     html: this.fieldLabel
43197                 };
43198                 
43199                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43200                 label.cn = [
43201                     indicator,
43202                     label_text
43203                 ];
43204                 
43205                 if(this.indicatorpos == 'right') {
43206                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43207                     label.cn = [
43208                         label_text,
43209                         indicator
43210                     ];
43211                 }
43212                 
43213                 if(align == 'left') {
43214                     container = {
43215                         tag: 'div',
43216                         cn: [
43217                             container
43218                         ]
43219                     };
43220                     
43221                     if(this.labelWidth > 12){
43222                         label.style = "width: " + this.labelWidth + 'px';
43223                     }
43224                     if(this.labelWidth < 13 && this.labelmd == 0){
43225                         this.labelmd = this.labelWidth;
43226                     }
43227                     if(this.labellg > 0){
43228                         label.cls += ' col-lg-' + this.labellg;
43229                         input.cls += ' col-lg-' + (12 - this.labellg);
43230                     }
43231                     if(this.labelmd > 0){
43232                         label.cls += ' col-md-' + this.labelmd;
43233                         container.cls += ' col-md-' + (12 - this.labelmd);
43234                     }
43235                     if(this.labelsm > 0){
43236                         label.cls += ' col-sm-' + this.labelsm;
43237                         container.cls += ' col-sm-' + (12 - this.labelsm);
43238                     }
43239                     if(this.labelxs > 0){
43240                         label.cls += ' col-xs-' + this.labelxs;
43241                         container.cls += ' col-xs-' + (12 - this.labelxs);
43242                     }
43243                 }
43244             }
43245             
43246             cfg.cn = [
43247                 label,
43248                 container
43249             ];
43250             
43251             var settings = this;
43252             
43253             ['xs','sm','md','lg'].map(function(size){
43254                 if (settings[size]) {
43255                     cfg.cls += ' col-' + size + '-' + settings[size];
43256                 }
43257             });
43258             
43259             this.store = new Roo.data.Store({
43260                 proxy : new Roo.data.MemoryProxy({}),
43261                 reader : new Roo.data.JsonReader({
43262                     fields : [
43263                         {
43264                             'name' : 'name',
43265                             'type' : 'string'
43266                         },
43267                         {
43268                             'name' : 'iso2',
43269                             'type' : 'string'
43270                         },
43271                         {
43272                             'name' : 'dialCode',
43273                             'type' : 'string'
43274                         },
43275                         {
43276                             'name' : 'priority',
43277                             'type' : 'string'
43278                         },
43279                         {
43280                             'name' : 'areaCodes',
43281                             'type' : 'string'
43282                         }
43283                     ]
43284                 })
43285             });
43286             
43287             if(!this.preferedCountries) {
43288                 this.preferedCountries = [
43289                     'hk',
43290                     'gb',
43291                     'us'
43292                 ];
43293             }
43294             
43295             var p = this.preferedCountries.reverse();
43296             
43297             if(p) {
43298                 for (var i = 0; i < p.length; i++) {
43299                     for (var j = 0; j < this.allCountries.length; j++) {
43300                         if(this.allCountries[j].iso2 == p[i]) {
43301                             var t = this.allCountries[j];
43302                             this.allCountries.splice(j,1);
43303                             this.allCountries.unshift(t);
43304                         }
43305                     } 
43306                 }
43307             }
43308             
43309             this.store.proxy.data = {
43310                 success: true,
43311                 data: this.allCountries
43312             };
43313             
43314             return cfg;
43315         },
43316         
43317         initEvents : function()
43318         {
43319             this.createList();
43320             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43321             
43322             this.indicator = this.indicatorEl();
43323             this.flag = this.flagEl();
43324             this.dialCodeHolder = this.dialCodeHolderEl();
43325             
43326             this.trigger = this.el.select('div.flag-box',true).first();
43327             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43328             
43329             var _this = this;
43330             
43331             (function(){
43332                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43333                 _this.list.setWidth(lw);
43334             }).defer(100);
43335             
43336             this.list.on('mouseover', this.onViewOver, this);
43337             this.list.on('mousemove', this.onViewMove, this);
43338             this.inputEl().on("keyup", this.onKeyUp, this);
43339             this.inputEl().on("keypress", this.onKeyPress, this);
43340             
43341             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43342
43343             this.view = new Roo.View(this.list, this.tpl, {
43344                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43345             });
43346             
43347             this.view.on('click', this.onViewClick, this);
43348             this.setValue(this.defaultDialCode);
43349         },
43350         
43351         onTriggerClick : function(e)
43352         {
43353             Roo.log('trigger click');
43354             if(this.disabled){
43355                 return;
43356             }
43357             
43358             if(this.isExpanded()){
43359                 this.collapse();
43360                 this.hasFocus = false;
43361             }else {
43362                 this.store.load({});
43363                 this.hasFocus = true;
43364                 this.expand();
43365             }
43366         },
43367         
43368         isExpanded : function()
43369         {
43370             return this.list.isVisible();
43371         },
43372         
43373         collapse : function()
43374         {
43375             if(!this.isExpanded()){
43376                 return;
43377             }
43378             this.list.hide();
43379             Roo.get(document).un('mousedown', this.collapseIf, this);
43380             Roo.get(document).un('mousewheel', this.collapseIf, this);
43381             this.fireEvent('collapse', this);
43382             this.validate();
43383         },
43384         
43385         expand : function()
43386         {
43387             Roo.log('expand');
43388
43389             if(this.isExpanded() || !this.hasFocus){
43390                 return;
43391             }
43392             
43393             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43394             this.list.setWidth(lw);
43395             
43396             this.list.show();
43397             this.restrictHeight();
43398             
43399             Roo.get(document).on('mousedown', this.collapseIf, this);
43400             Roo.get(document).on('mousewheel', this.collapseIf, this);
43401             
43402             this.fireEvent('expand', this);
43403         },
43404         
43405         restrictHeight : function()
43406         {
43407             this.list.alignTo(this.inputEl(), this.listAlign);
43408             this.list.alignTo(this.inputEl(), this.listAlign);
43409         },
43410         
43411         onViewOver : function(e, t)
43412         {
43413             if(this.inKeyMode){
43414                 return;
43415             }
43416             var item = this.view.findItemFromChild(t);
43417             
43418             if(item){
43419                 var index = this.view.indexOf(item);
43420                 this.select(index, false);
43421             }
43422         },
43423
43424         // private
43425         onViewClick : function(view, doFocus, el, e)
43426         {
43427             var index = this.view.getSelectedIndexes()[0];
43428             
43429             var r = this.store.getAt(index);
43430             
43431             if(r){
43432                 this.onSelect(r, index);
43433             }
43434             if(doFocus !== false && !this.blockFocus){
43435                 this.inputEl().focus();
43436             }
43437         },
43438         
43439         onViewMove : function(e, t)
43440         {
43441             this.inKeyMode = false;
43442         },
43443         
43444         select : function(index, scrollIntoView)
43445         {
43446             this.selectedIndex = index;
43447             this.view.select(index);
43448             if(scrollIntoView !== false){
43449                 var el = this.view.getNode(index);
43450                 if(el){
43451                     this.list.scrollChildIntoView(el, false);
43452                 }
43453             }
43454         },
43455         
43456         createList : function()
43457         {
43458             this.list = Roo.get(document.body).createChild({
43459                 tag: 'ul',
43460                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43461                 style: 'display:none'
43462             });
43463             
43464             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43465         },
43466         
43467         collapseIf : function(e)
43468         {
43469             var in_combo  = e.within(this.el);
43470             var in_list =  e.within(this.list);
43471             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43472             
43473             if (in_combo || in_list || is_list) {
43474                 return;
43475             }
43476             this.collapse();
43477         },
43478         
43479         onSelect : function(record, index)
43480         {
43481             if(this.fireEvent('beforeselect', this, record, index) !== false){
43482                 
43483                 this.setFlagClass(record.data.iso2);
43484                 this.setDialCode(record.data.dialCode);
43485                 this.hasFocus = false;
43486                 this.collapse();
43487                 this.fireEvent('select', this, record, index);
43488             }
43489         },
43490         
43491         flagEl : function()
43492         {
43493             var flag = this.el.select('div.flag',true).first();
43494             if(!flag){
43495                 return false;
43496             }
43497             return flag;
43498         },
43499         
43500         dialCodeHolderEl : function()
43501         {
43502             var d = this.el.select('input.dial-code-holder',true).first();
43503             if(!d){
43504                 return false;
43505             }
43506             return d;
43507         },
43508         
43509         setDialCode : function(v)
43510         {
43511             this.dialCodeHolder.dom.value = '+'+v;
43512         },
43513         
43514         setFlagClass : function(n)
43515         {
43516             this.flag.dom.className = 'flag '+n;
43517         },
43518         
43519         getValue : function()
43520         {
43521             var v = this.inputEl().getValue();
43522             if(this.dialCodeHolder) {
43523                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43524             }
43525             return v;
43526         },
43527         
43528         setValue : function(v)
43529         {
43530             var d = this.getDialCode(v);
43531             
43532             //invalid dial code
43533             if(v.length == 0 || !d || d.length == 0) {
43534                 if(this.rendered){
43535                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43536                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43537                 }
43538                 return;
43539             }
43540             
43541             //valid dial code
43542             this.setFlagClass(this.dialCodeMapping[d].iso2);
43543             this.setDialCode(d);
43544             this.inputEl().dom.value = v.replace('+'+d,'');
43545             this.hiddenEl().dom.value = this.getValue();
43546             
43547             this.validate();
43548         },
43549         
43550         getDialCode : function(v)
43551         {
43552             v = v ||  '';
43553             
43554             if (v.length == 0) {
43555                 return this.dialCodeHolder.dom.value;
43556             }
43557             
43558             var dialCode = "";
43559             if (v.charAt(0) != "+") {
43560                 return false;
43561             }
43562             var numericChars = "";
43563             for (var i = 1; i < v.length; i++) {
43564               var c = v.charAt(i);
43565               if (!isNaN(c)) {
43566                 numericChars += c;
43567                 if (this.dialCodeMapping[numericChars]) {
43568                   dialCode = v.substr(1, i);
43569                 }
43570                 if (numericChars.length == 4) {
43571                   break;
43572                 }
43573               }
43574             }
43575             return dialCode;
43576         },
43577         
43578         reset : function()
43579         {
43580             this.setValue(this.defaultDialCode);
43581             this.validate();
43582         },
43583         
43584         hiddenEl : function()
43585         {
43586             return this.el.select('input.hidden-tel-input',true).first();
43587         },
43588         
43589         // after setting val
43590         onKeyUp : function(e){
43591             this.setValue(this.getValue());
43592         },
43593         
43594         onKeyPress : function(e){
43595             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43596                 e.stopEvent();
43597             }
43598         }
43599         
43600 });
43601 /**
43602  * @class Roo.bootstrap.MoneyField
43603  * @extends Roo.bootstrap.ComboBox
43604  * Bootstrap MoneyField class
43605  * 
43606  * @constructor
43607  * Create a new MoneyField.
43608  * @param {Object} config Configuration options
43609  */
43610
43611 Roo.bootstrap.MoneyField = function(config) {
43612     
43613     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43614     
43615 };
43616
43617 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43618     
43619     /**
43620      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43621      */
43622     allowDecimals : true,
43623     /**
43624      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43625      */
43626     decimalSeparator : ".",
43627     /**
43628      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43629      */
43630     decimalPrecision : 0,
43631     /**
43632      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43633      */
43634     allowNegative : true,
43635     /**
43636      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43637      */
43638     allowZero: true,
43639     /**
43640      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43641      */
43642     minValue : Number.NEGATIVE_INFINITY,
43643     /**
43644      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43645      */
43646     maxValue : Number.MAX_VALUE,
43647     /**
43648      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43649      */
43650     minText : "The minimum value for this field is {0}",
43651     /**
43652      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43653      */
43654     maxText : "The maximum value for this field is {0}",
43655     /**
43656      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43657      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43658      */
43659     nanText : "{0} is not a valid number",
43660     /**
43661      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43662      */
43663     castInt : true,
43664     /**
43665      * @cfg {String} defaults currency of the MoneyField
43666      * value should be in lkey
43667      */
43668     defaultCurrency : false,
43669     /**
43670      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43671      */
43672     thousandsDelimiter : false,
43673     /**
43674      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43675      */
43676     max_length: false,
43677     
43678     inputlg : 9,
43679     inputmd : 9,
43680     inputsm : 9,
43681     inputxs : 6,
43682     
43683     store : false,
43684     
43685     getAutoCreate : function()
43686     {
43687         var align = this.labelAlign || this.parentLabelAlign();
43688         
43689         var id = Roo.id();
43690
43691         var cfg = {
43692             cls: 'form-group',
43693             cn: []
43694         };
43695
43696         var input =  {
43697             tag: 'input',
43698             id : id,
43699             cls : 'form-control roo-money-amount-input',
43700             autocomplete: 'new-password'
43701         };
43702         
43703         var hiddenInput = {
43704             tag: 'input',
43705             type: 'hidden',
43706             id: Roo.id(),
43707             cls: 'hidden-number-input'
43708         };
43709         
43710         if(this.max_length) {
43711             input.maxlength = this.max_length; 
43712         }
43713         
43714         if (this.name) {
43715             hiddenInput.name = this.name;
43716         }
43717
43718         if (this.disabled) {
43719             input.disabled = true;
43720         }
43721
43722         var clg = 12 - this.inputlg;
43723         var cmd = 12 - this.inputmd;
43724         var csm = 12 - this.inputsm;
43725         var cxs = 12 - this.inputxs;
43726         
43727         var container = {
43728             tag : 'div',
43729             cls : 'row roo-money-field',
43730             cn : [
43731                 {
43732                     tag : 'div',
43733                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43734                     cn : [
43735                         {
43736                             tag : 'div',
43737                             cls: 'roo-select2-container input-group',
43738                             cn: [
43739                                 {
43740                                     tag : 'input',
43741                                     cls : 'form-control roo-money-currency-input',
43742                                     autocomplete: 'new-password',
43743                                     readOnly : 1,
43744                                     name : this.currencyName
43745                                 },
43746                                 {
43747                                     tag :'span',
43748                                     cls : 'input-group-addon',
43749                                     cn : [
43750                                         {
43751                                             tag: 'span',
43752                                             cls: 'caret'
43753                                         }
43754                                     ]
43755                                 }
43756                             ]
43757                         }
43758                     ]
43759                 },
43760                 {
43761                     tag : 'div',
43762                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43763                     cn : [
43764                         {
43765                             tag: 'div',
43766                             cls: this.hasFeedback ? 'has-feedback' : '',
43767                             cn: [
43768                                 input
43769                             ]
43770                         }
43771                     ]
43772                 }
43773             ]
43774             
43775         };
43776         
43777         if (this.fieldLabel.length) {
43778             var indicator = {
43779                 tag: 'i',
43780                 tooltip: 'This field is required'
43781             };
43782
43783             var label = {
43784                 tag: 'label',
43785                 'for':  id,
43786                 cls: 'control-label',
43787                 cn: []
43788             };
43789
43790             var label_text = {
43791                 tag: 'span',
43792                 html: this.fieldLabel
43793             };
43794
43795             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43796             label.cn = [
43797                 indicator,
43798                 label_text
43799             ];
43800
43801             if(this.indicatorpos == 'right') {
43802                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43803                 label.cn = [
43804                     label_text,
43805                     indicator
43806                 ];
43807             }
43808
43809             if(align == 'left') {
43810                 container = {
43811                     tag: 'div',
43812                     cn: [
43813                         container
43814                     ]
43815                 };
43816
43817                 if(this.labelWidth > 12){
43818                     label.style = "width: " + this.labelWidth + 'px';
43819                 }
43820                 if(this.labelWidth < 13 && this.labelmd == 0){
43821                     this.labelmd = this.labelWidth;
43822                 }
43823                 if(this.labellg > 0){
43824                     label.cls += ' col-lg-' + this.labellg;
43825                     input.cls += ' col-lg-' + (12 - this.labellg);
43826                 }
43827                 if(this.labelmd > 0){
43828                     label.cls += ' col-md-' + this.labelmd;
43829                     container.cls += ' col-md-' + (12 - this.labelmd);
43830                 }
43831                 if(this.labelsm > 0){
43832                     label.cls += ' col-sm-' + this.labelsm;
43833                     container.cls += ' col-sm-' + (12 - this.labelsm);
43834                 }
43835                 if(this.labelxs > 0){
43836                     label.cls += ' col-xs-' + this.labelxs;
43837                     container.cls += ' col-xs-' + (12 - this.labelxs);
43838                 }
43839             }
43840         }
43841
43842         cfg.cn = [
43843             label,
43844             container,
43845             hiddenInput
43846         ];
43847         
43848         var settings = this;
43849
43850         ['xs','sm','md','lg'].map(function(size){
43851             if (settings[size]) {
43852                 cfg.cls += ' col-' + size + '-' + settings[size];
43853             }
43854         });
43855         
43856         return cfg;
43857     },
43858     
43859     initEvents : function()
43860     {
43861         this.indicator = this.indicatorEl();
43862         
43863         this.initCurrencyEvent();
43864         
43865         this.initNumberEvent();
43866     },
43867     
43868     initCurrencyEvent : function()
43869     {
43870         if (!this.store) {
43871             throw "can not find store for combo";
43872         }
43873         
43874         this.store = Roo.factory(this.store, Roo.data);
43875         this.store.parent = this;
43876         
43877         this.createList();
43878         
43879         this.triggerEl = this.el.select('.input-group-addon', true).first();
43880         
43881         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43882         
43883         var _this = this;
43884         
43885         (function(){
43886             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43887             _this.list.setWidth(lw);
43888         }).defer(100);
43889         
43890         this.list.on('mouseover', this.onViewOver, this);
43891         this.list.on('mousemove', this.onViewMove, this);
43892         this.list.on('scroll', this.onViewScroll, this);
43893         
43894         if(!this.tpl){
43895             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43896         }
43897         
43898         this.view = new Roo.View(this.list, this.tpl, {
43899             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43900         });
43901         
43902         this.view.on('click', this.onViewClick, this);
43903         
43904         this.store.on('beforeload', this.onBeforeLoad, this);
43905         this.store.on('load', this.onLoad, this);
43906         this.store.on('loadexception', this.onLoadException, this);
43907         
43908         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43909             "up" : function(e){
43910                 this.inKeyMode = true;
43911                 this.selectPrev();
43912             },
43913
43914             "down" : function(e){
43915                 if(!this.isExpanded()){
43916                     this.onTriggerClick();
43917                 }else{
43918                     this.inKeyMode = true;
43919                     this.selectNext();
43920                 }
43921             },
43922
43923             "enter" : function(e){
43924                 this.collapse();
43925                 
43926                 if(this.fireEvent("specialkey", this, e)){
43927                     this.onViewClick(false);
43928                 }
43929                 
43930                 return true;
43931             },
43932
43933             "esc" : function(e){
43934                 this.collapse();
43935             },
43936
43937             "tab" : function(e){
43938                 this.collapse();
43939                 
43940                 if(this.fireEvent("specialkey", this, e)){
43941                     this.onViewClick(false);
43942                 }
43943                 
43944                 return true;
43945             },
43946
43947             scope : this,
43948
43949             doRelay : function(foo, bar, hname){
43950                 if(hname == 'down' || this.scope.isExpanded()){
43951                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43952                 }
43953                 return true;
43954             },
43955
43956             forceKeyDown: true
43957         });
43958         
43959         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43960         
43961     },
43962     
43963     initNumberEvent : function(e)
43964     {
43965         this.inputEl().on("keydown" , this.fireKey,  this);
43966         this.inputEl().on("focus", this.onFocus,  this);
43967         this.inputEl().on("blur", this.onBlur,  this);
43968         
43969         this.inputEl().relayEvent('keyup', this);
43970         
43971         if(this.indicator){
43972             this.indicator.addClass('invisible');
43973         }
43974  
43975         this.originalValue = this.getValue();
43976         
43977         if(this.validationEvent == 'keyup'){
43978             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43979             this.inputEl().on('keyup', this.filterValidation, this);
43980         }
43981         else if(this.validationEvent !== false){
43982             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43983         }
43984         
43985         if(this.selectOnFocus){
43986             this.on("focus", this.preFocus, this);
43987             
43988         }
43989         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43990             this.inputEl().on("keypress", this.filterKeys, this);
43991         } else {
43992             this.inputEl().relayEvent('keypress', this);
43993         }
43994         
43995         var allowed = "0123456789";
43996         
43997         if(this.allowDecimals){
43998             allowed += this.decimalSeparator;
43999         }
44000         
44001         if(this.allowNegative){
44002             allowed += "-";
44003         }
44004         
44005         if(this.thousandsDelimiter) {
44006             allowed += ",";
44007         }
44008         
44009         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44010         
44011         var keyPress = function(e){
44012             
44013             var k = e.getKey();
44014             
44015             var c = e.getCharCode();
44016             
44017             if(
44018                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44019                     allowed.indexOf(String.fromCharCode(c)) === -1
44020             ){
44021                 e.stopEvent();
44022                 return;
44023             }
44024             
44025             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44026                 return;
44027             }
44028             
44029             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44030                 e.stopEvent();
44031             }
44032         };
44033         
44034         this.inputEl().on("keypress", keyPress, this);
44035         
44036     },
44037     
44038     onTriggerClick : function(e)
44039     {   
44040         if(this.disabled){
44041             return;
44042         }
44043         
44044         this.page = 0;
44045         this.loadNext = false;
44046         
44047         if(this.isExpanded()){
44048             this.collapse();
44049             return;
44050         }
44051         
44052         this.hasFocus = true;
44053         
44054         if(this.triggerAction == 'all') {
44055             this.doQuery(this.allQuery, true);
44056             return;
44057         }
44058         
44059         this.doQuery(this.getRawValue());
44060     },
44061     
44062     getCurrency : function()
44063     {   
44064         var v = this.currencyEl().getValue();
44065         
44066         return v;
44067     },
44068     
44069     restrictHeight : function()
44070     {
44071         this.list.alignTo(this.currencyEl(), this.listAlign);
44072         this.list.alignTo(this.currencyEl(), this.listAlign);
44073     },
44074     
44075     onViewClick : function(view, doFocus, el, e)
44076     {
44077         var index = this.view.getSelectedIndexes()[0];
44078         
44079         var r = this.store.getAt(index);
44080         
44081         if(r){
44082             this.onSelect(r, index);
44083         }
44084     },
44085     
44086     onSelect : function(record, index){
44087         
44088         if(this.fireEvent('beforeselect', this, record, index) !== false){
44089         
44090             this.setFromCurrencyData(index > -1 ? record.data : false);
44091             
44092             this.collapse();
44093             
44094             this.fireEvent('select', this, record, index);
44095         }
44096     },
44097     
44098     setFromCurrencyData : function(o)
44099     {
44100         var currency = '';
44101         
44102         this.lastCurrency = o;
44103         
44104         if (this.currencyField) {
44105             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44106         } else {
44107             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44108         }
44109         
44110         this.lastSelectionText = currency;
44111         
44112         //setting default currency
44113         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44114             this.setCurrency(this.defaultCurrency);
44115             return;
44116         }
44117         
44118         this.setCurrency(currency);
44119     },
44120     
44121     setFromData : function(o)
44122     {
44123         var c = {};
44124         
44125         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44126         
44127         this.setFromCurrencyData(c);
44128         
44129         var value = '';
44130         
44131         if (this.name) {
44132             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44133         } else {
44134             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44135         }
44136         
44137         this.setValue(value);
44138         
44139     },
44140     
44141     setCurrency : function(v)
44142     {   
44143         this.currencyValue = v;
44144         
44145         if(this.rendered){
44146             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44147             this.validate();
44148         }
44149     },
44150     
44151     setValue : function(v)
44152     {
44153         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44154         
44155         this.value = v;
44156         
44157         if(this.rendered){
44158             
44159             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44160             
44161             this.inputEl().dom.value = (v == '') ? '' :
44162                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44163             
44164             if(!this.allowZero && v === '0') {
44165                 this.hiddenEl().dom.value = '';
44166                 this.inputEl().dom.value = '';
44167             }
44168             
44169             this.validate();
44170         }
44171     },
44172     
44173     getRawValue : function()
44174     {
44175         var v = this.inputEl().getValue();
44176         
44177         return v;
44178     },
44179     
44180     getValue : function()
44181     {
44182         return this.fixPrecision(this.parseValue(this.getRawValue()));
44183     },
44184     
44185     parseValue : function(value)
44186     {
44187         if(this.thousandsDelimiter) {
44188             value += "";
44189             r = new RegExp(",", "g");
44190             value = value.replace(r, "");
44191         }
44192         
44193         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44194         return isNaN(value) ? '' : value;
44195         
44196     },
44197     
44198     fixPrecision : function(value)
44199     {
44200         if(this.thousandsDelimiter) {
44201             value += "";
44202             r = new RegExp(",", "g");
44203             value = value.replace(r, "");
44204         }
44205         
44206         var nan = isNaN(value);
44207         
44208         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44209             return nan ? '' : value;
44210         }
44211         return parseFloat(value).toFixed(this.decimalPrecision);
44212     },
44213     
44214     decimalPrecisionFcn : function(v)
44215     {
44216         return Math.floor(v);
44217     },
44218     
44219     validateValue : function(value)
44220     {
44221         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44222             return false;
44223         }
44224         
44225         var num = this.parseValue(value);
44226         
44227         if(isNaN(num)){
44228             this.markInvalid(String.format(this.nanText, value));
44229             return false;
44230         }
44231         
44232         if(num < this.minValue){
44233             this.markInvalid(String.format(this.minText, this.minValue));
44234             return false;
44235         }
44236         
44237         if(num > this.maxValue){
44238             this.markInvalid(String.format(this.maxText, this.maxValue));
44239             return false;
44240         }
44241         
44242         return true;
44243     },
44244     
44245     validate : function()
44246     {
44247         if(this.disabled || this.allowBlank){
44248             this.markValid();
44249             return true;
44250         }
44251         
44252         var currency = this.getCurrency();
44253         
44254         if(this.validateValue(this.getRawValue()) && currency.length){
44255             this.markValid();
44256             return true;
44257         }
44258         
44259         this.markInvalid();
44260         return false;
44261     },
44262     
44263     getName: function()
44264     {
44265         return this.name;
44266     },
44267     
44268     beforeBlur : function()
44269     {
44270         if(!this.castInt){
44271             return;
44272         }
44273         
44274         var v = this.parseValue(this.getRawValue());
44275         
44276         if(v || v == 0){
44277             this.setValue(v);
44278         }
44279     },
44280     
44281     onBlur : function()
44282     {
44283         this.beforeBlur();
44284         
44285         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44286             //this.el.removeClass(this.focusClass);
44287         }
44288         
44289         this.hasFocus = false;
44290         
44291         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44292             this.validate();
44293         }
44294         
44295         var v = this.getValue();
44296         
44297         if(String(v) !== String(this.startValue)){
44298             this.fireEvent('change', this, v, this.startValue);
44299         }
44300         
44301         this.fireEvent("blur", this);
44302     },
44303     
44304     inputEl : function()
44305     {
44306         return this.el.select('.roo-money-amount-input', true).first();
44307     },
44308     
44309     currencyEl : function()
44310     {
44311         return this.el.select('.roo-money-currency-input', true).first();
44312     },
44313     
44314     hiddenEl : function()
44315     {
44316         return this.el.select('input.hidden-number-input',true).first();
44317     }
44318     
44319 });/**
44320  * @class Roo.bootstrap.BezierSignature
44321  * @extends Roo.bootstrap.Component
44322  * Bootstrap BezierSignature class
44323  * This script refer to:
44324  *    Title: Signature Pad
44325  *    Author: szimek
44326  *    Availability: https://github.com/szimek/signature_pad
44327  *
44328  * @constructor
44329  * Create a new BezierSignature
44330  * @param {Object} config The config object
44331  */
44332
44333 Roo.bootstrap.BezierSignature = function(config){
44334     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44335     this.addEvents({
44336         "resize" : true
44337     });
44338 };
44339
44340 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44341 {
44342      
44343     curve_data: [],
44344     
44345     is_empty: true,
44346     
44347     mouse_btn_down: true,
44348     
44349     /**
44350      * @cfg {int} canvas height
44351      */
44352     canvas_height: '200px',
44353     
44354     /**
44355      * @cfg {float|function} Radius of a single dot.
44356      */ 
44357     dot_size: false,
44358     
44359     /**
44360      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44361      */
44362     min_width: 0.5,
44363     
44364     /**
44365      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44366      */
44367     max_width: 2.5,
44368     
44369     /**
44370      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44371      */
44372     throttle: 16,
44373     
44374     /**
44375      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44376      */
44377     min_distance: 5,
44378     
44379     /**
44380      * @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.
44381      */
44382     bg_color: 'rgba(0, 0, 0, 0)',
44383     
44384     /**
44385      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44386      */
44387     dot_color: 'black',
44388     
44389     /**
44390      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44391      */ 
44392     velocity_filter_weight: 0.7,
44393     
44394     /**
44395      * @cfg {function} Callback when stroke begin. 
44396      */
44397     onBegin: false,
44398     
44399     /**
44400      * @cfg {function} Callback when stroke end.
44401      */
44402     onEnd: false,
44403     
44404     getAutoCreate : function()
44405     {
44406         var cls = 'roo-signature column';
44407         
44408         if(this.cls){
44409             cls += ' ' + this.cls;
44410         }
44411         
44412         var col_sizes = [
44413             'lg',
44414             'md',
44415             'sm',
44416             'xs'
44417         ];
44418         
44419         for(var i = 0; i < col_sizes.length; i++) {
44420             if(this[col_sizes[i]]) {
44421                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44422             }
44423         }
44424         
44425         var cfg = {
44426             tag: 'div',
44427             cls: cls,
44428             cn: [
44429                 {
44430                     tag: 'div',
44431                     cls: 'roo-signature-body',
44432                     cn: [
44433                         {
44434                             tag: 'canvas',
44435                             cls: 'roo-signature-body-canvas',
44436                             height: this.canvas_height,
44437                             width: this.canvas_width
44438                         }
44439                     ]
44440                 },
44441                 {
44442                     tag: 'input',
44443                     type: 'file',
44444                     style: 'display: none'
44445                 }
44446             ]
44447         };
44448         
44449         return cfg;
44450     },
44451     
44452     initEvents: function() 
44453     {
44454         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44455         
44456         var canvas = this.canvasEl();
44457         
44458         // mouse && touch event swapping...
44459         canvas.dom.style.touchAction = 'none';
44460         canvas.dom.style.msTouchAction = 'none';
44461         
44462         this.mouse_btn_down = false;
44463         canvas.on('mousedown', this._handleMouseDown, this);
44464         canvas.on('mousemove', this._handleMouseMove, this);
44465         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44466         
44467         if (window.PointerEvent) {
44468             canvas.on('pointerdown', this._handleMouseDown, this);
44469             canvas.on('pointermove', this._handleMouseMove, this);
44470             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44471         }
44472         
44473         if ('ontouchstart' in window) {
44474             canvas.on('touchstart', this._handleTouchStart, this);
44475             canvas.on('touchmove', this._handleTouchMove, this);
44476             canvas.on('touchend', this._handleTouchEnd, this);
44477         }
44478         
44479         Roo.EventManager.onWindowResize(this.resize, this, true);
44480         
44481         // file input event
44482         this.fileEl().on('change', this.uploadImage, this);
44483         
44484         this.clear();
44485         
44486         this.resize();
44487     },
44488     
44489     resize: function(){
44490         
44491         var canvas = this.canvasEl().dom;
44492         var ctx = this.canvasElCtx();
44493         var img_data = false;
44494         
44495         if(canvas.width > 0) {
44496             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44497         }
44498         // setting canvas width will clean img data
44499         canvas.width = 0;
44500         
44501         var style = window.getComputedStyle ? 
44502             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44503             
44504         var padding_left = parseInt(style.paddingLeft) || 0;
44505         var padding_right = parseInt(style.paddingRight) || 0;
44506         
44507         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44508         
44509         if(img_data) {
44510             ctx.putImageData(img_data, 0, 0);
44511         }
44512     },
44513     
44514     _handleMouseDown: function(e)
44515     {
44516         if (e.browserEvent.which === 1) {
44517             this.mouse_btn_down = true;
44518             this.strokeBegin(e);
44519         }
44520     },
44521     
44522     _handleMouseMove: function (e)
44523     {
44524         if (this.mouse_btn_down) {
44525             this.strokeMoveUpdate(e);
44526         }
44527     },
44528     
44529     _handleMouseUp: function (e)
44530     {
44531         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44532             this.mouse_btn_down = false;
44533             this.strokeEnd(e);
44534         }
44535     },
44536     
44537     _handleTouchStart: function (e) {
44538         
44539         e.preventDefault();
44540         if (e.browserEvent.targetTouches.length === 1) {
44541             // var touch = e.browserEvent.changedTouches[0];
44542             // this.strokeBegin(touch);
44543             
44544              this.strokeBegin(e); // assume e catching the correct xy...
44545         }
44546     },
44547     
44548     _handleTouchMove: function (e) {
44549         e.preventDefault();
44550         // var touch = event.targetTouches[0];
44551         // _this._strokeMoveUpdate(touch);
44552         this.strokeMoveUpdate(e);
44553     },
44554     
44555     _handleTouchEnd: function (e) {
44556         var wasCanvasTouched = e.target === this.canvasEl().dom;
44557         if (wasCanvasTouched) {
44558             e.preventDefault();
44559             // var touch = event.changedTouches[0];
44560             // _this._strokeEnd(touch);
44561             this.strokeEnd(e);
44562         }
44563     },
44564     
44565     reset: function () {
44566         this._lastPoints = [];
44567         this._lastVelocity = 0;
44568         this._lastWidth = (this.min_width + this.max_width) / 2;
44569         this.canvasElCtx().fillStyle = this.dot_color;
44570     },
44571     
44572     strokeMoveUpdate: function(e)
44573     {
44574         this.strokeUpdate(e);
44575         
44576         if (this.throttle) {
44577             this.throttleStroke(this.strokeUpdate, this.throttle);
44578         }
44579         else {
44580             this.strokeUpdate(e);
44581         }
44582     },
44583     
44584     strokeBegin: function(e)
44585     {
44586         var newPointGroup = {
44587             color: this.dot_color,
44588             points: []
44589         };
44590         
44591         if (typeof this.onBegin === 'function') {
44592             this.onBegin(e);
44593         }
44594         
44595         this.curve_data.push(newPointGroup);
44596         this.reset();
44597         this.strokeUpdate(e);
44598     },
44599     
44600     strokeUpdate: function(e)
44601     {
44602         var rect = this.canvasEl().dom.getBoundingClientRect();
44603         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44604         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44605         var lastPoints = lastPointGroup.points;
44606         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44607         var isLastPointTooClose = lastPoint
44608             ? point.distanceTo(lastPoint) <= this.min_distance
44609             : false;
44610         var color = lastPointGroup.color;
44611         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44612             var curve = this.addPoint(point);
44613             if (!lastPoint) {
44614                 this.drawDot({color: color, point: point});
44615             }
44616             else if (curve) {
44617                 this.drawCurve({color: color, curve: curve});
44618             }
44619             lastPoints.push({
44620                 time: point.time,
44621                 x: point.x,
44622                 y: point.y
44623             });
44624         }
44625     },
44626     
44627     strokeEnd: function(e)
44628     {
44629         this.strokeUpdate(e);
44630         if (typeof this.onEnd === 'function') {
44631             this.onEnd(e);
44632         }
44633     },
44634     
44635     addPoint:  function (point) {
44636         var _lastPoints = this._lastPoints;
44637         _lastPoints.push(point);
44638         if (_lastPoints.length > 2) {
44639             if (_lastPoints.length === 3) {
44640                 _lastPoints.unshift(_lastPoints[0]);
44641             }
44642             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44643             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44644             _lastPoints.shift();
44645             return curve;
44646         }
44647         return null;
44648     },
44649     
44650     calculateCurveWidths: function (startPoint, endPoint) {
44651         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44652             (1 - this.velocity_filter_weight) * this._lastVelocity;
44653
44654         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44655         var widths = {
44656             end: newWidth,
44657             start: this._lastWidth
44658         };
44659         
44660         this._lastVelocity = velocity;
44661         this._lastWidth = newWidth;
44662         return widths;
44663     },
44664     
44665     drawDot: function (_a) {
44666         var color = _a.color, point = _a.point;
44667         var ctx = this.canvasElCtx();
44668         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44669         ctx.beginPath();
44670         this.drawCurveSegment(point.x, point.y, width);
44671         ctx.closePath();
44672         ctx.fillStyle = color;
44673         ctx.fill();
44674     },
44675     
44676     drawCurve: function (_a) {
44677         var color = _a.color, curve = _a.curve;
44678         var ctx = this.canvasElCtx();
44679         var widthDelta = curve.endWidth - curve.startWidth;
44680         var drawSteps = Math.floor(curve.length()) * 2;
44681         ctx.beginPath();
44682         ctx.fillStyle = color;
44683         for (var i = 0; i < drawSteps; i += 1) {
44684         var t = i / drawSteps;
44685         var tt = t * t;
44686         var ttt = tt * t;
44687         var u = 1 - t;
44688         var uu = u * u;
44689         var uuu = uu * u;
44690         var x = uuu * curve.startPoint.x;
44691         x += 3 * uu * t * curve.control1.x;
44692         x += 3 * u * tt * curve.control2.x;
44693         x += ttt * curve.endPoint.x;
44694         var y = uuu * curve.startPoint.y;
44695         y += 3 * uu * t * curve.control1.y;
44696         y += 3 * u * tt * curve.control2.y;
44697         y += ttt * curve.endPoint.y;
44698         var width = curve.startWidth + ttt * widthDelta;
44699         this.drawCurveSegment(x, y, width);
44700         }
44701         ctx.closePath();
44702         ctx.fill();
44703     },
44704     
44705     drawCurveSegment: function (x, y, width) {
44706         var ctx = this.canvasElCtx();
44707         ctx.moveTo(x, y);
44708         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44709         this.is_empty = false;
44710     },
44711     
44712     clear: function()
44713     {
44714         var ctx = this.canvasElCtx();
44715         var canvas = this.canvasEl().dom;
44716         ctx.fillStyle = this.bg_color;
44717         ctx.clearRect(0, 0, canvas.width, canvas.height);
44718         ctx.fillRect(0, 0, canvas.width, canvas.height);
44719         this.curve_data = [];
44720         this.reset();
44721         this.is_empty = true;
44722     },
44723     
44724     fileEl: function()
44725     {
44726         return  this.el.select('input',true).first();
44727     },
44728     
44729     canvasEl: function()
44730     {
44731         return this.el.select('canvas',true).first();
44732     },
44733     
44734     canvasElCtx: function()
44735     {
44736         return this.el.select('canvas',true).first().dom.getContext('2d');
44737     },
44738     
44739     getImage: function(type)
44740     {
44741         if(this.is_empty) {
44742             return false;
44743         }
44744         
44745         // encryption ?
44746         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44747     },
44748     
44749     drawFromImage: function(img_src)
44750     {
44751         var img = new Image();
44752         
44753         img.onload = function(){
44754             this.canvasElCtx().drawImage(img, 0, 0);
44755         }.bind(this);
44756         
44757         img.src = img_src;
44758         
44759         this.is_empty = false;
44760     },
44761     
44762     selectImage: function()
44763     {
44764         this.fileEl().dom.click();
44765     },
44766     
44767     uploadImage: function(e)
44768     {
44769         var reader = new FileReader();
44770         
44771         reader.onload = function(e){
44772             var img = new Image();
44773             img.onload = function(){
44774                 this.reset();
44775                 this.canvasElCtx().drawImage(img, 0, 0);
44776             }.bind(this);
44777             img.src = e.target.result;
44778         }.bind(this);
44779         
44780         reader.readAsDataURL(e.target.files[0]);
44781     },
44782     
44783     // Bezier Point Constructor
44784     Point: (function () {
44785         function Point(x, y, time) {
44786             this.x = x;
44787             this.y = y;
44788             this.time = time || Date.now();
44789         }
44790         Point.prototype.distanceTo = function (start) {
44791             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44792         };
44793         Point.prototype.equals = function (other) {
44794             return this.x === other.x && this.y === other.y && this.time === other.time;
44795         };
44796         Point.prototype.velocityFrom = function (start) {
44797             return this.time !== start.time
44798             ? this.distanceTo(start) / (this.time - start.time)
44799             : 0;
44800         };
44801         return Point;
44802     }()),
44803     
44804     
44805     // Bezier Constructor
44806     Bezier: (function () {
44807         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44808             this.startPoint = startPoint;
44809             this.control2 = control2;
44810             this.control1 = control1;
44811             this.endPoint = endPoint;
44812             this.startWidth = startWidth;
44813             this.endWidth = endWidth;
44814         }
44815         Bezier.fromPoints = function (points, widths, scope) {
44816             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44817             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44818             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44819         };
44820         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44821             var dx1 = s1.x - s2.x;
44822             var dy1 = s1.y - s2.y;
44823             var dx2 = s2.x - s3.x;
44824             var dy2 = s2.y - s3.y;
44825             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44826             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44827             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44828             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44829             var dxm = m1.x - m2.x;
44830             var dym = m1.y - m2.y;
44831             var k = l2 / (l1 + l2);
44832             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44833             var tx = s2.x - cm.x;
44834             var ty = s2.y - cm.y;
44835             return {
44836                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44837                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44838             };
44839         };
44840         Bezier.prototype.length = function () {
44841             var steps = 10;
44842             var length = 0;
44843             var px;
44844             var py;
44845             for (var i = 0; i <= steps; i += 1) {
44846                 var t = i / steps;
44847                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44848                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44849                 if (i > 0) {
44850                     var xdiff = cx - px;
44851                     var ydiff = cy - py;
44852                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44853                 }
44854                 px = cx;
44855                 py = cy;
44856             }
44857             return length;
44858         };
44859         Bezier.prototype.point = function (t, start, c1, c2, end) {
44860             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44861             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44862             + (3.0 * c2 * (1.0 - t) * t * t)
44863             + (end * t * t * t);
44864         };
44865         return Bezier;
44866     }()),
44867     
44868     throttleStroke: function(fn, wait) {
44869       if (wait === void 0) { wait = 250; }
44870       var previous = 0;
44871       var timeout = null;
44872       var result;
44873       var storedContext;
44874       var storedArgs;
44875       var later = function () {
44876           previous = Date.now();
44877           timeout = null;
44878           result = fn.apply(storedContext, storedArgs);
44879           if (!timeout) {
44880               storedContext = null;
44881               storedArgs = [];
44882           }
44883       };
44884       return function wrapper() {
44885           var args = [];
44886           for (var _i = 0; _i < arguments.length; _i++) {
44887               args[_i] = arguments[_i];
44888           }
44889           var now = Date.now();
44890           var remaining = wait - (now - previous);
44891           storedContext = this;
44892           storedArgs = args;
44893           if (remaining <= 0 || remaining > wait) {
44894               if (timeout) {
44895                   clearTimeout(timeout);
44896                   timeout = null;
44897               }
44898               previous = now;
44899               result = fn.apply(storedContext, storedArgs);
44900               if (!timeout) {
44901                   storedContext = null;
44902                   storedArgs = [];
44903               }
44904           }
44905           else if (!timeout) {
44906               timeout = window.setTimeout(later, remaining);
44907           }
44908           return result;
44909       };
44910   }
44911   
44912 });
44913
44914  
44915
44916