update docs and fix api for BS Alert
[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                 
8537                 html: cm.getColumnHeader(i)
8538             };
8539             
8540             var tooltip = cm.getColumnTooltip(i);
8541             if (tooltip) {
8542                 c.tooltip = tooltip;
8543             }
8544             
8545             
8546             var hh = '';
8547             
8548             if(typeof(config.sortable) != 'undefined' && config.sortable){
8549                 c.cls = 'sortable';
8550                 c.html = '<i class="fa"></i>' + c.html;
8551             }
8552             
8553             // could use BS4 hidden-..-down 
8554             
8555             if(typeof(config.lgHeader) != 'undefined'){
8556                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8557             }
8558             
8559             if(typeof(config.mdHeader) != 'undefined'){
8560                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8561             }
8562             
8563             if(typeof(config.smHeader) != 'undefined'){
8564                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8565             }
8566             
8567             if(typeof(config.xsHeader) != 'undefined'){
8568                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8569             }
8570             
8571             if(hh.length){
8572                 c.html = hh;
8573             }
8574             
8575             if(typeof(config.tooltip) != 'undefined'){
8576                 c.tooltip = config.tooltip;
8577             }
8578             
8579             if(typeof(config.colspan) != 'undefined'){
8580                 c.colspan = config.colspan;
8581             }
8582             
8583             if(typeof(config.hidden) != 'undefined' && config.hidden){
8584                 c.style += ' display:none;';
8585             }
8586             
8587             if(typeof(config.dataIndex) != 'undefined'){
8588                 c.sort = config.dataIndex;
8589             }
8590             
8591            
8592             
8593             if(typeof(config.align) != 'undefined' && config.align.length){
8594                 c.style += ' text-align:' + config.align + ';';
8595             }
8596             
8597             if(typeof(config.width) != 'undefined'){
8598                 c.style += ' width:' + config.width + 'px;';
8599                 this.totalWidth += config.width;
8600             } else {
8601                 this.totalWidth += 100; // assume minimum of 100 per column?
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                  
8614                 if (!config[size]) { // 0 = hidden
8615                     // BS 4 '0' is treated as hide that column and below.
8616                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8617                     return;
8618                 }
8619                 
8620                 c.cls += ' col-' + size + '-' + config[size] + (
8621                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8622                 );
8623                 
8624                 
8625             });
8626             
8627             header.cn.push(c)
8628         }
8629         
8630         return header;
8631     },
8632     
8633     renderBody : function()
8634     {
8635         var body = {
8636             tag: 'tbody',
8637             cn : [
8638                 {
8639                     tag: 'tr',
8640                     cn : [
8641                         {
8642                             tag : 'td',
8643                             colspan :  this.cm.getColumnCount()
8644                         }
8645                     ]
8646                 }
8647             ]
8648         };
8649         
8650         return body;
8651     },
8652     
8653     renderFooter : function()
8654     {
8655         var footer = {
8656             tag: 'tfoot',
8657             cn : [
8658                 {
8659                     tag: 'tr',
8660                     cn : [
8661                         {
8662                             tag : 'td',
8663                             colspan :  this.cm.getColumnCount()
8664                         }
8665                     ]
8666                 }
8667             ]
8668         };
8669         
8670         return footer;
8671     },
8672     
8673     
8674     
8675     onLoad : function()
8676     {
8677 //        Roo.log('ds onload');
8678         this.clear();
8679         
8680         var _this = this;
8681         var cm = this.cm;
8682         var ds = this.store;
8683         
8684         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8685             e.select('i', true).removeClass(['fa-arrow-up', 'fa-arrow-down']);
8686             if (_this.store.sortInfo) {
8687                     
8688                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8689                     e.select('i', true).addClass(['fa-arrow-up']);
8690                 }
8691                 
8692                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8693                     e.select('i', true).addClass(['fa-arrow-down']);
8694                 }
8695             }
8696         });
8697         
8698         var tbody =  this.mainBody;
8699               
8700         if(ds.getCount() > 0){
8701             ds.data.each(function(d,rowIndex){
8702                 var row =  this.renderRow(cm, ds, rowIndex);
8703                 
8704                 tbody.createChild(row);
8705                 
8706                 var _this = this;
8707                 
8708                 if(row.cellObjects.length){
8709                     Roo.each(row.cellObjects, function(r){
8710                         _this.renderCellObject(r);
8711                     })
8712                 }
8713                 
8714             }, this);
8715         }
8716         
8717         var tfoot = this.el.select('tfoot', true).first();
8718         
8719         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8720             
8721             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8722             
8723             var total = this.ds.getTotalCount();
8724             
8725             if(this.footer.pageSize < total){
8726                 this.mainFoot.show();
8727             }
8728         }
8729         
8730         Roo.each(this.el.select('tbody td', true).elements, function(e){
8731             e.on('mouseover', _this.onMouseover, _this);
8732         });
8733         
8734         Roo.each(this.el.select('tbody td', true).elements, function(e){
8735             e.on('mouseout', _this.onMouseout, _this);
8736         });
8737         this.fireEvent('rowsrendered', this);
8738         
8739         this.autoSize();
8740     },
8741     
8742     
8743     onUpdate : function(ds,record)
8744     {
8745         this.refreshRow(record);
8746         this.autoSize();
8747     },
8748     
8749     onRemove : function(ds, record, index, isUpdate){
8750         if(isUpdate !== true){
8751             this.fireEvent("beforerowremoved", this, index, record);
8752         }
8753         var bt = this.mainBody.dom;
8754         
8755         var rows = this.el.select('tbody > tr', true).elements;
8756         
8757         if(typeof(rows[index]) != 'undefined'){
8758             bt.removeChild(rows[index].dom);
8759         }
8760         
8761 //        if(bt.rows[index]){
8762 //            bt.removeChild(bt.rows[index]);
8763 //        }
8764         
8765         if(isUpdate !== true){
8766             //this.stripeRows(index);
8767             //this.syncRowHeights(index, index);
8768             //this.layout();
8769             this.fireEvent("rowremoved", this, index, record);
8770         }
8771     },
8772     
8773     onAdd : function(ds, records, rowIndex)
8774     {
8775         //Roo.log('on Add called');
8776         // - note this does not handle multiple adding very well..
8777         var bt = this.mainBody.dom;
8778         for (var i =0 ; i < records.length;i++) {
8779             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8780             //Roo.log(records[i]);
8781             //Roo.log(this.store.getAt(rowIndex+i));
8782             this.insertRow(this.store, rowIndex + i, false);
8783             return;
8784         }
8785         
8786     },
8787     
8788     
8789     refreshRow : function(record){
8790         var ds = this.store, index;
8791         if(typeof record == 'number'){
8792             index = record;
8793             record = ds.getAt(index);
8794         }else{
8795             index = ds.indexOf(record);
8796             if (index < 0) {
8797                 return; // should not happen - but seems to 
8798             }
8799         }
8800         this.insertRow(ds, index, true);
8801         this.autoSize();
8802         this.onRemove(ds, record, index+1, true);
8803         this.autoSize();
8804         //this.syncRowHeights(index, index);
8805         //this.layout();
8806         this.fireEvent("rowupdated", this, index, record);
8807     },
8808     
8809     insertRow : function(dm, rowIndex, isUpdate){
8810         
8811         if(!isUpdate){
8812             this.fireEvent("beforerowsinserted", this, rowIndex);
8813         }
8814             //var s = this.getScrollState();
8815         var row = this.renderRow(this.cm, this.store, rowIndex);
8816         // insert before rowIndex..
8817         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8818         
8819         var _this = this;
8820                 
8821         if(row.cellObjects.length){
8822             Roo.each(row.cellObjects, function(r){
8823                 _this.renderCellObject(r);
8824             })
8825         }
8826             
8827         if(!isUpdate){
8828             this.fireEvent("rowsinserted", this, rowIndex);
8829             //this.syncRowHeights(firstRow, lastRow);
8830             //this.stripeRows(firstRow);
8831             //this.layout();
8832         }
8833         
8834     },
8835     
8836     
8837     getRowDom : function(rowIndex)
8838     {
8839         var rows = this.el.select('tbody > tr', true).elements;
8840         
8841         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8842         
8843     },
8844     // returns the object tree for a tr..
8845   
8846     
8847     renderRow : function(cm, ds, rowIndex) 
8848     {
8849         var d = ds.getAt(rowIndex);
8850         
8851         var row = {
8852             tag : 'tr',
8853             cls : 'x-row-' + rowIndex,
8854             cn : []
8855         };
8856             
8857         var cellObjects = [];
8858         
8859         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8860             var config = cm.config[i];
8861             
8862             var renderer = cm.getRenderer(i);
8863             var value = '';
8864             var id = false;
8865             
8866             if(typeof(renderer) !== 'undefined'){
8867                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8868             }
8869             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8870             // and are rendered into the cells after the row is rendered - using the id for the element.
8871             
8872             if(typeof(value) === 'object'){
8873                 id = Roo.id();
8874                 cellObjects.push({
8875                     container : id,
8876                     cfg : value 
8877                 })
8878             }
8879             
8880             var rowcfg = {
8881                 record: d,
8882                 rowIndex : rowIndex,
8883                 colIndex : i,
8884                 rowClass : ''
8885             };
8886
8887             this.fireEvent('rowclass', this, rowcfg);
8888             
8889             var td = {
8890                 tag: 'td',
8891                 // this might end up displaying HTML?
8892                 // this is too messy... - better to only do it on columsn you know are going to be too long
8893                 //tooltip : (typeof(value) === 'object') ? '' : value,
8894                 cls : rowcfg.rowClass + ' x-col-' + i,
8895                 style: '',
8896                 html: (typeof(value) === 'object') ? '' : value
8897             };
8898             
8899             if (id) {
8900                 td.id = id;
8901             }
8902             
8903             if(typeof(config.colspan) != 'undefined'){
8904                 td.colspan = config.colspan;
8905             }
8906             
8907             if(typeof(config.hidden) != 'undefined' && config.hidden){
8908                 td.style += ' display:none;';
8909             }
8910             
8911             if(typeof(config.align) != 'undefined' && config.align.length){
8912                 td.style += ' text-align:' + config.align + ';';
8913             }
8914             if(typeof(config.valign) != 'undefined' && config.valign.length){
8915                 td.style += ' vertical-align:' + config.valign + ';';
8916             }
8917             
8918             if(typeof(config.width) != 'undefined'){
8919                 td.style += ' width:' +  config.width + 'px;';
8920             }
8921             
8922             if(typeof(config.cursor) != 'undefined'){
8923                 td.style += ' cursor:' +  config.cursor + ';';
8924             }
8925             
8926             if(typeof(config.cls) != 'undefined'){
8927                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8928             }
8929             
8930             ['xs','sm','md','lg'].map(function(size){
8931                 
8932                 if(typeof(config[size]) == 'undefined'){
8933                     return;
8934                 }
8935                 
8936                 
8937                   
8938                 if (!config[size]) { // 0 = hidden
8939                     // BS 4 '0' is treated as hide that column and below.
8940                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8941                     return;
8942                 }
8943                 
8944                 td.cls += ' col-' + size + '-' + config[size] + (
8945                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8946                 );
8947                  
8948
8949             });
8950             
8951             row.cn.push(td);
8952            
8953         }
8954         
8955         row.cellObjects = cellObjects;
8956         
8957         return row;
8958           
8959     },
8960     
8961     
8962     
8963     onBeforeLoad : function()
8964     {
8965         
8966     },
8967      /**
8968      * Remove all rows
8969      */
8970     clear : function()
8971     {
8972         this.el.select('tbody', true).first().dom.innerHTML = '';
8973     },
8974     /**
8975      * Show or hide a row.
8976      * @param {Number} rowIndex to show or hide
8977      * @param {Boolean} state hide
8978      */
8979     setRowVisibility : function(rowIndex, state)
8980     {
8981         var bt = this.mainBody.dom;
8982         
8983         var rows = this.el.select('tbody > tr', true).elements;
8984         
8985         if(typeof(rows[rowIndex]) == 'undefined'){
8986             return;
8987         }
8988         rows[rowIndex].dom.style.display = state ? '' : 'none';
8989     },
8990     
8991     
8992     getSelectionModel : function(){
8993         if(!this.selModel){
8994             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8995         }
8996         return this.selModel;
8997     },
8998     /*
8999      * Render the Roo.bootstrap object from renderder
9000      */
9001     renderCellObject : function(r)
9002     {
9003         var _this = this;
9004         
9005         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
9006         
9007         var t = r.cfg.render(r.container);
9008         
9009         if(r.cfg.cn){
9010             Roo.each(r.cfg.cn, function(c){
9011                 var child = {
9012                     container: t.getChildContainer(),
9013                     cfg: c
9014                 };
9015                 _this.renderCellObject(child);
9016             })
9017         }
9018     },
9019     
9020     getRowIndex : function(row)
9021     {
9022         var rowIndex = -1;
9023         
9024         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
9025             if(el != row){
9026                 return;
9027             }
9028             
9029             rowIndex = index;
9030         });
9031         
9032         return rowIndex;
9033     },
9034      /**
9035      * Returns the grid's underlying element = used by panel.Grid
9036      * @return {Element} The element
9037      */
9038     getGridEl : function(){
9039         return this.el;
9040     },
9041      /**
9042      * Forces a resize - used by panel.Grid
9043      * @return {Element} The element
9044      */
9045     autoSize : function()
9046     {
9047         //var ctr = Roo.get(this.container.dom.parentElement);
9048         var ctr = Roo.get(this.el.dom);
9049         
9050         var thd = this.getGridEl().select('thead',true).first();
9051         var tbd = this.getGridEl().select('tbody', true).first();
9052         var tfd = this.getGridEl().select('tfoot', true).first();
9053         
9054         var cw = ctr.getWidth();
9055         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
9056         
9057         if (tbd) {
9058             
9059             tbd.setWidth(ctr.getWidth());
9060             // if the body has a max height - and then scrolls - we should perhaps set up the height here
9061             // this needs fixing for various usage - currently only hydra job advers I think..
9062             //tdb.setHeight(
9063             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
9064             //); 
9065             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
9066             cw -= barsize;
9067         }
9068         cw = Math.max(cw, this.totalWidth);
9069         this.getGridEl().select('tbody tr',true).setWidth(cw);
9070         
9071         // resize 'expandable coloumn?
9072         
9073         return; // we doe not have a view in this design..
9074         
9075     },
9076     onBodyScroll: function()
9077     {
9078         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
9079         if(this.mainHead){
9080             this.mainHead.setStyle({
9081                 'position' : 'relative',
9082                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
9083             });
9084         }
9085         
9086         if(this.lazyLoad){
9087             
9088             var scrollHeight = this.mainBody.dom.scrollHeight;
9089             
9090             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
9091             
9092             var height = this.mainBody.getHeight();
9093             
9094             if(scrollHeight - height == scrollTop) {
9095                 
9096                 var total = this.ds.getTotalCount();
9097                 
9098                 if(this.footer.cursor + this.footer.pageSize < total){
9099                     
9100                     this.footer.ds.load({
9101                         params : {
9102                             start : this.footer.cursor + this.footer.pageSize,
9103                             limit : this.footer.pageSize
9104                         },
9105                         add : true
9106                     });
9107                 }
9108             }
9109             
9110         }
9111     },
9112     
9113     onHeaderChange : function()
9114     {
9115         var header = this.renderHeader();
9116         var table = this.el.select('table', true).first();
9117         
9118         this.mainHead.remove();
9119         this.mainHead = table.createChild(header, this.mainBody, false);
9120         
9121         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
9122             e.on('click', this.sort, this);
9123         }, this);
9124         
9125         
9126     },
9127     
9128     onHiddenChange : function(colModel, colIndex, hidden)
9129     {
9130         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
9131         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
9132         
9133         this.CSS.updateRule(thSelector, "display", "");
9134         this.CSS.updateRule(tdSelector, "display", "");
9135         
9136         if(hidden){
9137             this.CSS.updateRule(thSelector, "display", "none");
9138             this.CSS.updateRule(tdSelector, "display", "none");
9139         }
9140         
9141         this.onHeaderChange();
9142         this.onLoad();
9143     },
9144     
9145     setColumnWidth: function(col_index, width)
9146     {
9147         // width = "md-2 xs-2..."
9148         if(!this.colModel.config[col_index]) {
9149             return;
9150         }
9151         
9152         var w = width.split(" ");
9153         
9154         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
9155         
9156         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
9157         
9158         
9159         for(var j = 0; j < w.length; j++) {
9160             
9161             if(!w[j]) {
9162                 continue;
9163             }
9164             
9165             var size_cls = w[j].split("-");
9166             
9167             if(!Number.isInteger(size_cls[1] * 1)) {
9168                 continue;
9169             }
9170             
9171             if(!this.colModel.config[col_index][size_cls[0]]) {
9172                 continue;
9173             }
9174             
9175             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9176                 continue;
9177             }
9178             
9179             h_row[0].classList.replace(
9180                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9181                 "col-"+size_cls[0]+"-"+size_cls[1]
9182             );
9183             
9184             for(var i = 0; i < rows.length; i++) {
9185                 
9186                 var size_cls = w[j].split("-");
9187                 
9188                 if(!Number.isInteger(size_cls[1] * 1)) {
9189                     continue;
9190                 }
9191                 
9192                 if(!this.colModel.config[col_index][size_cls[0]]) {
9193                     continue;
9194                 }
9195                 
9196                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
9197                     continue;
9198                 }
9199                 
9200                 rows[i].classList.replace(
9201                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
9202                     "col-"+size_cls[0]+"-"+size_cls[1]
9203                 );
9204             }
9205             
9206             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
9207         }
9208     }
9209 });
9210
9211  
9212
9213  /*
9214  * - LGPL
9215  *
9216  * table cell
9217  * 
9218  */
9219
9220 /**
9221  * @class Roo.bootstrap.TableCell
9222  * @extends Roo.bootstrap.Component
9223  * Bootstrap TableCell class
9224  * @cfg {String} html cell contain text
9225  * @cfg {String} cls cell class
9226  * @cfg {String} tag cell tag (td|th) default td
9227  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
9228  * @cfg {String} align Aligns the content in a cell
9229  * @cfg {String} axis Categorizes cells
9230  * @cfg {String} bgcolor Specifies the background color of a cell
9231  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9232  * @cfg {Number} colspan Specifies the number of columns a cell should span
9233  * @cfg {String} headers Specifies one or more header cells a cell is related to
9234  * @cfg {Number} height Sets the height of a cell
9235  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
9236  * @cfg {Number} rowspan Sets the number of rows a cell should span
9237  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
9238  * @cfg {String} valign Vertical aligns the content in a cell
9239  * @cfg {Number} width Specifies the width of a cell
9240  * 
9241  * @constructor
9242  * Create a new TableCell
9243  * @param {Object} config The config object
9244  */
9245
9246 Roo.bootstrap.TableCell = function(config){
9247     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
9248 };
9249
9250 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
9251     
9252     html: false,
9253     cls: false,
9254     tag: false,
9255     abbr: false,
9256     align: false,
9257     axis: false,
9258     bgcolor: false,
9259     charoff: false,
9260     colspan: false,
9261     headers: false,
9262     height: false,
9263     nowrap: false,
9264     rowspan: false,
9265     scope: false,
9266     valign: false,
9267     width: false,
9268     
9269     
9270     getAutoCreate : function(){
9271         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
9272         
9273         cfg = {
9274             tag: 'td'
9275         };
9276         
9277         if(this.tag){
9278             cfg.tag = this.tag;
9279         }
9280         
9281         if (this.html) {
9282             cfg.html=this.html
9283         }
9284         if (this.cls) {
9285             cfg.cls=this.cls
9286         }
9287         if (this.abbr) {
9288             cfg.abbr=this.abbr
9289         }
9290         if (this.align) {
9291             cfg.align=this.align
9292         }
9293         if (this.axis) {
9294             cfg.axis=this.axis
9295         }
9296         if (this.bgcolor) {
9297             cfg.bgcolor=this.bgcolor
9298         }
9299         if (this.charoff) {
9300             cfg.charoff=this.charoff
9301         }
9302         if (this.colspan) {
9303             cfg.colspan=this.colspan
9304         }
9305         if (this.headers) {
9306             cfg.headers=this.headers
9307         }
9308         if (this.height) {
9309             cfg.height=this.height
9310         }
9311         if (this.nowrap) {
9312             cfg.nowrap=this.nowrap
9313         }
9314         if (this.rowspan) {
9315             cfg.rowspan=this.rowspan
9316         }
9317         if (this.scope) {
9318             cfg.scope=this.scope
9319         }
9320         if (this.valign) {
9321             cfg.valign=this.valign
9322         }
9323         if (this.width) {
9324             cfg.width=this.width
9325         }
9326         
9327         
9328         return cfg;
9329     }
9330    
9331 });
9332
9333  
9334
9335  /*
9336  * - LGPL
9337  *
9338  * table row
9339  * 
9340  */
9341
9342 /**
9343  * @class Roo.bootstrap.TableRow
9344  * @extends Roo.bootstrap.Component
9345  * Bootstrap TableRow class
9346  * @cfg {String} cls row class
9347  * @cfg {String} align Aligns the content in a table row
9348  * @cfg {String} bgcolor Specifies a background color for a table row
9349  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9350  * @cfg {String} valign Vertical aligns the content in a table row
9351  * 
9352  * @constructor
9353  * Create a new TableRow
9354  * @param {Object} config The config object
9355  */
9356
9357 Roo.bootstrap.TableRow = function(config){
9358     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9359 };
9360
9361 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9362     
9363     cls: false,
9364     align: false,
9365     bgcolor: false,
9366     charoff: false,
9367     valign: false,
9368     
9369     getAutoCreate : function(){
9370         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9371         
9372         cfg = {
9373             tag: 'tr'
9374         };
9375             
9376         if(this.cls){
9377             cfg.cls = this.cls;
9378         }
9379         if(this.align){
9380             cfg.align = this.align;
9381         }
9382         if(this.bgcolor){
9383             cfg.bgcolor = this.bgcolor;
9384         }
9385         if(this.charoff){
9386             cfg.charoff = this.charoff;
9387         }
9388         if(this.valign){
9389             cfg.valign = this.valign;
9390         }
9391         
9392         return cfg;
9393     }
9394    
9395 });
9396
9397  
9398
9399  /*
9400  * - LGPL
9401  *
9402  * table body
9403  * 
9404  */
9405
9406 /**
9407  * @class Roo.bootstrap.TableBody
9408  * @extends Roo.bootstrap.Component
9409  * Bootstrap TableBody class
9410  * @cfg {String} cls element class
9411  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9412  * @cfg {String} align Aligns the content inside the element
9413  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9414  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9415  * 
9416  * @constructor
9417  * Create a new TableBody
9418  * @param {Object} config The config object
9419  */
9420
9421 Roo.bootstrap.TableBody = function(config){
9422     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9423 };
9424
9425 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9426     
9427     cls: false,
9428     tag: false,
9429     align: false,
9430     charoff: false,
9431     valign: false,
9432     
9433     getAutoCreate : function(){
9434         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9435         
9436         cfg = {
9437             tag: 'tbody'
9438         };
9439             
9440         if (this.cls) {
9441             cfg.cls=this.cls
9442         }
9443         if(this.tag){
9444             cfg.tag = this.tag;
9445         }
9446         
9447         if(this.align){
9448             cfg.align = this.align;
9449         }
9450         if(this.charoff){
9451             cfg.charoff = this.charoff;
9452         }
9453         if(this.valign){
9454             cfg.valign = this.valign;
9455         }
9456         
9457         return cfg;
9458     }
9459     
9460     
9461 //    initEvents : function()
9462 //    {
9463 //        
9464 //        if(!this.store){
9465 //            return;
9466 //        }
9467 //        
9468 //        this.store = Roo.factory(this.store, Roo.data);
9469 //        this.store.on('load', this.onLoad, this);
9470 //        
9471 //        this.store.load();
9472 //        
9473 //    },
9474 //    
9475 //    onLoad: function () 
9476 //    {   
9477 //        this.fireEvent('load', this);
9478 //    }
9479 //    
9480 //   
9481 });
9482
9483  
9484
9485  /*
9486  * Based on:
9487  * Ext JS Library 1.1.1
9488  * Copyright(c) 2006-2007, Ext JS, LLC.
9489  *
9490  * Originally Released Under LGPL - original licence link has changed is not relivant.
9491  *
9492  * Fork - LGPL
9493  * <script type="text/javascript">
9494  */
9495
9496 // as we use this in bootstrap.
9497 Roo.namespace('Roo.form');
9498  /**
9499  * @class Roo.form.Action
9500  * Internal Class used to handle form actions
9501  * @constructor
9502  * @param {Roo.form.BasicForm} el The form element or its id
9503  * @param {Object} config Configuration options
9504  */
9505
9506  
9507  
9508 // define the action interface
9509 Roo.form.Action = function(form, options){
9510     this.form = form;
9511     this.options = options || {};
9512 };
9513 /**
9514  * Client Validation Failed
9515  * @const 
9516  */
9517 Roo.form.Action.CLIENT_INVALID = 'client';
9518 /**
9519  * Server Validation Failed
9520  * @const 
9521  */
9522 Roo.form.Action.SERVER_INVALID = 'server';
9523  /**
9524  * Connect to Server Failed
9525  * @const 
9526  */
9527 Roo.form.Action.CONNECT_FAILURE = 'connect';
9528 /**
9529  * Reading Data from Server Failed
9530  * @const 
9531  */
9532 Roo.form.Action.LOAD_FAILURE = 'load';
9533
9534 Roo.form.Action.prototype = {
9535     type : 'default',
9536     failureType : undefined,
9537     response : undefined,
9538     result : undefined,
9539
9540     // interface method
9541     run : function(options){
9542
9543     },
9544
9545     // interface method
9546     success : function(response){
9547
9548     },
9549
9550     // interface method
9551     handleResponse : function(response){
9552
9553     },
9554
9555     // default connection failure
9556     failure : function(response){
9557         
9558         this.response = response;
9559         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9560         this.form.afterAction(this, false);
9561     },
9562
9563     processResponse : function(response){
9564         this.response = response;
9565         if(!response.responseText){
9566             return true;
9567         }
9568         this.result = this.handleResponse(response);
9569         return this.result;
9570     },
9571
9572     // utility functions used internally
9573     getUrl : function(appendParams){
9574         var url = this.options.url || this.form.url || this.form.el.dom.action;
9575         if(appendParams){
9576             var p = this.getParams();
9577             if(p){
9578                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9579             }
9580         }
9581         return url;
9582     },
9583
9584     getMethod : function(){
9585         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9586     },
9587
9588     getParams : function(){
9589         var bp = this.form.baseParams;
9590         var p = this.options.params;
9591         if(p){
9592             if(typeof p == "object"){
9593                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9594             }else if(typeof p == 'string' && bp){
9595                 p += '&' + Roo.urlEncode(bp);
9596             }
9597         }else if(bp){
9598             p = Roo.urlEncode(bp);
9599         }
9600         return p;
9601     },
9602
9603     createCallback : function(){
9604         return {
9605             success: this.success,
9606             failure: this.failure,
9607             scope: this,
9608             timeout: (this.form.timeout*1000),
9609             upload: this.form.fileUpload ? this.success : undefined
9610         };
9611     }
9612 };
9613
9614 Roo.form.Action.Submit = function(form, options){
9615     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9616 };
9617
9618 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9619     type : 'submit',
9620
9621     haveProgress : false,
9622     uploadComplete : false,
9623     
9624     // uploadProgress indicator.
9625     uploadProgress : function()
9626     {
9627         if (!this.form.progressUrl) {
9628             return;
9629         }
9630         
9631         if (!this.haveProgress) {
9632             Roo.MessageBox.progress("Uploading", "Uploading");
9633         }
9634         if (this.uploadComplete) {
9635            Roo.MessageBox.hide();
9636            return;
9637         }
9638         
9639         this.haveProgress = true;
9640    
9641         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9642         
9643         var c = new Roo.data.Connection();
9644         c.request({
9645             url : this.form.progressUrl,
9646             params: {
9647                 id : uid
9648             },
9649             method: 'GET',
9650             success : function(req){
9651                //console.log(data);
9652                 var rdata = false;
9653                 var edata;
9654                 try  {
9655                    rdata = Roo.decode(req.responseText)
9656                 } catch (e) {
9657                     Roo.log("Invalid data from server..");
9658                     Roo.log(edata);
9659                     return;
9660                 }
9661                 if (!rdata || !rdata.success) {
9662                     Roo.log(rdata);
9663                     Roo.MessageBox.alert(Roo.encode(rdata));
9664                     return;
9665                 }
9666                 var data = rdata.data;
9667                 
9668                 if (this.uploadComplete) {
9669                    Roo.MessageBox.hide();
9670                    return;
9671                 }
9672                    
9673                 if (data){
9674                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9675                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9676                     );
9677                 }
9678                 this.uploadProgress.defer(2000,this);
9679             },
9680        
9681             failure: function(data) {
9682                 Roo.log('progress url failed ');
9683                 Roo.log(data);
9684             },
9685             scope : this
9686         });
9687            
9688     },
9689     
9690     
9691     run : function()
9692     {
9693         // run get Values on the form, so it syncs any secondary forms.
9694         this.form.getValues();
9695         
9696         var o = this.options;
9697         var method = this.getMethod();
9698         var isPost = method == 'POST';
9699         if(o.clientValidation === false || this.form.isValid()){
9700             
9701             if (this.form.progressUrl) {
9702                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9703                     (new Date() * 1) + '' + Math.random());
9704                     
9705             } 
9706             
9707             
9708             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9709                 form:this.form.el.dom,
9710                 url:this.getUrl(!isPost),
9711                 method: method,
9712                 params:isPost ? this.getParams() : null,
9713                 isUpload: this.form.fileUpload,
9714                 formData : this.form.formData
9715             }));
9716             
9717             this.uploadProgress();
9718
9719         }else if (o.clientValidation !== false){ // client validation failed
9720             this.failureType = Roo.form.Action.CLIENT_INVALID;
9721             this.form.afterAction(this, false);
9722         }
9723     },
9724
9725     success : function(response)
9726     {
9727         this.uploadComplete= true;
9728         if (this.haveProgress) {
9729             Roo.MessageBox.hide();
9730         }
9731         
9732         
9733         var result = this.processResponse(response);
9734         if(result === true || result.success){
9735             this.form.afterAction(this, true);
9736             return;
9737         }
9738         if(result.errors){
9739             this.form.markInvalid(result.errors);
9740             this.failureType = Roo.form.Action.SERVER_INVALID;
9741         }
9742         this.form.afterAction(this, false);
9743     },
9744     failure : function(response)
9745     {
9746         this.uploadComplete= true;
9747         if (this.haveProgress) {
9748             Roo.MessageBox.hide();
9749         }
9750         
9751         this.response = response;
9752         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9753         this.form.afterAction(this, false);
9754     },
9755     
9756     handleResponse : function(response){
9757         if(this.form.errorReader){
9758             var rs = this.form.errorReader.read(response);
9759             var errors = [];
9760             if(rs.records){
9761                 for(var i = 0, len = rs.records.length; i < len; i++) {
9762                     var r = rs.records[i];
9763                     errors[i] = r.data;
9764                 }
9765             }
9766             if(errors.length < 1){
9767                 errors = null;
9768             }
9769             return {
9770                 success : rs.success,
9771                 errors : errors
9772             };
9773         }
9774         var ret = false;
9775         try {
9776             ret = Roo.decode(response.responseText);
9777         } catch (e) {
9778             ret = {
9779                 success: false,
9780                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9781                 errors : []
9782             };
9783         }
9784         return ret;
9785         
9786     }
9787 });
9788
9789
9790 Roo.form.Action.Load = function(form, options){
9791     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9792     this.reader = this.form.reader;
9793 };
9794
9795 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9796     type : 'load',
9797
9798     run : function(){
9799         
9800         Roo.Ajax.request(Roo.apply(
9801                 this.createCallback(), {
9802                     method:this.getMethod(),
9803                     url:this.getUrl(false),
9804                     params:this.getParams()
9805         }));
9806     },
9807
9808     success : function(response){
9809         
9810         var result = this.processResponse(response);
9811         if(result === true || !result.success || !result.data){
9812             this.failureType = Roo.form.Action.LOAD_FAILURE;
9813             this.form.afterAction(this, false);
9814             return;
9815         }
9816         this.form.clearInvalid();
9817         this.form.setValues(result.data);
9818         this.form.afterAction(this, true);
9819     },
9820
9821     handleResponse : function(response){
9822         if(this.form.reader){
9823             var rs = this.form.reader.read(response);
9824             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9825             return {
9826                 success : rs.success,
9827                 data : data
9828             };
9829         }
9830         return Roo.decode(response.responseText);
9831     }
9832 });
9833
9834 Roo.form.Action.ACTION_TYPES = {
9835     'load' : Roo.form.Action.Load,
9836     'submit' : Roo.form.Action.Submit
9837 };/*
9838  * - LGPL
9839  *
9840  * form
9841  *
9842  */
9843
9844 /**
9845  * @class Roo.bootstrap.Form
9846  * @extends Roo.bootstrap.Component
9847  * Bootstrap Form class
9848  * @cfg {String} method  GET | POST (default POST)
9849  * @cfg {String} labelAlign top | left (default top)
9850  * @cfg {String} align left  | right - for navbars
9851  * @cfg {Boolean} loadMask load mask when submit (default true)
9852
9853  *
9854  * @constructor
9855  * Create a new Form
9856  * @param {Object} config The config object
9857  */
9858
9859
9860 Roo.bootstrap.Form = function(config){
9861     
9862     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9863     
9864     Roo.bootstrap.Form.popover.apply();
9865     
9866     this.addEvents({
9867         /**
9868          * @event clientvalidation
9869          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9870          * @param {Form} this
9871          * @param {Boolean} valid true if the form has passed client-side validation
9872          */
9873         clientvalidation: true,
9874         /**
9875          * @event beforeaction
9876          * Fires before any action is performed. Return false to cancel the action.
9877          * @param {Form} this
9878          * @param {Action} action The action to be performed
9879          */
9880         beforeaction: true,
9881         /**
9882          * @event actionfailed
9883          * Fires when an action fails.
9884          * @param {Form} this
9885          * @param {Action} action The action that failed
9886          */
9887         actionfailed : true,
9888         /**
9889          * @event actioncomplete
9890          * Fires when an action is completed.
9891          * @param {Form} this
9892          * @param {Action} action The action that completed
9893          */
9894         actioncomplete : true
9895     });
9896 };
9897
9898 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9899
9900      /**
9901      * @cfg {String} method
9902      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9903      */
9904     method : 'POST',
9905     /**
9906      * @cfg {String} url
9907      * The URL to use for form actions if one isn't supplied in the action options.
9908      */
9909     /**
9910      * @cfg {Boolean} fileUpload
9911      * Set to true if this form is a file upload.
9912      */
9913
9914     /**
9915      * @cfg {Object} baseParams
9916      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9917      */
9918
9919     /**
9920      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9921      */
9922     timeout: 30,
9923     /**
9924      * @cfg {Sting} align (left|right) for navbar forms
9925      */
9926     align : 'left',
9927
9928     // private
9929     activeAction : null,
9930
9931     /**
9932      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9933      * element by passing it or its id or mask the form itself by passing in true.
9934      * @type Mixed
9935      */
9936     waitMsgTarget : false,
9937
9938     loadMask : true,
9939     
9940     /**
9941      * @cfg {Boolean} errorMask (true|false) default false
9942      */
9943     errorMask : false,
9944     
9945     /**
9946      * @cfg {Number} maskOffset Default 100
9947      */
9948     maskOffset : 100,
9949     
9950     /**
9951      * @cfg {Boolean} maskBody
9952      */
9953     maskBody : false,
9954
9955     getAutoCreate : function(){
9956
9957         var cfg = {
9958             tag: 'form',
9959             method : this.method || 'POST',
9960             id : this.id || Roo.id(),
9961             cls : ''
9962         };
9963         if (this.parent().xtype.match(/^Nav/)) {
9964             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9965
9966         }
9967
9968         if (this.labelAlign == 'left' ) {
9969             cfg.cls += ' form-horizontal';
9970         }
9971
9972
9973         return cfg;
9974     },
9975     initEvents : function()
9976     {
9977         this.el.on('submit', this.onSubmit, this);
9978         // this was added as random key presses on the form where triggering form submit.
9979         this.el.on('keypress', function(e) {
9980             if (e.getCharCode() != 13) {
9981                 return true;
9982             }
9983             // we might need to allow it for textareas.. and some other items.
9984             // check e.getTarget().
9985
9986             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9987                 return true;
9988             }
9989
9990             Roo.log("keypress blocked");
9991
9992             e.preventDefault();
9993             return false;
9994         });
9995         
9996     },
9997     // private
9998     onSubmit : function(e){
9999         e.stopEvent();
10000     },
10001
10002      /**
10003      * Returns true if client-side validation on the form is successful.
10004      * @return Boolean
10005      */
10006     isValid : function(){
10007         var items = this.getItems();
10008         var valid = true;
10009         var target = false;
10010         
10011         items.each(function(f){
10012             
10013             if(f.validate()){
10014                 return;
10015             }
10016             
10017             Roo.log('invalid field: ' + f.name);
10018             
10019             valid = false;
10020
10021             if(!target && f.el.isVisible(true)){
10022                 target = f;
10023             }
10024            
10025         });
10026         
10027         if(this.errorMask && !valid){
10028             Roo.bootstrap.Form.popover.mask(this, target);
10029         }
10030         
10031         return valid;
10032     },
10033     
10034     /**
10035      * Returns true if any fields in this form have changed since their original load.
10036      * @return Boolean
10037      */
10038     isDirty : function(){
10039         var dirty = false;
10040         var items = this.getItems();
10041         items.each(function(f){
10042            if(f.isDirty()){
10043                dirty = true;
10044                return false;
10045            }
10046            return true;
10047         });
10048         return dirty;
10049     },
10050      /**
10051      * Performs a predefined action (submit or load) or custom actions you define on this form.
10052      * @param {String} actionName The name of the action type
10053      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
10054      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
10055      * accept other config options):
10056      * <pre>
10057 Property          Type             Description
10058 ----------------  ---------------  ----------------------------------------------------------------------------------
10059 url               String           The url for the action (defaults to the form's url)
10060 method            String           The form method to use (defaults to the form's method, or POST if not defined)
10061 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
10062 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
10063                                    validate the form on the client (defaults to false)
10064      * </pre>
10065      * @return {BasicForm} this
10066      */
10067     doAction : function(action, options){
10068         if(typeof action == 'string'){
10069             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
10070         }
10071         if(this.fireEvent('beforeaction', this, action) !== false){
10072             this.beforeAction(action);
10073             action.run.defer(100, action);
10074         }
10075         return this;
10076     },
10077
10078     // private
10079     beforeAction : function(action){
10080         var o = action.options;
10081         
10082         if(this.loadMask){
10083             
10084             if(this.maskBody){
10085                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
10086             } else {
10087                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10088             }
10089         }
10090         // not really supported yet.. ??
10091
10092         //if(this.waitMsgTarget === true){
10093         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
10094         //}else if(this.waitMsgTarget){
10095         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
10096         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
10097         //}else {
10098         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
10099        // }
10100
10101     },
10102
10103     // private
10104     afterAction : function(action, success){
10105         this.activeAction = null;
10106         var o = action.options;
10107
10108         if(this.loadMask){
10109             
10110             if(this.maskBody){
10111                 Roo.get(document.body).unmask();
10112             } else {
10113                 this.el.unmask();
10114             }
10115         }
10116         
10117         //if(this.waitMsgTarget === true){
10118 //            this.el.unmask();
10119         //}else if(this.waitMsgTarget){
10120         //    this.waitMsgTarget.unmask();
10121         //}else{
10122         //    Roo.MessageBox.updateProgress(1);
10123         //    Roo.MessageBox.hide();
10124        // }
10125         //
10126         if(success){
10127             if(o.reset){
10128                 this.reset();
10129             }
10130             Roo.callback(o.success, o.scope, [this, action]);
10131             this.fireEvent('actioncomplete', this, action);
10132
10133         }else{
10134
10135             // failure condition..
10136             // we have a scenario where updates need confirming.
10137             // eg. if a locking scenario exists..
10138             // we look for { errors : { needs_confirm : true }} in the response.
10139             if (
10140                 (typeof(action.result) != 'undefined')  &&
10141                 (typeof(action.result.errors) != 'undefined')  &&
10142                 (typeof(action.result.errors.needs_confirm) != 'undefined')
10143            ){
10144                 var _t = this;
10145                 Roo.log("not supported yet");
10146                  /*
10147
10148                 Roo.MessageBox.confirm(
10149                     "Change requires confirmation",
10150                     action.result.errorMsg,
10151                     function(r) {
10152                         if (r != 'yes') {
10153                             return;
10154                         }
10155                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
10156                     }
10157
10158                 );
10159                 */
10160
10161
10162                 return;
10163             }
10164
10165             Roo.callback(o.failure, o.scope, [this, action]);
10166             // show an error message if no failed handler is set..
10167             if (!this.hasListener('actionfailed')) {
10168                 Roo.log("need to add dialog support");
10169                 /*
10170                 Roo.MessageBox.alert("Error",
10171                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
10172                         action.result.errorMsg :
10173                         "Saving Failed, please check your entries or try again"
10174                 );
10175                 */
10176             }
10177
10178             this.fireEvent('actionfailed', this, action);
10179         }
10180
10181     },
10182     /**
10183      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
10184      * @param {String} id The value to search for
10185      * @return Field
10186      */
10187     findField : function(id){
10188         var items = this.getItems();
10189         var field = items.get(id);
10190         if(!field){
10191              items.each(function(f){
10192                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
10193                     field = f;
10194                     return false;
10195                 }
10196                 return true;
10197             });
10198         }
10199         return field || null;
10200     },
10201      /**
10202      * Mark fields in this form invalid in bulk.
10203      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
10204      * @return {BasicForm} this
10205      */
10206     markInvalid : function(errors){
10207         if(errors instanceof Array){
10208             for(var i = 0, len = errors.length; i < len; i++){
10209                 var fieldError = errors[i];
10210                 var f = this.findField(fieldError.id);
10211                 if(f){
10212                     f.markInvalid(fieldError.msg);
10213                 }
10214             }
10215         }else{
10216             var field, id;
10217             for(id in errors){
10218                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
10219                     field.markInvalid(errors[id]);
10220                 }
10221             }
10222         }
10223         //Roo.each(this.childForms || [], function (f) {
10224         //    f.markInvalid(errors);
10225         //});
10226
10227         return this;
10228     },
10229
10230     /**
10231      * Set values for fields in this form in bulk.
10232      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
10233      * @return {BasicForm} this
10234      */
10235     setValues : function(values){
10236         if(values instanceof Array){ // array of objects
10237             for(var i = 0, len = values.length; i < len; i++){
10238                 var v = values[i];
10239                 var f = this.findField(v.id);
10240                 if(f){
10241                     f.setValue(v.value);
10242                     if(this.trackResetOnLoad){
10243                         f.originalValue = f.getValue();
10244                     }
10245                 }
10246             }
10247         }else{ // object hash
10248             var field, id;
10249             for(id in values){
10250                 if(typeof values[id] != 'function' && (field = this.findField(id))){
10251
10252                     if (field.setFromData &&
10253                         field.valueField &&
10254                         field.displayField &&
10255                         // combos' with local stores can
10256                         // be queried via setValue()
10257                         // to set their value..
10258                         (field.store && !field.store.isLocal)
10259                         ) {
10260                         // it's a combo
10261                         var sd = { };
10262                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
10263                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
10264                         field.setFromData(sd);
10265
10266                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
10267                         
10268                         field.setFromData(values);
10269                         
10270                     } else {
10271                         field.setValue(values[id]);
10272                     }
10273
10274
10275                     if(this.trackResetOnLoad){
10276                         field.originalValue = field.getValue();
10277                     }
10278                 }
10279             }
10280         }
10281
10282         //Roo.each(this.childForms || [], function (f) {
10283         //    f.setValues(values);
10284         //});
10285
10286         return this;
10287     },
10288
10289     /**
10290      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
10291      * they are returned as an array.
10292      * @param {Boolean} asString
10293      * @return {Object}
10294      */
10295     getValues : function(asString){
10296         //if (this.childForms) {
10297             // copy values from the child forms
10298         //    Roo.each(this.childForms, function (f) {
10299         //        this.setValues(f.getValues());
10300         //    }, this);
10301         //}
10302
10303
10304
10305         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10306         if(asString === true){
10307             return fs;
10308         }
10309         return Roo.urlDecode(fs);
10310     },
10311
10312     /**
10313      * Returns the fields in this form as an object with key/value pairs.
10314      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10315      * @return {Object}
10316      */
10317     getFieldValues : function(with_hidden)
10318     {
10319         var items = this.getItems();
10320         var ret = {};
10321         items.each(function(f){
10322             
10323             if (!f.getName()) {
10324                 return;
10325             }
10326             
10327             var v = f.getValue();
10328             
10329             if (f.inputType =='radio') {
10330                 if (typeof(ret[f.getName()]) == 'undefined') {
10331                     ret[f.getName()] = ''; // empty..
10332                 }
10333
10334                 if (!f.el.dom.checked) {
10335                     return;
10336
10337                 }
10338                 v = f.el.dom.value;
10339
10340             }
10341             
10342             if(f.xtype == 'MoneyField'){
10343                 ret[f.currencyName] = f.getCurrency();
10344             }
10345
10346             // not sure if this supported any more..
10347             if ((typeof(v) == 'object') && f.getRawValue) {
10348                 v = f.getRawValue() ; // dates..
10349             }
10350             // combo boxes where name != hiddenName...
10351             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10352                 ret[f.name] = f.getRawValue();
10353             }
10354             ret[f.getName()] = v;
10355         });
10356
10357         return ret;
10358     },
10359
10360     /**
10361      * Clears all invalid messages in this form.
10362      * @return {BasicForm} this
10363      */
10364     clearInvalid : function(){
10365         var items = this.getItems();
10366
10367         items.each(function(f){
10368            f.clearInvalid();
10369         });
10370
10371         return this;
10372     },
10373
10374     /**
10375      * Resets this form.
10376      * @return {BasicForm} this
10377      */
10378     reset : function(){
10379         var items = this.getItems();
10380         items.each(function(f){
10381             f.reset();
10382         });
10383
10384         Roo.each(this.childForms || [], function (f) {
10385             f.reset();
10386         });
10387
10388
10389         return this;
10390     },
10391     
10392     getItems : function()
10393     {
10394         var r=new Roo.util.MixedCollection(false, function(o){
10395             return o.id || (o.id = Roo.id());
10396         });
10397         var iter = function(el) {
10398             if (el.inputEl) {
10399                 r.add(el);
10400             }
10401             if (!el.items) {
10402                 return;
10403             }
10404             Roo.each(el.items,function(e) {
10405                 iter(e);
10406             });
10407         };
10408
10409         iter(this);
10410         return r;
10411     },
10412     
10413     hideFields : function(items)
10414     {
10415         Roo.each(items, function(i){
10416             
10417             var f = this.findField(i);
10418             
10419             if(!f){
10420                 return;
10421             }
10422             
10423             f.hide();
10424             
10425         }, this);
10426     },
10427     
10428     showFields : function(items)
10429     {
10430         Roo.each(items, function(i){
10431             
10432             var f = this.findField(i);
10433             
10434             if(!f){
10435                 return;
10436             }
10437             
10438             f.show();
10439             
10440         }, this);
10441     }
10442
10443 });
10444
10445 Roo.apply(Roo.bootstrap.Form, {
10446     
10447     popover : {
10448         
10449         padding : 5,
10450         
10451         isApplied : false,
10452         
10453         isMasked : false,
10454         
10455         form : false,
10456         
10457         target : false,
10458         
10459         toolTip : false,
10460         
10461         intervalID : false,
10462         
10463         maskEl : false,
10464         
10465         apply : function()
10466         {
10467             if(this.isApplied){
10468                 return;
10469             }
10470             
10471             this.maskEl = {
10472                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10473                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10474                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10475                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10476             };
10477             
10478             this.maskEl.top.enableDisplayMode("block");
10479             this.maskEl.left.enableDisplayMode("block");
10480             this.maskEl.bottom.enableDisplayMode("block");
10481             this.maskEl.right.enableDisplayMode("block");
10482             
10483             this.toolTip = new Roo.bootstrap.Tooltip({
10484                 cls : 'roo-form-error-popover',
10485                 alignment : {
10486                     'left' : ['r-l', [-2,0], 'right'],
10487                     'right' : ['l-r', [2,0], 'left'],
10488                     'bottom' : ['tl-bl', [0,2], 'top'],
10489                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10490                 }
10491             });
10492             
10493             this.toolTip.render(Roo.get(document.body));
10494
10495             this.toolTip.el.enableDisplayMode("block");
10496             
10497             Roo.get(document.body).on('click', function(){
10498                 this.unmask();
10499             }, this);
10500             
10501             Roo.get(document.body).on('touchstart', function(){
10502                 this.unmask();
10503             }, this);
10504             
10505             this.isApplied = true
10506         },
10507         
10508         mask : function(form, target)
10509         {
10510             this.form = form;
10511             
10512             this.target = target;
10513             
10514             if(!this.form.errorMask || !target.el){
10515                 return;
10516             }
10517             
10518             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10519             
10520             Roo.log(scrollable);
10521             
10522             var ot = this.target.el.calcOffsetsTo(scrollable);
10523             
10524             var scrollTo = ot[1] - this.form.maskOffset;
10525             
10526             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10527             
10528             scrollable.scrollTo('top', scrollTo);
10529             
10530             var box = this.target.el.getBox();
10531             Roo.log(box);
10532             var zIndex = Roo.bootstrap.Modal.zIndex++;
10533
10534             
10535             this.maskEl.top.setStyle('position', 'absolute');
10536             this.maskEl.top.setStyle('z-index', zIndex);
10537             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10538             this.maskEl.top.setLeft(0);
10539             this.maskEl.top.setTop(0);
10540             this.maskEl.top.show();
10541             
10542             this.maskEl.left.setStyle('position', 'absolute');
10543             this.maskEl.left.setStyle('z-index', zIndex);
10544             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10545             this.maskEl.left.setLeft(0);
10546             this.maskEl.left.setTop(box.y - this.padding);
10547             this.maskEl.left.show();
10548
10549             this.maskEl.bottom.setStyle('position', 'absolute');
10550             this.maskEl.bottom.setStyle('z-index', zIndex);
10551             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10552             this.maskEl.bottom.setLeft(0);
10553             this.maskEl.bottom.setTop(box.bottom + this.padding);
10554             this.maskEl.bottom.show();
10555
10556             this.maskEl.right.setStyle('position', 'absolute');
10557             this.maskEl.right.setStyle('z-index', zIndex);
10558             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10559             this.maskEl.right.setLeft(box.right + this.padding);
10560             this.maskEl.right.setTop(box.y - this.padding);
10561             this.maskEl.right.show();
10562
10563             this.toolTip.bindEl = this.target.el;
10564
10565             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10566
10567             var tip = this.target.blankText;
10568
10569             if(this.target.getValue() !== '' ) {
10570                 
10571                 if (this.target.invalidText.length) {
10572                     tip = this.target.invalidText;
10573                 } else if (this.target.regexText.length){
10574                     tip = this.target.regexText;
10575                 }
10576             }
10577
10578             this.toolTip.show(tip);
10579
10580             this.intervalID = window.setInterval(function() {
10581                 Roo.bootstrap.Form.popover.unmask();
10582             }, 10000);
10583
10584             window.onwheel = function(){ return false;};
10585             
10586             (function(){ this.isMasked = true; }).defer(500, this);
10587             
10588         },
10589         
10590         unmask : function()
10591         {
10592             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10593                 return;
10594             }
10595             
10596             this.maskEl.top.setStyle('position', 'absolute');
10597             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10598             this.maskEl.top.hide();
10599
10600             this.maskEl.left.setStyle('position', 'absolute');
10601             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10602             this.maskEl.left.hide();
10603
10604             this.maskEl.bottom.setStyle('position', 'absolute');
10605             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10606             this.maskEl.bottom.hide();
10607
10608             this.maskEl.right.setStyle('position', 'absolute');
10609             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10610             this.maskEl.right.hide();
10611             
10612             this.toolTip.hide();
10613             
10614             this.toolTip.el.hide();
10615             
10616             window.onwheel = function(){ return true;};
10617             
10618             if(this.intervalID){
10619                 window.clearInterval(this.intervalID);
10620                 this.intervalID = false;
10621             }
10622             
10623             this.isMasked = false;
10624             
10625         }
10626         
10627     }
10628     
10629 });
10630
10631 /*
10632  * Based on:
10633  * Ext JS Library 1.1.1
10634  * Copyright(c) 2006-2007, Ext JS, LLC.
10635  *
10636  * Originally Released Under LGPL - original licence link has changed is not relivant.
10637  *
10638  * Fork - LGPL
10639  * <script type="text/javascript">
10640  */
10641 /**
10642  * @class Roo.form.VTypes
10643  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10644  * @singleton
10645  */
10646 Roo.form.VTypes = function(){
10647     // closure these in so they are only created once.
10648     var alpha = /^[a-zA-Z_]+$/;
10649     var alphanum = /^[a-zA-Z0-9_]+$/;
10650     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10651     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10652
10653     // All these messages and functions are configurable
10654     return {
10655         /**
10656          * The function used to validate email addresses
10657          * @param {String} value The email address
10658          */
10659         'email' : function(v){
10660             return email.test(v);
10661         },
10662         /**
10663          * The error text to display when the email validation function returns false
10664          * @type String
10665          */
10666         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10667         /**
10668          * The keystroke filter mask to be applied on email input
10669          * @type RegExp
10670          */
10671         'emailMask' : /[a-z0-9_\.\-@]/i,
10672
10673         /**
10674          * The function used to validate URLs
10675          * @param {String} value The URL
10676          */
10677         'url' : function(v){
10678             return url.test(v);
10679         },
10680         /**
10681          * The error text to display when the url validation function returns false
10682          * @type String
10683          */
10684         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10685         
10686         /**
10687          * The function used to validate alpha values
10688          * @param {String} value The value
10689          */
10690         'alpha' : function(v){
10691             return alpha.test(v);
10692         },
10693         /**
10694          * The error text to display when the alpha validation function returns false
10695          * @type String
10696          */
10697         'alphaText' : 'This field should only contain letters and _',
10698         /**
10699          * The keystroke filter mask to be applied on alpha input
10700          * @type RegExp
10701          */
10702         'alphaMask' : /[a-z_]/i,
10703
10704         /**
10705          * The function used to validate alphanumeric values
10706          * @param {String} value The value
10707          */
10708         'alphanum' : function(v){
10709             return alphanum.test(v);
10710         },
10711         /**
10712          * The error text to display when the alphanumeric validation function returns false
10713          * @type String
10714          */
10715         'alphanumText' : 'This field should only contain letters, numbers and _',
10716         /**
10717          * The keystroke filter mask to be applied on alphanumeric input
10718          * @type RegExp
10719          */
10720         'alphanumMask' : /[a-z0-9_]/i
10721     };
10722 }();/*
10723  * - LGPL
10724  *
10725  * Input
10726  * 
10727  */
10728
10729 /**
10730  * @class Roo.bootstrap.Input
10731  * @extends Roo.bootstrap.Component
10732  * Bootstrap Input class
10733  * @cfg {Boolean} disabled is it disabled
10734  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10735  * @cfg {String} name name of the input
10736  * @cfg {string} fieldLabel - the label associated
10737  * @cfg {string} placeholder - placeholder to put in text.
10738  * @cfg {string}  before - input group add on before
10739  * @cfg {string} after - input group add on after
10740  * @cfg {string} size - (lg|sm) or leave empty..
10741  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10742  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10743  * @cfg {Number} md colspan out of 12 for computer-sized screens
10744  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10745  * @cfg {string} value default value of the input
10746  * @cfg {Number} labelWidth set the width of label 
10747  * @cfg {Number} labellg set the width of label (1-12)
10748  * @cfg {Number} labelmd set the width of label (1-12)
10749  * @cfg {Number} labelsm set the width of label (1-12)
10750  * @cfg {Number} labelxs set the width of label (1-12)
10751  * @cfg {String} labelAlign (top|left)
10752  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10753  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10754  * @cfg {String} indicatorpos (left|right) default left
10755  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10756  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10757  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10758
10759  * @cfg {String} align (left|center|right) Default left
10760  * @cfg {Boolean} forceFeedback (true|false) Default false
10761  * 
10762  * @constructor
10763  * Create a new Input
10764  * @param {Object} config The config object
10765  */
10766
10767 Roo.bootstrap.Input = function(config){
10768     
10769     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10770     
10771     this.addEvents({
10772         /**
10773          * @event focus
10774          * Fires when this field receives input focus.
10775          * @param {Roo.form.Field} this
10776          */
10777         focus : true,
10778         /**
10779          * @event blur
10780          * Fires when this field loses input focus.
10781          * @param {Roo.form.Field} this
10782          */
10783         blur : true,
10784         /**
10785          * @event specialkey
10786          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10787          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10788          * @param {Roo.form.Field} this
10789          * @param {Roo.EventObject} e The event object
10790          */
10791         specialkey : true,
10792         /**
10793          * @event change
10794          * Fires just before the field blurs if the field value has changed.
10795          * @param {Roo.form.Field} this
10796          * @param {Mixed} newValue The new value
10797          * @param {Mixed} oldValue The original value
10798          */
10799         change : true,
10800         /**
10801          * @event invalid
10802          * Fires after the field has been marked as invalid.
10803          * @param {Roo.form.Field} this
10804          * @param {String} msg The validation message
10805          */
10806         invalid : true,
10807         /**
10808          * @event valid
10809          * Fires after the field has been validated with no errors.
10810          * @param {Roo.form.Field} this
10811          */
10812         valid : true,
10813          /**
10814          * @event keyup
10815          * Fires after the key up
10816          * @param {Roo.form.Field} this
10817          * @param {Roo.EventObject}  e The event Object
10818          */
10819         keyup : true,
10820         /**
10821          * @event paste
10822          * Fires after the user pastes into input
10823          * @param {Roo.form.Field} this
10824          * @param {Roo.EventObject}  e The event Object
10825          */
10826         paste : true
10827     });
10828 };
10829
10830 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10831      /**
10832      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10833       automatic validation (defaults to "keyup").
10834      */
10835     validationEvent : "keyup",
10836      /**
10837      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10838      */
10839     validateOnBlur : true,
10840     /**
10841      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10842      */
10843     validationDelay : 250,
10844      /**
10845      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10846      */
10847     focusClass : "x-form-focus",  // not needed???
10848     
10849        
10850     /**
10851      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10852      */
10853     invalidClass : "has-warning",
10854     
10855     /**
10856      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10857      */
10858     validClass : "has-success",
10859     
10860     /**
10861      * @cfg {Boolean} hasFeedback (true|false) default true
10862      */
10863     hasFeedback : true,
10864     
10865     /**
10866      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10867      */
10868     invalidFeedbackClass : "glyphicon-warning-sign",
10869     
10870     /**
10871      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10872      */
10873     validFeedbackClass : "glyphicon-ok",
10874     
10875     /**
10876      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10877      */
10878     selectOnFocus : false,
10879     
10880      /**
10881      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10882      */
10883     maskRe : null,
10884        /**
10885      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10886      */
10887     vtype : null,
10888     
10889       /**
10890      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10891      */
10892     disableKeyFilter : false,
10893     
10894        /**
10895      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10896      */
10897     disabled : false,
10898      /**
10899      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10900      */
10901     allowBlank : true,
10902     /**
10903      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10904      */
10905     blankText : "Please complete this mandatory field",
10906     
10907      /**
10908      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10909      */
10910     minLength : 0,
10911     /**
10912      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10913      */
10914     maxLength : Number.MAX_VALUE,
10915     /**
10916      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10917      */
10918     minLengthText : "The minimum length for this field is {0}",
10919     /**
10920      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10921      */
10922     maxLengthText : "The maximum length for this field is {0}",
10923   
10924     
10925     /**
10926      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10927      * If available, this function will be called only after the basic validators all return true, and will be passed the
10928      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10929      */
10930     validator : null,
10931     /**
10932      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10933      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10934      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10935      */
10936     regex : null,
10937     /**
10938      * @cfg {String} regexText -- Depricated - use Invalid Text
10939      */
10940     regexText : "",
10941     
10942     /**
10943      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10944      */
10945     invalidText : "",
10946     
10947     
10948     
10949     autocomplete: false,
10950     
10951     
10952     fieldLabel : '',
10953     inputType : 'text',
10954     
10955     name : false,
10956     placeholder: false,
10957     before : false,
10958     after : false,
10959     size : false,
10960     hasFocus : false,
10961     preventMark: false,
10962     isFormField : true,
10963     value : '',
10964     labelWidth : 2,
10965     labelAlign : false,
10966     readOnly : false,
10967     align : false,
10968     formatedValue : false,
10969     forceFeedback : false,
10970     
10971     indicatorpos : 'left',
10972     
10973     labellg : 0,
10974     labelmd : 0,
10975     labelsm : 0,
10976     labelxs : 0,
10977     
10978     capture : '',
10979     accept : '',
10980     
10981     parentLabelAlign : function()
10982     {
10983         var parent = this;
10984         while (parent.parent()) {
10985             parent = parent.parent();
10986             if (typeof(parent.labelAlign) !='undefined') {
10987                 return parent.labelAlign;
10988             }
10989         }
10990         return 'left';
10991         
10992     },
10993     
10994     getAutoCreate : function()
10995     {
10996         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10997         
10998         var id = Roo.id();
10999         
11000         var cfg = {};
11001         
11002         if(this.inputType != 'hidden'){
11003             cfg.cls = 'form-group' //input-group
11004         }
11005         
11006         var input =  {
11007             tag: 'input',
11008             id : id,
11009             type : this.inputType,
11010             value : this.value,
11011             cls : 'form-control',
11012             placeholder : this.placeholder || '',
11013             autocomplete : this.autocomplete || 'new-password'
11014         };
11015         if (this.inputType == 'file') {
11016             input.style = 'overflow:hidden'; // why not in CSS?
11017         }
11018         
11019         if(this.capture.length){
11020             input.capture = this.capture;
11021         }
11022         
11023         if(this.accept.length){
11024             input.accept = this.accept + "/*";
11025         }
11026         
11027         if(this.align){
11028             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
11029         }
11030         
11031         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11032             input.maxLength = this.maxLength;
11033         }
11034         
11035         if (this.disabled) {
11036             input.disabled=true;
11037         }
11038         
11039         if (this.readOnly) {
11040             input.readonly=true;
11041         }
11042         
11043         if (this.name) {
11044             input.name = this.name;
11045         }
11046         
11047         if (this.size) {
11048             input.cls += ' input-' + this.size;
11049         }
11050         
11051         var settings=this;
11052         ['xs','sm','md','lg'].map(function(size){
11053             if (settings[size]) {
11054                 cfg.cls += ' col-' + size + '-' + settings[size];
11055             }
11056         });
11057         
11058         var inputblock = input;
11059         
11060         var feedback = {
11061             tag: 'span',
11062             cls: 'glyphicon form-control-feedback'
11063         };
11064             
11065         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11066             
11067             inputblock = {
11068                 cls : 'has-feedback',
11069                 cn :  [
11070                     input,
11071                     feedback
11072                 ] 
11073             };  
11074         }
11075         
11076         if (this.before || this.after) {
11077             
11078             inputblock = {
11079                 cls : 'input-group',
11080                 cn :  [] 
11081             };
11082             
11083             if (this.before && typeof(this.before) == 'string') {
11084                 
11085                 inputblock.cn.push({
11086                     tag :'span',
11087                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
11088                     html : this.before
11089                 });
11090             }
11091             if (this.before && typeof(this.before) == 'object') {
11092                 this.before = Roo.factory(this.before);
11093                 
11094                 inputblock.cn.push({
11095                     tag :'span',
11096                     cls : 'roo-input-before input-group-prepend   input-group-' +
11097                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11098                 });
11099             }
11100             
11101             inputblock.cn.push(input);
11102             
11103             if (this.after && typeof(this.after) == 'string') {
11104                 inputblock.cn.push({
11105                     tag :'span',
11106                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
11107                     html : this.after
11108                 });
11109             }
11110             if (this.after && typeof(this.after) == 'object') {
11111                 this.after = Roo.factory(this.after);
11112                 
11113                 inputblock.cn.push({
11114                     tag :'span',
11115                     cls : 'roo-input-after input-group-append  input-group-' +
11116                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
11117                 });
11118             }
11119             
11120             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11121                 inputblock.cls += ' has-feedback';
11122                 inputblock.cn.push(feedback);
11123             }
11124         };
11125         var indicator = {
11126             tag : 'i',
11127             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
11128             tooltip : 'This field is required'
11129         };
11130         if (this.allowBlank ) {
11131             indicator.style = this.allowBlank ? ' display:none' : '';
11132         }
11133         if (align ==='left' && this.fieldLabel.length) {
11134             
11135             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
11136             
11137             cfg.cn = [
11138                 indicator,
11139                 {
11140                     tag: 'label',
11141                     'for' :  id,
11142                     cls : 'control-label col-form-label',
11143                     html : this.fieldLabel
11144
11145                 },
11146                 {
11147                     cls : "", 
11148                     cn: [
11149                         inputblock
11150                     ]
11151                 }
11152             ];
11153             
11154             var labelCfg = cfg.cn[1];
11155             var contentCfg = cfg.cn[2];
11156             
11157             if(this.indicatorpos == 'right'){
11158                 cfg.cn = [
11159                     {
11160                         tag: 'label',
11161                         'for' :  id,
11162                         cls : 'control-label col-form-label',
11163                         cn : [
11164                             {
11165                                 tag : 'span',
11166                                 html : this.fieldLabel
11167                             },
11168                             indicator
11169                         ]
11170                     },
11171                     {
11172                         cls : "",
11173                         cn: [
11174                             inputblock
11175                         ]
11176                     }
11177
11178                 ];
11179                 
11180                 labelCfg = cfg.cn[0];
11181                 contentCfg = cfg.cn[1];
11182             
11183             }
11184             
11185             if(this.labelWidth > 12){
11186                 labelCfg.style = "width: " + this.labelWidth + 'px';
11187             }
11188             
11189             if(this.labelWidth < 13 && this.labelmd == 0){
11190                 this.labellg = this.labellg > 0 ? this.labellg : this.labelWidth;
11191             }
11192             
11193             if(this.labellg > 0){
11194                 labelCfg.cls += ' col-lg-' + this.labellg;
11195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
11196             }
11197             
11198             if(this.labelmd > 0){
11199                 labelCfg.cls += ' col-md-' + this.labelmd;
11200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
11201             }
11202             
11203             if(this.labelsm > 0){
11204                 labelCfg.cls += ' col-sm-' + this.labelsm;
11205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
11206             }
11207             
11208             if(this.labelxs > 0){
11209                 labelCfg.cls += ' col-xs-' + this.labelxs;
11210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
11211             }
11212             
11213             
11214         } else if ( this.fieldLabel.length) {
11215                 
11216             
11217             
11218             cfg.cn = [
11219                 {
11220                     tag : 'i',
11221                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
11222                     tooltip : 'This field is required',
11223                     style : this.allowBlank ? ' display:none' : '' 
11224                 },
11225                 {
11226                     tag: 'label',
11227                    //cls : 'input-group-addon',
11228                     html : this.fieldLabel
11229
11230                 },
11231
11232                inputblock
11233
11234            ];
11235            
11236            if(this.indicatorpos == 'right'){
11237        
11238                 cfg.cn = [
11239                     {
11240                         tag: 'label',
11241                        //cls : 'input-group-addon',
11242                         html : this.fieldLabel
11243
11244                     },
11245                     {
11246                         tag : 'i',
11247                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
11248                         tooltip : 'This field is required',
11249                         style : this.allowBlank ? ' display:none' : '' 
11250                     },
11251
11252                    inputblock
11253
11254                ];
11255
11256             }
11257
11258         } else {
11259             
11260             cfg.cn = [
11261
11262                     inputblock
11263
11264             ];
11265                 
11266                 
11267         };
11268         
11269         if (this.parentType === 'Navbar' &&  this.parent().bar) {
11270            cfg.cls += ' navbar-form';
11271         }
11272         
11273         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
11274             // on BS4 we do this only if not form 
11275             cfg.cls += ' navbar-form';
11276             cfg.tag = 'li';
11277         }
11278         
11279         return cfg;
11280         
11281     },
11282     /**
11283      * return the real input element.
11284      */
11285     inputEl: function ()
11286     {
11287         return this.el.select('input.form-control',true).first();
11288     },
11289     
11290     tooltipEl : function()
11291     {
11292         return this.inputEl();
11293     },
11294     
11295     indicatorEl : function()
11296     {
11297         if (Roo.bootstrap.version == 4) {
11298             return false; // not enabled in v4 yet.
11299         }
11300         
11301         var indicator = this.el.select('i.roo-required-indicator',true).first();
11302         
11303         if(!indicator){
11304             return false;
11305         }
11306         
11307         return indicator;
11308         
11309     },
11310     
11311     setDisabled : function(v)
11312     {
11313         var i  = this.inputEl().dom;
11314         if (!v) {
11315             i.removeAttribute('disabled');
11316             return;
11317             
11318         }
11319         i.setAttribute('disabled','true');
11320     },
11321     initEvents : function()
11322     {
11323           
11324         this.inputEl().on("keydown" , this.fireKey,  this);
11325         this.inputEl().on("focus", this.onFocus,  this);
11326         this.inputEl().on("blur", this.onBlur,  this);
11327         
11328         this.inputEl().relayEvent('keyup', this);
11329         this.inputEl().relayEvent('paste', this);
11330         
11331         this.indicator = this.indicatorEl();
11332         
11333         if(this.indicator){
11334             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11335         }
11336  
11337         // reference to original value for reset
11338         this.originalValue = this.getValue();
11339         //Roo.form.TextField.superclass.initEvents.call(this);
11340         if(this.validationEvent == 'keyup'){
11341             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11342             this.inputEl().on('keyup', this.filterValidation, this);
11343         }
11344         else if(this.validationEvent !== false){
11345             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11346         }
11347         
11348         if(this.selectOnFocus){
11349             this.on("focus", this.preFocus, this);
11350             
11351         }
11352         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11353             this.inputEl().on("keypress", this.filterKeys, this);
11354         } else {
11355             this.inputEl().relayEvent('keypress', this);
11356         }
11357        /* if(this.grow){
11358             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11359             this.el.on("click", this.autoSize,  this);
11360         }
11361         */
11362         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11363             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11364         }
11365         
11366         if (typeof(this.before) == 'object') {
11367             this.before.render(this.el.select('.roo-input-before',true).first());
11368         }
11369         if (typeof(this.after) == 'object') {
11370             this.after.render(this.el.select('.roo-input-after',true).first());
11371         }
11372         
11373         this.inputEl().on('change', this.onChange, this);
11374         
11375     },
11376     filterValidation : function(e){
11377         if(!e.isNavKeyPress()){
11378             this.validationTask.delay(this.validationDelay);
11379         }
11380     },
11381      /**
11382      * Validates the field value
11383      * @return {Boolean} True if the value is valid, else false
11384      */
11385     validate : function(){
11386         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11387         if(this.disabled || this.validateValue(this.getRawValue())){
11388             this.markValid();
11389             return true;
11390         }
11391         
11392         this.markInvalid();
11393         return false;
11394     },
11395     
11396     
11397     /**
11398      * Validates a value according to the field's validation rules and marks the field as invalid
11399      * if the validation fails
11400      * @param {Mixed} value The value to validate
11401      * @return {Boolean} True if the value is valid, else false
11402      */
11403     validateValue : function(value)
11404     {
11405         if(this.getVisibilityEl().hasClass('hidden')){
11406             return true;
11407         }
11408         
11409         if(value.length < 1)  { // if it's blank
11410             if(this.allowBlank){
11411                 return true;
11412             }
11413             return false;
11414         }
11415         
11416         if(value.length < this.minLength){
11417             return false;
11418         }
11419         if(value.length > this.maxLength){
11420             return false;
11421         }
11422         if(this.vtype){
11423             var vt = Roo.form.VTypes;
11424             if(!vt[this.vtype](value, this)){
11425                 return false;
11426             }
11427         }
11428         if(typeof this.validator == "function"){
11429             var msg = this.validator(value);
11430             if(msg !== true){
11431                 return false;
11432             }
11433             if (typeof(msg) == 'string') {
11434                 this.invalidText = msg;
11435             }
11436         }
11437         
11438         if(this.regex && !this.regex.test(value)){
11439             return false;
11440         }
11441         
11442         return true;
11443     },
11444     
11445      // private
11446     fireKey : function(e){
11447         //Roo.log('field ' + e.getKey());
11448         if(e.isNavKeyPress()){
11449             this.fireEvent("specialkey", this, e);
11450         }
11451     },
11452     focus : function (selectText){
11453         if(this.rendered){
11454             this.inputEl().focus();
11455             if(selectText === true){
11456                 this.inputEl().dom.select();
11457             }
11458         }
11459         return this;
11460     } ,
11461     
11462     onFocus : function(){
11463         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11464            // this.el.addClass(this.focusClass);
11465         }
11466         if(!this.hasFocus){
11467             this.hasFocus = true;
11468             this.startValue = this.getValue();
11469             this.fireEvent("focus", this);
11470         }
11471     },
11472     
11473     beforeBlur : Roo.emptyFn,
11474
11475     
11476     // private
11477     onBlur : function(){
11478         this.beforeBlur();
11479         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11480             //this.el.removeClass(this.focusClass);
11481         }
11482         this.hasFocus = false;
11483         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11484             this.validate();
11485         }
11486         var v = this.getValue();
11487         if(String(v) !== String(this.startValue)){
11488             this.fireEvent('change', this, v, this.startValue);
11489         }
11490         this.fireEvent("blur", this);
11491     },
11492     
11493     onChange : function(e)
11494     {
11495         var v = this.getValue();
11496         if(String(v) !== String(this.startValue)){
11497             this.fireEvent('change', this, v, this.startValue);
11498         }
11499         
11500     },
11501     
11502     /**
11503      * Resets the current field value to the originally loaded value and clears any validation messages
11504      */
11505     reset : function(){
11506         this.setValue(this.originalValue);
11507         this.validate();
11508     },
11509      /**
11510      * Returns the name of the field
11511      * @return {Mixed} name The name field
11512      */
11513     getName: function(){
11514         return this.name;
11515     },
11516      /**
11517      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11518      * @return {Mixed} value The field value
11519      */
11520     getValue : function(){
11521         
11522         var v = this.inputEl().getValue();
11523         
11524         return v;
11525     },
11526     /**
11527      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11528      * @return {Mixed} value The field value
11529      */
11530     getRawValue : function(){
11531         var v = this.inputEl().getValue();
11532         
11533         return v;
11534     },
11535     
11536     /**
11537      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11538      * @param {Mixed} value The value to set
11539      */
11540     setRawValue : function(v){
11541         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11542     },
11543     
11544     selectText : function(start, end){
11545         var v = this.getRawValue();
11546         if(v.length > 0){
11547             start = start === undefined ? 0 : start;
11548             end = end === undefined ? v.length : end;
11549             var d = this.inputEl().dom;
11550             if(d.setSelectionRange){
11551                 d.setSelectionRange(start, end);
11552             }else if(d.createTextRange){
11553                 var range = d.createTextRange();
11554                 range.moveStart("character", start);
11555                 range.moveEnd("character", v.length-end);
11556                 range.select();
11557             }
11558         }
11559     },
11560     
11561     /**
11562      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11563      * @param {Mixed} value The value to set
11564      */
11565     setValue : function(v){
11566         this.value = v;
11567         if(this.rendered){
11568             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11569             this.validate();
11570         }
11571     },
11572     
11573     /*
11574     processValue : function(value){
11575         if(this.stripCharsRe){
11576             var newValue = value.replace(this.stripCharsRe, '');
11577             if(newValue !== value){
11578                 this.setRawValue(newValue);
11579                 return newValue;
11580             }
11581         }
11582         return value;
11583     },
11584   */
11585     preFocus : function(){
11586         
11587         if(this.selectOnFocus){
11588             this.inputEl().dom.select();
11589         }
11590     },
11591     filterKeys : function(e){
11592         var k = e.getKey();
11593         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11594             return;
11595         }
11596         var c = e.getCharCode(), cc = String.fromCharCode(c);
11597         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11598             return;
11599         }
11600         if(!this.maskRe.test(cc)){
11601             e.stopEvent();
11602         }
11603     },
11604      /**
11605      * Clear any invalid styles/messages for this field
11606      */
11607     clearInvalid : function(){
11608         
11609         if(!this.el || this.preventMark){ // not rendered
11610             return;
11611         }
11612         
11613         
11614         this.el.removeClass([this.invalidClass, 'is-invalid']);
11615         
11616         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11617             
11618             var feedback = this.el.select('.form-control-feedback', true).first();
11619             
11620             if(feedback){
11621                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11622             }
11623             
11624         }
11625         
11626         if(this.indicator){
11627             this.indicator.removeClass('visible');
11628             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11629         }
11630         
11631         this.fireEvent('valid', this);
11632     },
11633     
11634      /**
11635      * Mark this field as valid
11636      */
11637     markValid : function()
11638     {
11639         if(!this.el  || this.preventMark){ // not rendered...
11640             return;
11641         }
11642         
11643         this.el.removeClass([this.invalidClass, this.validClass]);
11644         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11645
11646         var feedback = this.el.select('.form-control-feedback', true).first();
11647             
11648         if(feedback){
11649             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11650         }
11651         
11652         if(this.indicator){
11653             this.indicator.removeClass('visible');
11654             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11655         }
11656         
11657         if(this.disabled){
11658             return;
11659         }
11660         
11661            
11662         if(this.allowBlank && !this.getRawValue().length){
11663             return;
11664         }
11665         if (Roo.bootstrap.version == 3) {
11666             this.el.addClass(this.validClass);
11667         } else {
11668             this.inputEl().addClass('is-valid');
11669         }
11670
11671         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11672             
11673             var feedback = this.el.select('.form-control-feedback', true).first();
11674             
11675             if(feedback){
11676                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11677                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11678             }
11679             
11680         }
11681         
11682         this.fireEvent('valid', this);
11683     },
11684     
11685      /**
11686      * Mark this field as invalid
11687      * @param {String} msg The validation message
11688      */
11689     markInvalid : function(msg)
11690     {
11691         if(!this.el  || this.preventMark){ // not rendered
11692             return;
11693         }
11694         
11695         this.el.removeClass([this.invalidClass, this.validClass]);
11696         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11697         
11698         var feedback = this.el.select('.form-control-feedback', true).first();
11699             
11700         if(feedback){
11701             this.el.select('.form-control-feedback', true).first().removeClass(
11702                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11703         }
11704
11705         if(this.disabled){
11706             return;
11707         }
11708         
11709         if(this.allowBlank && !this.getRawValue().length){
11710             return;
11711         }
11712         
11713         if(this.indicator){
11714             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11715             this.indicator.addClass('visible');
11716         }
11717         if (Roo.bootstrap.version == 3) {
11718             this.el.addClass(this.invalidClass);
11719         } else {
11720             this.inputEl().addClass('is-invalid');
11721         }
11722         
11723         
11724         
11725         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11726             
11727             var feedback = this.el.select('.form-control-feedback', true).first();
11728             
11729             if(feedback){
11730                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11731                 
11732                 if(this.getValue().length || this.forceFeedback){
11733                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11734                 }
11735                 
11736             }
11737             
11738         }
11739         
11740         this.fireEvent('invalid', this, msg);
11741     },
11742     // private
11743     SafariOnKeyDown : function(event)
11744     {
11745         // this is a workaround for a password hang bug on chrome/ webkit.
11746         if (this.inputEl().dom.type != 'password') {
11747             return;
11748         }
11749         
11750         var isSelectAll = false;
11751         
11752         if(this.inputEl().dom.selectionEnd > 0){
11753             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11754         }
11755         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11756             event.preventDefault();
11757             this.setValue('');
11758             return;
11759         }
11760         
11761         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11762             
11763             event.preventDefault();
11764             // this is very hacky as keydown always get's upper case.
11765             //
11766             var cc = String.fromCharCode(event.getCharCode());
11767             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11768             
11769         }
11770     },
11771     adjustWidth : function(tag, w){
11772         tag = tag.toLowerCase();
11773         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11774             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11775                 if(tag == 'input'){
11776                     return w + 2;
11777                 }
11778                 if(tag == 'textarea'){
11779                     return w-2;
11780                 }
11781             }else if(Roo.isOpera){
11782                 if(tag == 'input'){
11783                     return w + 2;
11784                 }
11785                 if(tag == 'textarea'){
11786                     return w-2;
11787                 }
11788             }
11789         }
11790         return w;
11791     },
11792     
11793     setFieldLabel : function(v)
11794     {
11795         if(!this.rendered){
11796             return;
11797         }
11798         
11799         if(this.indicatorEl()){
11800             var ar = this.el.select('label > span',true);
11801             
11802             if (ar.elements.length) {
11803                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11804                 this.fieldLabel = v;
11805                 return;
11806             }
11807             
11808             var br = this.el.select('label',true);
11809             
11810             if(br.elements.length) {
11811                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11812                 this.fieldLabel = v;
11813                 return;
11814             }
11815             
11816             Roo.log('Cannot Found any of label > span || label in input');
11817             return;
11818         }
11819         
11820         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11821         this.fieldLabel = v;
11822         
11823         
11824     }
11825 });
11826
11827  
11828 /*
11829  * - LGPL
11830  *
11831  * Input
11832  * 
11833  */
11834
11835 /**
11836  * @class Roo.bootstrap.TextArea
11837  * @extends Roo.bootstrap.Input
11838  * Bootstrap TextArea class
11839  * @cfg {Number} cols Specifies the visible width of a text area
11840  * @cfg {Number} rows Specifies the visible number of lines in a text area
11841  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11842  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11843  * @cfg {string} html text
11844  * 
11845  * @constructor
11846  * Create a new TextArea
11847  * @param {Object} config The config object
11848  */
11849
11850 Roo.bootstrap.TextArea = function(config){
11851     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11852    
11853 };
11854
11855 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11856      
11857     cols : false,
11858     rows : 5,
11859     readOnly : false,
11860     warp : 'soft',
11861     resize : false,
11862     value: false,
11863     html: false,
11864     
11865     getAutoCreate : function(){
11866         
11867         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11868         
11869         var id = Roo.id();
11870         
11871         var cfg = {};
11872         
11873         if(this.inputType != 'hidden'){
11874             cfg.cls = 'form-group' //input-group
11875         }
11876         
11877         var input =  {
11878             tag: 'textarea',
11879             id : id,
11880             warp : this.warp,
11881             rows : this.rows,
11882             value : this.value || '',
11883             html: this.html || '',
11884             cls : 'form-control',
11885             placeholder : this.placeholder || '' 
11886             
11887         };
11888         
11889         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11890             input.maxLength = this.maxLength;
11891         }
11892         
11893         if(this.resize){
11894             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11895         }
11896         
11897         if(this.cols){
11898             input.cols = this.cols;
11899         }
11900         
11901         if (this.readOnly) {
11902             input.readonly = true;
11903         }
11904         
11905         if (this.name) {
11906             input.name = this.name;
11907         }
11908         
11909         if (this.size) {
11910             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11911         }
11912         
11913         var settings=this;
11914         ['xs','sm','md','lg'].map(function(size){
11915             if (settings[size]) {
11916                 cfg.cls += ' col-' + size + '-' + settings[size];
11917             }
11918         });
11919         
11920         var inputblock = input;
11921         
11922         if(this.hasFeedback && !this.allowBlank){
11923             
11924             var feedback = {
11925                 tag: 'span',
11926                 cls: 'glyphicon form-control-feedback'
11927             };
11928
11929             inputblock = {
11930                 cls : 'has-feedback',
11931                 cn :  [
11932                     input,
11933                     feedback
11934                 ] 
11935             };  
11936         }
11937         
11938         
11939         if (this.before || this.after) {
11940             
11941             inputblock = {
11942                 cls : 'input-group',
11943                 cn :  [] 
11944             };
11945             if (this.before) {
11946                 inputblock.cn.push({
11947                     tag :'span',
11948                     cls : 'input-group-addon',
11949                     html : this.before
11950                 });
11951             }
11952             
11953             inputblock.cn.push(input);
11954             
11955             if(this.hasFeedback && !this.allowBlank){
11956                 inputblock.cls += ' has-feedback';
11957                 inputblock.cn.push(feedback);
11958             }
11959             
11960             if (this.after) {
11961                 inputblock.cn.push({
11962                     tag :'span',
11963                     cls : 'input-group-addon',
11964                     html : this.after
11965                 });
11966             }
11967             
11968         }
11969         
11970         if (align ==='left' && this.fieldLabel.length) {
11971             cfg.cn = [
11972                 {
11973                     tag: 'label',
11974                     'for' :  id,
11975                     cls : 'control-label',
11976                     html : this.fieldLabel
11977                 },
11978                 {
11979                     cls : "",
11980                     cn: [
11981                         inputblock
11982                     ]
11983                 }
11984
11985             ];
11986             
11987             if(this.labelWidth > 12){
11988                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11989             }
11990
11991             if(this.labelWidth < 13 && this.labelmd == 0){
11992                 this.labelmd = this.labelWidth;
11993             }
11994
11995             if(this.labellg > 0){
11996                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11997                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11998             }
11999
12000             if(this.labelmd > 0){
12001                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
12002                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
12003             }
12004
12005             if(this.labelsm > 0){
12006                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
12007                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
12008             }
12009
12010             if(this.labelxs > 0){
12011                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
12012                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
12013             }
12014             
12015         } else if ( this.fieldLabel.length) {
12016             cfg.cn = [
12017
12018                {
12019                    tag: 'label',
12020                    //cls : 'input-group-addon',
12021                    html : this.fieldLabel
12022
12023                },
12024
12025                inputblock
12026
12027            ];
12028
12029         } else {
12030
12031             cfg.cn = [
12032
12033                 inputblock
12034
12035             ];
12036                 
12037         }
12038         
12039         if (this.disabled) {
12040             input.disabled=true;
12041         }
12042         
12043         return cfg;
12044         
12045     },
12046     /**
12047      * return the real textarea element.
12048      */
12049     inputEl: function ()
12050     {
12051         return this.el.select('textarea.form-control',true).first();
12052     },
12053     
12054     /**
12055      * Clear any invalid styles/messages for this field
12056      */
12057     clearInvalid : function()
12058     {
12059         
12060         if(!this.el || this.preventMark){ // not rendered
12061             return;
12062         }
12063         
12064         var label = this.el.select('label', true).first();
12065         var icon = this.el.select('i.fa-star', true).first();
12066         
12067         if(label && icon){
12068             icon.remove();
12069         }
12070         this.el.removeClass( this.validClass);
12071         this.inputEl().removeClass('is-invalid');
12072          
12073         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12074             
12075             var feedback = this.el.select('.form-control-feedback', true).first();
12076             
12077             if(feedback){
12078                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
12079             }
12080             
12081         }
12082         
12083         this.fireEvent('valid', this);
12084     },
12085     
12086      /**
12087      * Mark this field as valid
12088      */
12089     markValid : function()
12090     {
12091         if(!this.el  || this.preventMark){ // not rendered
12092             return;
12093         }
12094         
12095         this.el.removeClass([this.invalidClass, this.validClass]);
12096         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12097         
12098         var feedback = this.el.select('.form-control-feedback', true).first();
12099             
12100         if(feedback){
12101             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12102         }
12103
12104         if(this.disabled || this.allowBlank){
12105             return;
12106         }
12107         
12108         var label = this.el.select('label', true).first();
12109         var icon = this.el.select('i.fa-star', true).first();
12110         
12111         if(label && icon){
12112             icon.remove();
12113         }
12114         if (Roo.bootstrap.version == 3) {
12115             this.el.addClass(this.validClass);
12116         } else {
12117             this.inputEl().addClass('is-valid');
12118         }
12119         
12120         
12121         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
12122             
12123             var feedback = this.el.select('.form-control-feedback', true).first();
12124             
12125             if(feedback){
12126                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12127                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
12128             }
12129             
12130         }
12131         
12132         this.fireEvent('valid', this);
12133     },
12134     
12135      /**
12136      * Mark this field as invalid
12137      * @param {String} msg The validation message
12138      */
12139     markInvalid : function(msg)
12140     {
12141         if(!this.el  || this.preventMark){ // not rendered
12142             return;
12143         }
12144         
12145         this.el.removeClass([this.invalidClass, this.validClass]);
12146         this.inputEl().removeClass(['is-valid', 'is-invalid']);
12147         
12148         var feedback = this.el.select('.form-control-feedback', true).first();
12149             
12150         if(feedback){
12151             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12152         }
12153
12154         if(this.disabled || this.allowBlank){
12155             return;
12156         }
12157         
12158         var label = this.el.select('label', true).first();
12159         var icon = this.el.select('i.fa-star', true).first();
12160         
12161         if(!this.getValue().length && label && !icon){
12162             this.el.createChild({
12163                 tag : 'i',
12164                 cls : 'text-danger fa fa-lg fa-star',
12165                 tooltip : 'This field is required',
12166                 style : 'margin-right:5px;'
12167             }, label, true);
12168         }
12169         
12170         if (Roo.bootstrap.version == 3) {
12171             this.el.addClass(this.invalidClass);
12172         } else {
12173             this.inputEl().addClass('is-invalid');
12174         }
12175         
12176         // fixme ... this may be depricated need to test..
12177         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
12178             
12179             var feedback = this.el.select('.form-control-feedback', true).first();
12180             
12181             if(feedback){
12182                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
12183                 
12184                 if(this.getValue().length || this.forceFeedback){
12185                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
12186                 }
12187                 
12188             }
12189             
12190         }
12191         
12192         this.fireEvent('invalid', this, msg);
12193     }
12194 });
12195
12196  
12197 /*
12198  * - LGPL
12199  *
12200  * trigger field - base class for combo..
12201  * 
12202  */
12203  
12204 /**
12205  * @class Roo.bootstrap.TriggerField
12206  * @extends Roo.bootstrap.Input
12207  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
12208  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
12209  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
12210  * for which you can provide a custom implementation.  For example:
12211  * <pre><code>
12212 var trigger = new Roo.bootstrap.TriggerField();
12213 trigger.onTriggerClick = myTriggerFn;
12214 trigger.applyTo('my-field');
12215 </code></pre>
12216  *
12217  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
12218  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
12219  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
12220  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
12221  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
12222
12223  * @constructor
12224  * Create a new TriggerField.
12225  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
12226  * to the base TextField)
12227  */
12228 Roo.bootstrap.TriggerField = function(config){
12229     this.mimicing = false;
12230     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
12231 };
12232
12233 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
12234     /**
12235      * @cfg {String} triggerClass A CSS class to apply to the trigger
12236      */
12237      /**
12238      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
12239      */
12240     hideTrigger:false,
12241
12242     /**
12243      * @cfg {Boolean} removable (true|false) special filter default false
12244      */
12245     removable : false,
12246     
12247     /** @cfg {Boolean} grow @hide */
12248     /** @cfg {Number} growMin @hide */
12249     /** @cfg {Number} growMax @hide */
12250
12251     /**
12252      * @hide 
12253      * @method
12254      */
12255     autoSize: Roo.emptyFn,
12256     // private
12257     monitorTab : true,
12258     // private
12259     deferHeight : true,
12260
12261     
12262     actionMode : 'wrap',
12263     
12264     caret : false,
12265     
12266     
12267     getAutoCreate : function(){
12268        
12269         var align = this.labelAlign || this.parentLabelAlign();
12270         
12271         var id = Roo.id();
12272         
12273         var cfg = {
12274             cls: 'form-group' //input-group
12275         };
12276         
12277         
12278         var input =  {
12279             tag: 'input',
12280             id : id,
12281             type : this.inputType,
12282             cls : 'form-control',
12283             autocomplete: 'new-password',
12284             placeholder : this.placeholder || '' 
12285             
12286         };
12287         if (this.name) {
12288             input.name = this.name;
12289         }
12290         if (this.size) {
12291             input.cls += ' input-' + this.size;
12292         }
12293         
12294         if (this.disabled) {
12295             input.disabled=true;
12296         }
12297         
12298         var inputblock = input;
12299         
12300         if(this.hasFeedback && !this.allowBlank){
12301             
12302             var feedback = {
12303                 tag: 'span',
12304                 cls: 'glyphicon form-control-feedback'
12305             };
12306             
12307             if(this.removable && !this.editable  ){
12308                 inputblock = {
12309                     cls : 'has-feedback',
12310                     cn :  [
12311                         inputblock,
12312                         {
12313                             tag: 'button',
12314                             html : 'x',
12315                             cls : 'roo-combo-removable-btn close'
12316                         },
12317                         feedback
12318                     ] 
12319                 };
12320             } else {
12321                 inputblock = {
12322                     cls : 'has-feedback',
12323                     cn :  [
12324                         inputblock,
12325                         feedback
12326                     ] 
12327                 };
12328             }
12329
12330         } else {
12331             if(this.removable && !this.editable ){
12332                 inputblock = {
12333                     cls : 'roo-removable',
12334                     cn :  [
12335                         inputblock,
12336                         {
12337                             tag: 'button',
12338                             html : 'x',
12339                             cls : 'roo-combo-removable-btn close'
12340                         }
12341                     ] 
12342                 };
12343             }
12344         }
12345         
12346         if (this.before || this.after) {
12347             
12348             inputblock = {
12349                 cls : 'input-group',
12350                 cn :  [] 
12351             };
12352             if (this.before) {
12353                 inputblock.cn.push({
12354                     tag :'span',
12355                     cls : 'input-group-addon input-group-prepend input-group-text',
12356                     html : this.before
12357                 });
12358             }
12359             
12360             inputblock.cn.push(input);
12361             
12362             if(this.hasFeedback && !this.allowBlank){
12363                 inputblock.cls += ' has-feedback';
12364                 inputblock.cn.push(feedback);
12365             }
12366             
12367             if (this.after) {
12368                 inputblock.cn.push({
12369                     tag :'span',
12370                     cls : 'input-group-addon input-group-append input-group-text',
12371                     html : this.after
12372                 });
12373             }
12374             
12375         };
12376         
12377       
12378         
12379         var ibwrap = inputblock;
12380         
12381         if(this.multiple){
12382             ibwrap = {
12383                 tag: 'ul',
12384                 cls: 'roo-select2-choices',
12385                 cn:[
12386                     {
12387                         tag: 'li',
12388                         cls: 'roo-select2-search-field',
12389                         cn: [
12390
12391                             inputblock
12392                         ]
12393                     }
12394                 ]
12395             };
12396                 
12397         }
12398         
12399         var combobox = {
12400             cls: 'roo-select2-container input-group',
12401             cn: [
12402                  {
12403                     tag: 'input',
12404                     type : 'hidden',
12405                     cls: 'form-hidden-field'
12406                 },
12407                 ibwrap
12408             ]
12409         };
12410         
12411         if(!this.multiple && this.showToggleBtn){
12412             
12413             var caret = {
12414                         tag: 'span',
12415                         cls: 'caret'
12416              };
12417             if (this.caret != false) {
12418                 caret = {
12419                      tag: 'i',
12420                      cls: 'fa fa-' + this.caret
12421                 };
12422                 
12423             }
12424             
12425             combobox.cn.push({
12426                 tag :'span',
12427                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12428                 cn : [
12429                     Roo.bootstrap.version == 3 ? caret : '',
12430                     {
12431                         tag: 'span',
12432                         cls: 'combobox-clear',
12433                         cn  : [
12434                             {
12435                                 tag : 'i',
12436                                 cls: 'icon-remove'
12437                             }
12438                         ]
12439                     }
12440                 ]
12441
12442             })
12443         }
12444         
12445         if(this.multiple){
12446             combobox.cls += ' roo-select2-container-multi';
12447         }
12448          var indicator = {
12449             tag : 'i',
12450             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12451             tooltip : 'This field is required'
12452         };
12453         if (Roo.bootstrap.version == 4) {
12454             indicator = {
12455                 tag : 'i',
12456                 style : 'display:none'
12457             };
12458         }
12459         
12460         
12461         if (align ==='left' && this.fieldLabel.length) {
12462             
12463             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12464
12465             cfg.cn = [
12466                 indicator,
12467                 {
12468                     tag: 'label',
12469                     'for' :  id,
12470                     cls : 'control-label',
12471                     html : this.fieldLabel
12472
12473                 },
12474                 {
12475                     cls : "", 
12476                     cn: [
12477                         combobox
12478                     ]
12479                 }
12480
12481             ];
12482             
12483             var labelCfg = cfg.cn[1];
12484             var contentCfg = cfg.cn[2];
12485             
12486             if(this.indicatorpos == 'right'){
12487                 cfg.cn = [
12488                     {
12489                         tag: 'label',
12490                         'for' :  id,
12491                         cls : 'control-label',
12492                         cn : [
12493                             {
12494                                 tag : 'span',
12495                                 html : this.fieldLabel
12496                             },
12497                             indicator
12498                         ]
12499                     },
12500                     {
12501                         cls : "", 
12502                         cn: [
12503                             combobox
12504                         ]
12505                     }
12506
12507                 ];
12508                 
12509                 labelCfg = cfg.cn[0];
12510                 contentCfg = cfg.cn[1];
12511             }
12512             
12513             if(this.labelWidth > 12){
12514                 labelCfg.style = "width: " + this.labelWidth + 'px';
12515             }
12516             
12517             if(this.labelWidth < 13 && this.labelmd == 0){
12518                 this.labelmd = this.labelWidth;
12519             }
12520             
12521             if(this.labellg > 0){
12522                 labelCfg.cls += ' col-lg-' + this.labellg;
12523                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12524             }
12525             
12526             if(this.labelmd > 0){
12527                 labelCfg.cls += ' col-md-' + this.labelmd;
12528                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12529             }
12530             
12531             if(this.labelsm > 0){
12532                 labelCfg.cls += ' col-sm-' + this.labelsm;
12533                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12534             }
12535             
12536             if(this.labelxs > 0){
12537                 labelCfg.cls += ' col-xs-' + this.labelxs;
12538                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12539             }
12540             
12541         } else if ( this.fieldLabel.length) {
12542 //                Roo.log(" label");
12543             cfg.cn = [
12544                 indicator,
12545                {
12546                    tag: 'label',
12547                    //cls : 'input-group-addon',
12548                    html : this.fieldLabel
12549
12550                },
12551
12552                combobox
12553
12554             ];
12555             
12556             if(this.indicatorpos == 'right'){
12557                 
12558                 cfg.cn = [
12559                     {
12560                        tag: 'label',
12561                        cn : [
12562                            {
12563                                tag : 'span',
12564                                html : this.fieldLabel
12565                            },
12566                            indicator
12567                        ]
12568
12569                     },
12570                     combobox
12571
12572                 ];
12573
12574             }
12575
12576         } else {
12577             
12578 //                Roo.log(" no label && no align");
12579                 cfg = combobox
12580                      
12581                 
12582         }
12583         
12584         var settings=this;
12585         ['xs','sm','md','lg'].map(function(size){
12586             if (settings[size]) {
12587                 cfg.cls += ' col-' + size + '-' + settings[size];
12588             }
12589         });
12590         
12591         return cfg;
12592         
12593     },
12594     
12595     
12596     
12597     // private
12598     onResize : function(w, h){
12599 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12600 //        if(typeof w == 'number'){
12601 //            var x = w - this.trigger.getWidth();
12602 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12603 //            this.trigger.setStyle('left', x+'px');
12604 //        }
12605     },
12606
12607     // private
12608     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12609
12610     // private
12611     getResizeEl : function(){
12612         return this.inputEl();
12613     },
12614
12615     // private
12616     getPositionEl : function(){
12617         return this.inputEl();
12618     },
12619
12620     // private
12621     alignErrorIcon : function(){
12622         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12623     },
12624
12625     // private
12626     initEvents : function(){
12627         
12628         this.createList();
12629         
12630         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12631         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12632         if(!this.multiple && this.showToggleBtn){
12633             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12634             if(this.hideTrigger){
12635                 this.trigger.setDisplayed(false);
12636             }
12637             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12638         }
12639         
12640         if(this.multiple){
12641             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12642         }
12643         
12644         if(this.removable && !this.editable && !this.tickable){
12645             var close = this.closeTriggerEl();
12646             
12647             if(close){
12648                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12649                 close.on('click', this.removeBtnClick, this, close);
12650             }
12651         }
12652         
12653         //this.trigger.addClassOnOver('x-form-trigger-over');
12654         //this.trigger.addClassOnClick('x-form-trigger-click');
12655         
12656         //if(!this.width){
12657         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12658         //}
12659     },
12660     
12661     closeTriggerEl : function()
12662     {
12663         var close = this.el.select('.roo-combo-removable-btn', true).first();
12664         return close ? close : false;
12665     },
12666     
12667     removeBtnClick : function(e, h, el)
12668     {
12669         e.preventDefault();
12670         
12671         if(this.fireEvent("remove", this) !== false){
12672             this.reset();
12673             this.fireEvent("afterremove", this)
12674         }
12675     },
12676     
12677     createList : function()
12678     {
12679         this.list = Roo.get(document.body).createChild({
12680             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12681             cls: 'typeahead typeahead-long dropdown-menu shadow',
12682             style: 'display:none'
12683         });
12684         
12685         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12686         
12687     },
12688
12689     // private
12690     initTrigger : function(){
12691        
12692     },
12693
12694     // private
12695     onDestroy : function(){
12696         if(this.trigger){
12697             this.trigger.removeAllListeners();
12698           //  this.trigger.remove();
12699         }
12700         //if(this.wrap){
12701         //    this.wrap.remove();
12702         //}
12703         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12704     },
12705
12706     // private
12707     onFocus : function(){
12708         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12709         /*
12710         if(!this.mimicing){
12711             this.wrap.addClass('x-trigger-wrap-focus');
12712             this.mimicing = true;
12713             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12714             if(this.monitorTab){
12715                 this.el.on("keydown", this.checkTab, this);
12716             }
12717         }
12718         */
12719     },
12720
12721     // private
12722     checkTab : function(e){
12723         if(e.getKey() == e.TAB){
12724             this.triggerBlur();
12725         }
12726     },
12727
12728     // private
12729     onBlur : function(){
12730         // do nothing
12731     },
12732
12733     // private
12734     mimicBlur : function(e, t){
12735         /*
12736         if(!this.wrap.contains(t) && this.validateBlur()){
12737             this.triggerBlur();
12738         }
12739         */
12740     },
12741
12742     // private
12743     triggerBlur : function(){
12744         this.mimicing = false;
12745         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12746         if(this.monitorTab){
12747             this.el.un("keydown", this.checkTab, this);
12748         }
12749         //this.wrap.removeClass('x-trigger-wrap-focus');
12750         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12751     },
12752
12753     // private
12754     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12755     validateBlur : function(e, t){
12756         return true;
12757     },
12758
12759     // private
12760     onDisable : function(){
12761         this.inputEl().dom.disabled = true;
12762         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12763         //if(this.wrap){
12764         //    this.wrap.addClass('x-item-disabled');
12765         //}
12766     },
12767
12768     // private
12769     onEnable : function(){
12770         this.inputEl().dom.disabled = false;
12771         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12772         //if(this.wrap){
12773         //    this.el.removeClass('x-item-disabled');
12774         //}
12775     },
12776
12777     // private
12778     onShow : function(){
12779         var ae = this.getActionEl();
12780         
12781         if(ae){
12782             ae.dom.style.display = '';
12783             ae.dom.style.visibility = 'visible';
12784         }
12785     },
12786
12787     // private
12788     
12789     onHide : function(){
12790         var ae = this.getActionEl();
12791         ae.dom.style.display = 'none';
12792     },
12793
12794     /**
12795      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12796      * by an implementing function.
12797      * @method
12798      * @param {EventObject} e
12799      */
12800     onTriggerClick : Roo.emptyFn
12801 });
12802  
12803 /*
12804 * Licence: LGPL
12805 */
12806
12807 /**
12808  * @class Roo.bootstrap.CardUploader
12809  * @extends Roo.bootstrap.Button
12810  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12811  * @cfg {Number} errorTimeout default 3000
12812  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12813  * @cfg {Array}  html The button text.
12814
12815  *
12816  * @constructor
12817  * Create a new CardUploader
12818  * @param {Object} config The config object
12819  */
12820
12821 Roo.bootstrap.CardUploader = function(config){
12822     
12823  
12824     
12825     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12826     
12827     
12828     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12829         return r.data.id
12830      });
12831     
12832      this.addEvents({
12833          // raw events
12834         /**
12835          * @event preview
12836          * When a image is clicked on - and needs to display a slideshow or similar..
12837          * @param {Roo.bootstrap.Card} this
12838          * @param {Object} The image information data 
12839          *
12840          */
12841         'preview' : true,
12842          /**
12843          * @event download
12844          * When a the download link is clicked
12845          * @param {Roo.bootstrap.Card} this
12846          * @param {Object} The image information data  contains 
12847          */
12848         'download' : true
12849         
12850     });
12851 };
12852  
12853 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12854     
12855      
12856     errorTimeout : 3000,
12857      
12858     images : false,
12859    
12860     fileCollection : false,
12861     allowBlank : true,
12862     
12863     getAutoCreate : function()
12864     {
12865         
12866         var cfg =  {
12867             cls :'form-group' ,
12868             cn : [
12869                
12870                 {
12871                     tag: 'label',
12872                    //cls : 'input-group-addon',
12873                     html : this.fieldLabel
12874
12875                 },
12876
12877                 {
12878                     tag: 'input',
12879                     type : 'hidden',
12880                     name : this.name,
12881                     value : this.value,
12882                     cls : 'd-none  form-control'
12883                 },
12884                 
12885                 {
12886                     tag: 'input',
12887                     multiple : 'multiple',
12888                     type : 'file',
12889                     cls : 'd-none  roo-card-upload-selector'
12890                 },
12891                 
12892                 {
12893                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12894                 },
12895                 {
12896                     cls : 'card-columns roo-card-uploader-container'
12897                 }
12898
12899             ]
12900         };
12901            
12902          
12903         return cfg;
12904     },
12905     
12906     getChildContainer : function() /// what children are added to.
12907     {
12908         return this.containerEl;
12909     },
12910    
12911     getButtonContainer : function() /// what children are added to.
12912     {
12913         return this.el.select(".roo-card-uploader-button-container").first();
12914     },
12915    
12916     initEvents : function()
12917     {
12918         
12919         Roo.bootstrap.Input.prototype.initEvents.call(this);
12920         
12921         var t = this;
12922         this.addxtype({
12923             xns: Roo.bootstrap,
12924
12925             xtype : 'Button',
12926             container_method : 'getButtonContainer' ,            
12927             html :  this.html, // fix changable?
12928             cls : 'w-100 ',
12929             listeners : {
12930                 'click' : function(btn, e) {
12931                     t.onClick(e);
12932                 }
12933             }
12934         });
12935         
12936         
12937         
12938         
12939         this.urlAPI = (window.createObjectURL && window) || 
12940                                 (window.URL && URL.revokeObjectURL && URL) || 
12941                                 (window.webkitURL && webkitURL);
12942                         
12943          
12944          
12945          
12946         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12947         
12948         this.selectorEl.on('change', this.onFileSelected, this);
12949         if (this.images) {
12950             var t = this;
12951             this.images.forEach(function(img) {
12952                 t.addCard(img)
12953             });
12954             this.images = false;
12955         }
12956         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12957          
12958        
12959     },
12960     
12961    
12962     onClick : function(e)
12963     {
12964         e.preventDefault();
12965          
12966         this.selectorEl.dom.click();
12967          
12968     },
12969     
12970     onFileSelected : function(e)
12971     {
12972         e.preventDefault();
12973         
12974         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12975             return;
12976         }
12977         
12978         Roo.each(this.selectorEl.dom.files, function(file){    
12979             this.addFile(file);
12980         }, this);
12981          
12982     },
12983     
12984       
12985     
12986       
12987     
12988     addFile : function(file)
12989     {
12990            
12991         if(typeof(file) === 'string'){
12992             throw "Add file by name?"; // should not happen
12993             return;
12994         }
12995         
12996         if(!file || !this.urlAPI){
12997             return;
12998         }
12999         
13000         // file;
13001         // file.type;
13002         
13003         var _this = this;
13004         
13005         
13006         var url = _this.urlAPI.createObjectURL( file);
13007            
13008         this.addCard({
13009             id : Roo.bootstrap.CardUploader.ID--,
13010             is_uploaded : false,
13011             src : url,
13012             srcfile : file,
13013             title : file.name,
13014             mimetype : file.type,
13015             preview : false,
13016             is_deleted : 0
13017         });
13018         
13019     },
13020     
13021     /**
13022      * addCard - add an Attachment to the uploader
13023      * @param data - the data about the image to upload
13024      *
13025      * {
13026           id : 123
13027           title : "Title of file",
13028           is_uploaded : false,
13029           src : "http://.....",
13030           srcfile : { the File upload object },
13031           mimetype : file.type,
13032           preview : false,
13033           is_deleted : 0
13034           .. any other data...
13035         }
13036      *
13037      * 
13038     */
13039     
13040     addCard : function (data)
13041     {
13042         // hidden input element?
13043         // if the file is not an image...
13044         //then we need to use something other that and header_image
13045         var t = this;
13046         //   remove.....
13047         var footer = [
13048             {
13049                 xns : Roo.bootstrap,
13050                 xtype : 'CardFooter',
13051                  items: [
13052                     {
13053                         xns : Roo.bootstrap,
13054                         xtype : 'Element',
13055                         cls : 'd-flex',
13056                         items : [
13057                             
13058                             {
13059                                 xns : Roo.bootstrap,
13060                                 xtype : 'Button',
13061                                 html : String.format("<small>{0}</small>", data.title),
13062                                 cls : 'col-10 text-left',
13063                                 size: 'sm',
13064                                 weight: 'link',
13065                                 fa : 'download',
13066                                 listeners : {
13067                                     click : function() {
13068                                      
13069                                         t.fireEvent( "download", t, data );
13070                                     }
13071                                 }
13072                             },
13073                           
13074                             {
13075                                 xns : Roo.bootstrap,
13076                                 xtype : 'Button',
13077                                 style: 'max-height: 28px; ',
13078                                 size : 'sm',
13079                                 weight: 'danger',
13080                                 cls : 'col-2',
13081                                 fa : 'times',
13082                                 listeners : {
13083                                     click : function() {
13084                                         t.removeCard(data.id)
13085                                     }
13086                                 }
13087                             }
13088                         ]
13089                     }
13090                     
13091                 ] 
13092             }
13093             
13094         ];
13095         
13096         var cn = this.addxtype(
13097             {
13098                  
13099                 xns : Roo.bootstrap,
13100                 xtype : 'Card',
13101                 closeable : true,
13102                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
13103                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
13104                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
13105                 data : data,
13106                 html : false,
13107                  
13108                 items : footer,
13109                 initEvents : function() {
13110                     Roo.bootstrap.Card.prototype.initEvents.call(this);
13111                     var card = this;
13112                     this.imgEl = this.el.select('.card-img-top').first();
13113                     if (this.imgEl) {
13114                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
13115                         this.imgEl.set({ 'pointer' : 'cursor' });
13116                                   
13117                     }
13118                     this.getCardFooter().addClass('p-1');
13119                     
13120                   
13121                 }
13122                 
13123             }
13124         );
13125         // dont' really need ot update items.
13126         // this.items.push(cn);
13127         this.fileCollection.add(cn);
13128         
13129         if (!data.srcfile) {
13130             this.updateInput();
13131             return;
13132         }
13133             
13134         var _t = this;
13135         var reader = new FileReader();
13136         reader.addEventListener("load", function() {  
13137             data.srcdata =  reader.result;
13138             _t.updateInput();
13139         });
13140         reader.readAsDataURL(data.srcfile);
13141         
13142         
13143         
13144     },
13145     removeCard : function(id)
13146     {
13147         
13148         var card  = this.fileCollection.get(id);
13149         card.data.is_deleted = 1;
13150         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
13151         //this.fileCollection.remove(card);
13152         //this.items = this.items.filter(function(e) { return e != card });
13153         // dont' really need ot update items.
13154         card.el.dom.parentNode.removeChild(card.el.dom);
13155         this.updateInput();
13156
13157         
13158     },
13159     reset: function()
13160     {
13161         this.fileCollection.each(function(card) {
13162             if (card.el.dom && card.el.dom.parentNode) {
13163                 card.el.dom.parentNode.removeChild(card.el.dom);
13164             }
13165         });
13166         this.fileCollection.clear();
13167         this.updateInput();
13168     },
13169     
13170     updateInput : function()
13171     {
13172          var data = [];
13173         this.fileCollection.each(function(e) {
13174             data.push(e.data);
13175             
13176         });
13177         this.inputEl().dom.value = JSON.stringify(data);
13178         
13179         
13180         
13181     }
13182     
13183     
13184 });
13185
13186
13187 Roo.bootstrap.CardUploader.ID = -1;/*
13188  * Based on:
13189  * Ext JS Library 1.1.1
13190  * Copyright(c) 2006-2007, Ext JS, LLC.
13191  *
13192  * Originally Released Under LGPL - original licence link has changed is not relivant.
13193  *
13194  * Fork - LGPL
13195  * <script type="text/javascript">
13196  */
13197
13198
13199 /**
13200  * @class Roo.data.SortTypes
13201  * @singleton
13202  * Defines the default sorting (casting?) comparison functions used when sorting data.
13203  */
13204 Roo.data.SortTypes = {
13205     /**
13206      * Default sort that does nothing
13207      * @param {Mixed} s The value being converted
13208      * @return {Mixed} The comparison value
13209      */
13210     none : function(s){
13211         return s;
13212     },
13213     
13214     /**
13215      * The regular expression used to strip tags
13216      * @type {RegExp}
13217      * @property
13218      */
13219     stripTagsRE : /<\/?[^>]+>/gi,
13220     
13221     /**
13222      * Strips all HTML tags to sort on text only
13223      * @param {Mixed} s The value being converted
13224      * @return {String} The comparison value
13225      */
13226     asText : function(s){
13227         return String(s).replace(this.stripTagsRE, "");
13228     },
13229     
13230     /**
13231      * Strips all HTML tags to sort on text only - Case insensitive
13232      * @param {Mixed} s The value being converted
13233      * @return {String} The comparison value
13234      */
13235     asUCText : function(s){
13236         return String(s).toUpperCase().replace(this.stripTagsRE, "");
13237     },
13238     
13239     /**
13240      * Case insensitive string
13241      * @param {Mixed} s The value being converted
13242      * @return {String} The comparison value
13243      */
13244     asUCString : function(s) {
13245         return String(s).toUpperCase();
13246     },
13247     
13248     /**
13249      * Date sorting
13250      * @param {Mixed} s The value being converted
13251      * @return {Number} The comparison value
13252      */
13253     asDate : function(s) {
13254         if(!s){
13255             return 0;
13256         }
13257         if(s instanceof Date){
13258             return s.getTime();
13259         }
13260         return Date.parse(String(s));
13261     },
13262     
13263     /**
13264      * Float sorting
13265      * @param {Mixed} s The value being converted
13266      * @return {Float} The comparison value
13267      */
13268     asFloat : function(s) {
13269         var val = parseFloat(String(s).replace(/,/g, ""));
13270         if(isNaN(val)) {
13271             val = 0;
13272         }
13273         return val;
13274     },
13275     
13276     /**
13277      * Integer sorting
13278      * @param {Mixed} s The value being converted
13279      * @return {Number} The comparison value
13280      */
13281     asInt : function(s) {
13282         var val = parseInt(String(s).replace(/,/g, ""));
13283         if(isNaN(val)) {
13284             val = 0;
13285         }
13286         return val;
13287     }
13288 };/*
13289  * Based on:
13290  * Ext JS Library 1.1.1
13291  * Copyright(c) 2006-2007, Ext JS, LLC.
13292  *
13293  * Originally Released Under LGPL - original licence link has changed is not relivant.
13294  *
13295  * Fork - LGPL
13296  * <script type="text/javascript">
13297  */
13298
13299 /**
13300 * @class Roo.data.Record
13301  * Instances of this class encapsulate both record <em>definition</em> information, and record
13302  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13303  * to access Records cached in an {@link Roo.data.Store} object.<br>
13304  * <p>
13305  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13306  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13307  * objects.<br>
13308  * <p>
13309  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13310  * @constructor
13311  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13312  * {@link #create}. The parameters are the same.
13313  * @param {Array} data An associative Array of data values keyed by the field name.
13314  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13315  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13316  * not specified an integer id is generated.
13317  */
13318 Roo.data.Record = function(data, id){
13319     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13320     this.data = data;
13321 };
13322
13323 /**
13324  * Generate a constructor for a specific record layout.
13325  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13326  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13327  * Each field definition object may contain the following properties: <ul>
13328  * <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,
13329  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13330  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13331  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13332  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13333  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13334  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13335  * this may be omitted.</p></li>
13336  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13337  * <ul><li>auto (Default, implies no conversion)</li>
13338  * <li>string</li>
13339  * <li>int</li>
13340  * <li>float</li>
13341  * <li>boolean</li>
13342  * <li>date</li></ul></p></li>
13343  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13344  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13345  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13346  * by the Reader into an object that will be stored in the Record. It is passed the
13347  * following parameters:<ul>
13348  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13349  * </ul></p></li>
13350  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13351  * </ul>
13352  * <br>usage:<br><pre><code>
13353 var TopicRecord = Roo.data.Record.create(
13354     {name: 'title', mapping: 'topic_title'},
13355     {name: 'author', mapping: 'username'},
13356     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13357     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13358     {name: 'lastPoster', mapping: 'user2'},
13359     {name: 'excerpt', mapping: 'post_text'}
13360 );
13361
13362 var myNewRecord = new TopicRecord({
13363     title: 'Do my job please',
13364     author: 'noobie',
13365     totalPosts: 1,
13366     lastPost: new Date(),
13367     lastPoster: 'Animal',
13368     excerpt: 'No way dude!'
13369 });
13370 myStore.add(myNewRecord);
13371 </code></pre>
13372  * @method create
13373  * @static
13374  */
13375 Roo.data.Record.create = function(o){
13376     var f = function(){
13377         f.superclass.constructor.apply(this, arguments);
13378     };
13379     Roo.extend(f, Roo.data.Record);
13380     var p = f.prototype;
13381     p.fields = new Roo.util.MixedCollection(false, function(field){
13382         return field.name;
13383     });
13384     for(var i = 0, len = o.length; i < len; i++){
13385         p.fields.add(new Roo.data.Field(o[i]));
13386     }
13387     f.getField = function(name){
13388         return p.fields.get(name);  
13389     };
13390     return f;
13391 };
13392
13393 Roo.data.Record.AUTO_ID = 1000;
13394 Roo.data.Record.EDIT = 'edit';
13395 Roo.data.Record.REJECT = 'reject';
13396 Roo.data.Record.COMMIT = 'commit';
13397
13398 Roo.data.Record.prototype = {
13399     /**
13400      * Readonly flag - true if this record has been modified.
13401      * @type Boolean
13402      */
13403     dirty : false,
13404     editing : false,
13405     error: null,
13406     modified: null,
13407
13408     // private
13409     join : function(store){
13410         this.store = store;
13411     },
13412
13413     /**
13414      * Set the named field to the specified value.
13415      * @param {String} name The name of the field to set.
13416      * @param {Object} value The value to set the field to.
13417      */
13418     set : function(name, value){
13419         if(this.data[name] == value){
13420             return;
13421         }
13422         this.dirty = true;
13423         if(!this.modified){
13424             this.modified = {};
13425         }
13426         if(typeof this.modified[name] == 'undefined'){
13427             this.modified[name] = this.data[name];
13428         }
13429         this.data[name] = value;
13430         if(!this.editing && this.store){
13431             this.store.afterEdit(this);
13432         }       
13433     },
13434
13435     /**
13436      * Get the value of the named field.
13437      * @param {String} name The name of the field to get the value of.
13438      * @return {Object} The value of the field.
13439      */
13440     get : function(name){
13441         return this.data[name]; 
13442     },
13443
13444     // private
13445     beginEdit : function(){
13446         this.editing = true;
13447         this.modified = {}; 
13448     },
13449
13450     // private
13451     cancelEdit : function(){
13452         this.editing = false;
13453         delete this.modified;
13454     },
13455
13456     // private
13457     endEdit : function(){
13458         this.editing = false;
13459         if(this.dirty && this.store){
13460             this.store.afterEdit(this);
13461         }
13462     },
13463
13464     /**
13465      * Usually called by the {@link Roo.data.Store} which owns the Record.
13466      * Rejects all changes made to the Record since either creation, or the last commit operation.
13467      * Modified fields are reverted to their original values.
13468      * <p>
13469      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13470      * of reject operations.
13471      */
13472     reject : function(){
13473         var m = this.modified;
13474         for(var n in m){
13475             if(typeof m[n] != "function"){
13476                 this.data[n] = m[n];
13477             }
13478         }
13479         this.dirty = false;
13480         delete this.modified;
13481         this.editing = false;
13482         if(this.store){
13483             this.store.afterReject(this);
13484         }
13485     },
13486
13487     /**
13488      * Usually called by the {@link Roo.data.Store} which owns the Record.
13489      * Commits all changes made to the Record since either creation, or the last commit operation.
13490      * <p>
13491      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13492      * of commit operations.
13493      */
13494     commit : function(){
13495         this.dirty = false;
13496         delete this.modified;
13497         this.editing = false;
13498         if(this.store){
13499             this.store.afterCommit(this);
13500         }
13501     },
13502
13503     // private
13504     hasError : function(){
13505         return this.error != null;
13506     },
13507
13508     // private
13509     clearError : function(){
13510         this.error = null;
13511     },
13512
13513     /**
13514      * Creates a copy of this record.
13515      * @param {String} id (optional) A new record id if you don't want to use this record's id
13516      * @return {Record}
13517      */
13518     copy : function(newId) {
13519         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13520     }
13521 };/*
13522  * Based on:
13523  * Ext JS Library 1.1.1
13524  * Copyright(c) 2006-2007, Ext JS, LLC.
13525  *
13526  * Originally Released Under LGPL - original licence link has changed is not relivant.
13527  *
13528  * Fork - LGPL
13529  * <script type="text/javascript">
13530  */
13531
13532
13533
13534 /**
13535  * @class Roo.data.Store
13536  * @extends Roo.util.Observable
13537  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13538  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13539  * <p>
13540  * 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
13541  * has no knowledge of the format of the data returned by the Proxy.<br>
13542  * <p>
13543  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13544  * instances from the data object. These records are cached and made available through accessor functions.
13545  * @constructor
13546  * Creates a new Store.
13547  * @param {Object} config A config object containing the objects needed for the Store to access data,
13548  * and read the data into Records.
13549  */
13550 Roo.data.Store = function(config){
13551     this.data = new Roo.util.MixedCollection(false);
13552     this.data.getKey = function(o){
13553         return o.id;
13554     };
13555     this.baseParams = {};
13556     // private
13557     this.paramNames = {
13558         "start" : "start",
13559         "limit" : "limit",
13560         "sort" : "sort",
13561         "dir" : "dir",
13562         "multisort" : "_multisort"
13563     };
13564
13565     if(config && config.data){
13566         this.inlineData = config.data;
13567         delete config.data;
13568     }
13569
13570     Roo.apply(this, config);
13571     
13572     if(this.reader){ // reader passed
13573         this.reader = Roo.factory(this.reader, Roo.data);
13574         this.reader.xmodule = this.xmodule || false;
13575         if(!this.recordType){
13576             this.recordType = this.reader.recordType;
13577         }
13578         if(this.reader.onMetaChange){
13579             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13580         }
13581     }
13582
13583     if(this.recordType){
13584         this.fields = this.recordType.prototype.fields;
13585     }
13586     this.modified = [];
13587
13588     this.addEvents({
13589         /**
13590          * @event datachanged
13591          * Fires when the data cache has changed, and a widget which is using this Store
13592          * as a Record cache should refresh its view.
13593          * @param {Store} this
13594          */
13595         datachanged : true,
13596         /**
13597          * @event metachange
13598          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13599          * @param {Store} this
13600          * @param {Object} meta The JSON metadata
13601          */
13602         metachange : true,
13603         /**
13604          * @event add
13605          * Fires when Records have been added to the Store
13606          * @param {Store} this
13607          * @param {Roo.data.Record[]} records The array of Records added
13608          * @param {Number} index The index at which the record(s) were added
13609          */
13610         add : true,
13611         /**
13612          * @event remove
13613          * Fires when a Record has been removed from the Store
13614          * @param {Store} this
13615          * @param {Roo.data.Record} record The Record that was removed
13616          * @param {Number} index The index at which the record was removed
13617          */
13618         remove : true,
13619         /**
13620          * @event update
13621          * Fires when a Record has been updated
13622          * @param {Store} this
13623          * @param {Roo.data.Record} record The Record that was updated
13624          * @param {String} operation The update operation being performed.  Value may be one of:
13625          * <pre><code>
13626  Roo.data.Record.EDIT
13627  Roo.data.Record.REJECT
13628  Roo.data.Record.COMMIT
13629          * </code></pre>
13630          */
13631         update : true,
13632         /**
13633          * @event clear
13634          * Fires when the data cache has been cleared.
13635          * @param {Store} this
13636          */
13637         clear : true,
13638         /**
13639          * @event beforeload
13640          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13641          * the load action will be canceled.
13642          * @param {Store} this
13643          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13644          */
13645         beforeload : true,
13646         /**
13647          * @event beforeloadadd
13648          * Fires after a new set of Records has been loaded.
13649          * @param {Store} this
13650          * @param {Roo.data.Record[]} records The Records that were loaded
13651          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13652          */
13653         beforeloadadd : true,
13654         /**
13655          * @event load
13656          * Fires after a new set of Records has been loaded, before they are added to the store.
13657          * @param {Store} this
13658          * @param {Roo.data.Record[]} records The Records that were loaded
13659          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13660          * @params {Object} return from reader
13661          */
13662         load : true,
13663         /**
13664          * @event loadexception
13665          * Fires if an exception occurs in the Proxy during loading.
13666          * Called with the signature of the Proxy's "loadexception" event.
13667          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13668          * 
13669          * @param {Proxy} 
13670          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13671          * @param {Object} load options 
13672          * @param {Object} jsonData from your request (normally this contains the Exception)
13673          */
13674         loadexception : true
13675     });
13676     
13677     if(this.proxy){
13678         this.proxy = Roo.factory(this.proxy, Roo.data);
13679         this.proxy.xmodule = this.xmodule || false;
13680         this.relayEvents(this.proxy,  ["loadexception"]);
13681     }
13682     this.sortToggle = {};
13683     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13684
13685     Roo.data.Store.superclass.constructor.call(this);
13686
13687     if(this.inlineData){
13688         this.loadData(this.inlineData);
13689         delete this.inlineData;
13690     }
13691 };
13692
13693 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13694      /**
13695     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13696     * without a remote query - used by combo/forms at present.
13697     */
13698     
13699     /**
13700     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13701     */
13702     /**
13703     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13704     */
13705     /**
13706     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13707     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13708     */
13709     /**
13710     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13711     * on any HTTP request
13712     */
13713     /**
13714     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13715     */
13716     /**
13717     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13718     */
13719     multiSort: false,
13720     /**
13721     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13722     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13723     */
13724     remoteSort : false,
13725
13726     /**
13727     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13728      * loaded or when a record is removed. (defaults to false).
13729     */
13730     pruneModifiedRecords : false,
13731
13732     // private
13733     lastOptions : null,
13734
13735     /**
13736      * Add Records to the Store and fires the add event.
13737      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13738      */
13739     add : function(records){
13740         records = [].concat(records);
13741         for(var i = 0, len = records.length; i < len; i++){
13742             records[i].join(this);
13743         }
13744         var index = this.data.length;
13745         this.data.addAll(records);
13746         this.fireEvent("add", this, records, index);
13747     },
13748
13749     /**
13750      * Remove a Record from the Store and fires the remove event.
13751      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13752      */
13753     remove : function(record){
13754         var index = this.data.indexOf(record);
13755         this.data.removeAt(index);
13756  
13757         if(this.pruneModifiedRecords){
13758             this.modified.remove(record);
13759         }
13760         this.fireEvent("remove", this, record, index);
13761     },
13762
13763     /**
13764      * Remove all Records from the Store and fires the clear event.
13765      */
13766     removeAll : function(){
13767         this.data.clear();
13768         if(this.pruneModifiedRecords){
13769             this.modified = [];
13770         }
13771         this.fireEvent("clear", this);
13772     },
13773
13774     /**
13775      * Inserts Records to the Store at the given index and fires the add event.
13776      * @param {Number} index The start index at which to insert the passed Records.
13777      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13778      */
13779     insert : function(index, records){
13780         records = [].concat(records);
13781         for(var i = 0, len = records.length; i < len; i++){
13782             this.data.insert(index, records[i]);
13783             records[i].join(this);
13784         }
13785         this.fireEvent("add", this, records, index);
13786     },
13787
13788     /**
13789      * Get the index within the cache of the passed Record.
13790      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13791      * @return {Number} The index of the passed Record. Returns -1 if not found.
13792      */
13793     indexOf : function(record){
13794         return this.data.indexOf(record);
13795     },
13796
13797     /**
13798      * Get the index within the cache of the Record with the passed id.
13799      * @param {String} id The id of the Record to find.
13800      * @return {Number} The index of the Record. Returns -1 if not found.
13801      */
13802     indexOfId : function(id){
13803         return this.data.indexOfKey(id);
13804     },
13805
13806     /**
13807      * Get the Record with the specified id.
13808      * @param {String} id The id of the Record to find.
13809      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13810      */
13811     getById : function(id){
13812         return this.data.key(id);
13813     },
13814
13815     /**
13816      * Get the Record at the specified index.
13817      * @param {Number} index The index of the Record to find.
13818      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13819      */
13820     getAt : function(index){
13821         return this.data.itemAt(index);
13822     },
13823
13824     /**
13825      * Returns a range of Records between specified indices.
13826      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13827      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13828      * @return {Roo.data.Record[]} An array of Records
13829      */
13830     getRange : function(start, end){
13831         return this.data.getRange(start, end);
13832     },
13833
13834     // private
13835     storeOptions : function(o){
13836         o = Roo.apply({}, o);
13837         delete o.callback;
13838         delete o.scope;
13839         this.lastOptions = o;
13840     },
13841
13842     /**
13843      * Loads the Record cache from the configured Proxy using the configured Reader.
13844      * <p>
13845      * If using remote paging, then the first load call must specify the <em>start</em>
13846      * and <em>limit</em> properties in the options.params property to establish the initial
13847      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13848      * <p>
13849      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13850      * and this call will return before the new data has been loaded. Perform any post-processing
13851      * in a callback function, or in a "load" event handler.</strong>
13852      * <p>
13853      * @param {Object} options An object containing properties which control loading options:<ul>
13854      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13855      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13856      * passed the following arguments:<ul>
13857      * <li>r : Roo.data.Record[]</li>
13858      * <li>options: Options object from the load call</li>
13859      * <li>success: Boolean success indicator</li></ul></li>
13860      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13861      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13862      * </ul>
13863      */
13864     load : function(options){
13865         options = options || {};
13866         if(this.fireEvent("beforeload", this, options) !== false){
13867             this.storeOptions(options);
13868             var p = Roo.apply(options.params || {}, this.baseParams);
13869             // if meta was not loaded from remote source.. try requesting it.
13870             if (!this.reader.metaFromRemote) {
13871                 p._requestMeta = 1;
13872             }
13873             if(this.sortInfo && this.remoteSort){
13874                 var pn = this.paramNames;
13875                 p[pn["sort"]] = this.sortInfo.field;
13876                 p[pn["dir"]] = this.sortInfo.direction;
13877             }
13878             if (this.multiSort) {
13879                 var pn = this.paramNames;
13880                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13881             }
13882             
13883             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13884         }
13885     },
13886
13887     /**
13888      * Reloads the Record cache from the configured Proxy using the configured Reader and
13889      * the options from the last load operation performed.
13890      * @param {Object} options (optional) An object containing properties which may override the options
13891      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13892      * the most recently used options are reused).
13893      */
13894     reload : function(options){
13895         this.load(Roo.applyIf(options||{}, this.lastOptions));
13896     },
13897
13898     // private
13899     // Called as a callback by the Reader during a load operation.
13900     loadRecords : function(o, options, success){
13901         if(!o || success === false){
13902             if(success !== false){
13903                 this.fireEvent("load", this, [], options, o);
13904             }
13905             if(options.callback){
13906                 options.callback.call(options.scope || this, [], options, false);
13907             }
13908             return;
13909         }
13910         // if data returned failure - throw an exception.
13911         if (o.success === false) {
13912             // show a message if no listener is registered.
13913             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13914                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13915             }
13916             // loadmask wil be hooked into this..
13917             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13918             return;
13919         }
13920         var r = o.records, t = o.totalRecords || r.length;
13921         
13922         this.fireEvent("beforeloadadd", this, r, options, o);
13923         
13924         if(!options || options.add !== true){
13925             if(this.pruneModifiedRecords){
13926                 this.modified = [];
13927             }
13928             for(var i = 0, len = r.length; i < len; i++){
13929                 r[i].join(this);
13930             }
13931             if(this.snapshot){
13932                 this.data = this.snapshot;
13933                 delete this.snapshot;
13934             }
13935             this.data.clear();
13936             this.data.addAll(r);
13937             this.totalLength = t;
13938             this.applySort();
13939             this.fireEvent("datachanged", this);
13940         }else{
13941             this.totalLength = Math.max(t, this.data.length+r.length);
13942             this.add(r);
13943         }
13944         
13945         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13946                 
13947             var e = new Roo.data.Record({});
13948
13949             e.set(this.parent.displayField, this.parent.emptyTitle);
13950             e.set(this.parent.valueField, '');
13951
13952             this.insert(0, e);
13953         }
13954             
13955         this.fireEvent("load", this, r, options, o);
13956         if(options.callback){
13957             options.callback.call(options.scope || this, r, options, true);
13958         }
13959     },
13960
13961
13962     /**
13963      * Loads data from a passed data block. A Reader which understands the format of the data
13964      * must have been configured in the constructor.
13965      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13966      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13967      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13968      */
13969     loadData : function(o, append){
13970         var r = this.reader.readRecords(o);
13971         this.loadRecords(r, {add: append}, true);
13972     },
13973     
13974      /**
13975      * using 'cn' the nested child reader read the child array into it's child stores.
13976      * @param {Object} rec The record with a 'children array
13977      */
13978     loadDataFromChildren : function(rec)
13979     {
13980         this.loadData(this.reader.toLoadData(rec));
13981     },
13982     
13983
13984     /**
13985      * Gets the number of cached records.
13986      * <p>
13987      * <em>If using paging, this may not be the total size of the dataset. If the data object
13988      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13989      * the data set size</em>
13990      */
13991     getCount : function(){
13992         return this.data.length || 0;
13993     },
13994
13995     /**
13996      * Gets the total number of records in the dataset as returned by the server.
13997      * <p>
13998      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13999      * the dataset size</em>
14000      */
14001     getTotalCount : function(){
14002         return this.totalLength || 0;
14003     },
14004
14005     /**
14006      * Returns the sort state of the Store as an object with two properties:
14007      * <pre><code>
14008  field {String} The name of the field by which the Records are sorted
14009  direction {String} The sort order, "ASC" or "DESC"
14010      * </code></pre>
14011      */
14012     getSortState : function(){
14013         return this.sortInfo;
14014     },
14015
14016     // private
14017     applySort : function(){
14018         if(this.sortInfo && !this.remoteSort){
14019             var s = this.sortInfo, f = s.field;
14020             var st = this.fields.get(f).sortType;
14021             var fn = function(r1, r2){
14022                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
14023                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
14024             };
14025             this.data.sort(s.direction, fn);
14026             if(this.snapshot && this.snapshot != this.data){
14027                 this.snapshot.sort(s.direction, fn);
14028             }
14029         }
14030     },
14031
14032     /**
14033      * Sets the default sort column and order to be used by the next load operation.
14034      * @param {String} fieldName The name of the field to sort by.
14035      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14036      */
14037     setDefaultSort : function(field, dir){
14038         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
14039     },
14040
14041     /**
14042      * Sort the Records.
14043      * If remote sorting is used, the sort is performed on the server, and the cache is
14044      * reloaded. If local sorting is used, the cache is sorted internally.
14045      * @param {String} fieldName The name of the field to sort by.
14046      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
14047      */
14048     sort : function(fieldName, dir){
14049         var f = this.fields.get(fieldName);
14050         if(!dir){
14051             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
14052             
14053             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
14054                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
14055             }else{
14056                 dir = f.sortDir;
14057             }
14058         }
14059         this.sortToggle[f.name] = dir;
14060         this.sortInfo = {field: f.name, direction: dir};
14061         if(!this.remoteSort){
14062             this.applySort();
14063             this.fireEvent("datachanged", this);
14064         }else{
14065             this.load(this.lastOptions);
14066         }
14067     },
14068
14069     /**
14070      * Calls the specified function for each of the Records in the cache.
14071      * @param {Function} fn The function to call. The Record is passed as the first parameter.
14072      * Returning <em>false</em> aborts and exits the iteration.
14073      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
14074      */
14075     each : function(fn, scope){
14076         this.data.each(fn, scope);
14077     },
14078
14079     /**
14080      * Gets all records modified since the last commit.  Modified records are persisted across load operations
14081      * (e.g., during paging).
14082      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
14083      */
14084     getModifiedRecords : function(){
14085         return this.modified;
14086     },
14087
14088     // private
14089     createFilterFn : function(property, value, anyMatch){
14090         if(!value.exec){ // not a regex
14091             value = String(value);
14092             if(value.length == 0){
14093                 return false;
14094             }
14095             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
14096         }
14097         return function(r){
14098             return value.test(r.data[property]);
14099         };
14100     },
14101
14102     /**
14103      * Sums the value of <i>property</i> for each record between start and end and returns the result.
14104      * @param {String} property A field on your records
14105      * @param {Number} start The record index to start at (defaults to 0)
14106      * @param {Number} end The last record index to include (defaults to length - 1)
14107      * @return {Number} The sum
14108      */
14109     sum : function(property, start, end){
14110         var rs = this.data.items, v = 0;
14111         start = start || 0;
14112         end = (end || end === 0) ? end : rs.length-1;
14113
14114         for(var i = start; i <= end; i++){
14115             v += (rs[i].data[property] || 0);
14116         }
14117         return v;
14118     },
14119
14120     /**
14121      * Filter the records by a specified property.
14122      * @param {String} field A field on your records
14123      * @param {String/RegExp} value Either a string that the field
14124      * should start with or a RegExp to test against the field
14125      * @param {Boolean} anyMatch True to match any part not just the beginning
14126      */
14127     filter : function(property, value, anyMatch){
14128         var fn = this.createFilterFn(property, value, anyMatch);
14129         return fn ? this.filterBy(fn) : this.clearFilter();
14130     },
14131
14132     /**
14133      * Filter by a function. The specified function will be called with each
14134      * record in this data source. If the function returns true the record is included,
14135      * otherwise it is filtered.
14136      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14137      * @param {Object} scope (optional) The scope of the function (defaults to this)
14138      */
14139     filterBy : function(fn, scope){
14140         this.snapshot = this.snapshot || this.data;
14141         this.data = this.queryBy(fn, scope||this);
14142         this.fireEvent("datachanged", this);
14143     },
14144
14145     /**
14146      * Query the records by a specified property.
14147      * @param {String} field A field on your records
14148      * @param {String/RegExp} value Either a string that the field
14149      * should start with or a RegExp to test against the field
14150      * @param {Boolean} anyMatch True to match any part not just the beginning
14151      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14152      */
14153     query : function(property, value, anyMatch){
14154         var fn = this.createFilterFn(property, value, anyMatch);
14155         return fn ? this.queryBy(fn) : this.data.clone();
14156     },
14157
14158     /**
14159      * Query by a function. The specified function will be called with each
14160      * record in this data source. If the function returns true the record is included
14161      * in the results.
14162      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
14163      * @param {Object} scope (optional) The scope of the function (defaults to this)
14164       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
14165      **/
14166     queryBy : function(fn, scope){
14167         var data = this.snapshot || this.data;
14168         return data.filterBy(fn, scope||this);
14169     },
14170
14171     /**
14172      * Collects unique values for a particular dataIndex from this store.
14173      * @param {String} dataIndex The property to collect
14174      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
14175      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
14176      * @return {Array} An array of the unique values
14177      **/
14178     collect : function(dataIndex, allowNull, bypassFilter){
14179         var d = (bypassFilter === true && this.snapshot) ?
14180                 this.snapshot.items : this.data.items;
14181         var v, sv, r = [], l = {};
14182         for(var i = 0, len = d.length; i < len; i++){
14183             v = d[i].data[dataIndex];
14184             sv = String(v);
14185             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
14186                 l[sv] = true;
14187                 r[r.length] = v;
14188             }
14189         }
14190         return r;
14191     },
14192
14193     /**
14194      * Revert to a view of the Record cache with no filtering applied.
14195      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
14196      */
14197     clearFilter : function(suppressEvent){
14198         if(this.snapshot && this.snapshot != this.data){
14199             this.data = this.snapshot;
14200             delete this.snapshot;
14201             if(suppressEvent !== true){
14202                 this.fireEvent("datachanged", this);
14203             }
14204         }
14205     },
14206
14207     // private
14208     afterEdit : function(record){
14209         if(this.modified.indexOf(record) == -1){
14210             this.modified.push(record);
14211         }
14212         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
14213     },
14214     
14215     // private
14216     afterReject : function(record){
14217         this.modified.remove(record);
14218         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
14219     },
14220
14221     // private
14222     afterCommit : function(record){
14223         this.modified.remove(record);
14224         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
14225     },
14226
14227     /**
14228      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
14229      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
14230      */
14231     commitChanges : function(){
14232         var m = this.modified.slice(0);
14233         this.modified = [];
14234         for(var i = 0, len = m.length; i < len; i++){
14235             m[i].commit();
14236         }
14237     },
14238
14239     /**
14240      * Cancel outstanding changes on all changed records.
14241      */
14242     rejectChanges : function(){
14243         var m = this.modified.slice(0);
14244         this.modified = [];
14245         for(var i = 0, len = m.length; i < len; i++){
14246             m[i].reject();
14247         }
14248     },
14249
14250     onMetaChange : function(meta, rtype, o){
14251         this.recordType = rtype;
14252         this.fields = rtype.prototype.fields;
14253         delete this.snapshot;
14254         this.sortInfo = meta.sortInfo || this.sortInfo;
14255         this.modified = [];
14256         this.fireEvent('metachange', this, this.reader.meta);
14257     },
14258     
14259     moveIndex : function(data, type)
14260     {
14261         var index = this.indexOf(data);
14262         
14263         var newIndex = index + type;
14264         
14265         this.remove(data);
14266         
14267         this.insert(newIndex, data);
14268         
14269     }
14270 });/*
14271  * Based on:
14272  * Ext JS Library 1.1.1
14273  * Copyright(c) 2006-2007, Ext JS, LLC.
14274  *
14275  * Originally Released Under LGPL - original licence link has changed is not relivant.
14276  *
14277  * Fork - LGPL
14278  * <script type="text/javascript">
14279  */
14280
14281 /**
14282  * @class Roo.data.SimpleStore
14283  * @extends Roo.data.Store
14284  * Small helper class to make creating Stores from Array data easier.
14285  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
14286  * @cfg {Array} fields An array of field definition objects, or field name strings.
14287  * @cfg {Object} an existing reader (eg. copied from another store)
14288  * @cfg {Array} data The multi-dimensional array of data
14289  * @constructor
14290  * @param {Object} config
14291  */
14292 Roo.data.SimpleStore = function(config)
14293 {
14294     Roo.data.SimpleStore.superclass.constructor.call(this, {
14295         isLocal : true,
14296         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14297                 id: config.id
14298             },
14299             Roo.data.Record.create(config.fields)
14300         ),
14301         proxy : new Roo.data.MemoryProxy(config.data)
14302     });
14303     this.load();
14304 };
14305 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14306  * Based on:
14307  * Ext JS Library 1.1.1
14308  * Copyright(c) 2006-2007, Ext JS, LLC.
14309  *
14310  * Originally Released Under LGPL - original licence link has changed is not relivant.
14311  *
14312  * Fork - LGPL
14313  * <script type="text/javascript">
14314  */
14315
14316 /**
14317 /**
14318  * @extends Roo.data.Store
14319  * @class Roo.data.JsonStore
14320  * Small helper class to make creating Stores for JSON data easier. <br/>
14321 <pre><code>
14322 var store = new Roo.data.JsonStore({
14323     url: 'get-images.php',
14324     root: 'images',
14325     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14326 });
14327 </code></pre>
14328  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14329  * JsonReader and HttpProxy (unless inline data is provided).</b>
14330  * @cfg {Array} fields An array of field definition objects, or field name strings.
14331  * @constructor
14332  * @param {Object} config
14333  */
14334 Roo.data.JsonStore = function(c){
14335     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14336         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14337         reader: new Roo.data.JsonReader(c, c.fields)
14338     }));
14339 };
14340 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14341  * Based on:
14342  * Ext JS Library 1.1.1
14343  * Copyright(c) 2006-2007, Ext JS, LLC.
14344  *
14345  * Originally Released Under LGPL - original licence link has changed is not relivant.
14346  *
14347  * Fork - LGPL
14348  * <script type="text/javascript">
14349  */
14350
14351  
14352 Roo.data.Field = function(config){
14353     if(typeof config == "string"){
14354         config = {name: config};
14355     }
14356     Roo.apply(this, config);
14357     
14358     if(!this.type){
14359         this.type = "auto";
14360     }
14361     
14362     var st = Roo.data.SortTypes;
14363     // named sortTypes are supported, here we look them up
14364     if(typeof this.sortType == "string"){
14365         this.sortType = st[this.sortType];
14366     }
14367     
14368     // set default sortType for strings and dates
14369     if(!this.sortType){
14370         switch(this.type){
14371             case "string":
14372                 this.sortType = st.asUCString;
14373                 break;
14374             case "date":
14375                 this.sortType = st.asDate;
14376                 break;
14377             default:
14378                 this.sortType = st.none;
14379         }
14380     }
14381
14382     // define once
14383     var stripRe = /[\$,%]/g;
14384
14385     // prebuilt conversion function for this field, instead of
14386     // switching every time we're reading a value
14387     if(!this.convert){
14388         var cv, dateFormat = this.dateFormat;
14389         switch(this.type){
14390             case "":
14391             case "auto":
14392             case undefined:
14393                 cv = function(v){ return v; };
14394                 break;
14395             case "string":
14396                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14397                 break;
14398             case "int":
14399                 cv = function(v){
14400                     return v !== undefined && v !== null && v !== '' ?
14401                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14402                     };
14403                 break;
14404             case "float":
14405                 cv = function(v){
14406                     return v !== undefined && v !== null && v !== '' ?
14407                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14408                     };
14409                 break;
14410             case "bool":
14411             case "boolean":
14412                 cv = function(v){ return v === true || v === "true" || v == 1; };
14413                 break;
14414             case "date":
14415                 cv = function(v){
14416                     if(!v){
14417                         return '';
14418                     }
14419                     if(v instanceof Date){
14420                         return v;
14421                     }
14422                     if(dateFormat){
14423                         if(dateFormat == "timestamp"){
14424                             return new Date(v*1000);
14425                         }
14426                         return Date.parseDate(v, dateFormat);
14427                     }
14428                     var parsed = Date.parse(v);
14429                     return parsed ? new Date(parsed) : null;
14430                 };
14431              break;
14432             
14433         }
14434         this.convert = cv;
14435     }
14436 };
14437
14438 Roo.data.Field.prototype = {
14439     dateFormat: null,
14440     defaultValue: "",
14441     mapping: null,
14442     sortType : null,
14443     sortDir : "ASC"
14444 };/*
14445  * Based on:
14446  * Ext JS Library 1.1.1
14447  * Copyright(c) 2006-2007, Ext JS, LLC.
14448  *
14449  * Originally Released Under LGPL - original licence link has changed is not relivant.
14450  *
14451  * Fork - LGPL
14452  * <script type="text/javascript">
14453  */
14454  
14455 // Base class for reading structured data from a data source.  This class is intended to be
14456 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14457
14458 /**
14459  * @class Roo.data.DataReader
14460  * Base class for reading structured data from a data source.  This class is intended to be
14461  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14462  */
14463
14464 Roo.data.DataReader = function(meta, recordType){
14465     
14466     this.meta = meta;
14467     
14468     this.recordType = recordType instanceof Array ? 
14469         Roo.data.Record.create(recordType) : recordType;
14470 };
14471
14472 Roo.data.DataReader.prototype = {
14473     
14474     
14475     readerType : 'Data',
14476      /**
14477      * Create an empty record
14478      * @param {Object} data (optional) - overlay some values
14479      * @return {Roo.data.Record} record created.
14480      */
14481     newRow :  function(d) {
14482         var da =  {};
14483         this.recordType.prototype.fields.each(function(c) {
14484             switch( c.type) {
14485                 case 'int' : da[c.name] = 0; break;
14486                 case 'date' : da[c.name] = new Date(); break;
14487                 case 'float' : da[c.name] = 0.0; break;
14488                 case 'boolean' : da[c.name] = false; break;
14489                 default : da[c.name] = ""; break;
14490             }
14491             
14492         });
14493         return new this.recordType(Roo.apply(da, d));
14494     }
14495     
14496     
14497 };/*
14498  * Based on:
14499  * Ext JS Library 1.1.1
14500  * Copyright(c) 2006-2007, Ext JS, LLC.
14501  *
14502  * Originally Released Under LGPL - original licence link has changed is not relivant.
14503  *
14504  * Fork - LGPL
14505  * <script type="text/javascript">
14506  */
14507
14508 /**
14509  * @class Roo.data.DataProxy
14510  * @extends Roo.data.Observable
14511  * This class is an abstract base class for implementations which provide retrieval of
14512  * unformatted data objects.<br>
14513  * <p>
14514  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14515  * (of the appropriate type which knows how to parse the data object) to provide a block of
14516  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14517  * <p>
14518  * Custom implementations must implement the load method as described in
14519  * {@link Roo.data.HttpProxy#load}.
14520  */
14521 Roo.data.DataProxy = function(){
14522     this.addEvents({
14523         /**
14524          * @event beforeload
14525          * Fires before a network request is made to retrieve a data object.
14526          * @param {Object} This DataProxy object.
14527          * @param {Object} params The params parameter to the load function.
14528          */
14529         beforeload : true,
14530         /**
14531          * @event load
14532          * Fires before the load method's callback is called.
14533          * @param {Object} This DataProxy object.
14534          * @param {Object} o The data object.
14535          * @param {Object} arg The callback argument object passed to the load function.
14536          */
14537         load : true,
14538         /**
14539          * @event loadexception
14540          * Fires if an Exception occurs during data retrieval.
14541          * @param {Object} This DataProxy object.
14542          * @param {Object} o The data object.
14543          * @param {Object} arg The callback argument object passed to the load function.
14544          * @param {Object} e The Exception.
14545          */
14546         loadexception : true
14547     });
14548     Roo.data.DataProxy.superclass.constructor.call(this);
14549 };
14550
14551 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14552
14553     /**
14554      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14555      */
14556 /*
14557  * Based on:
14558  * Ext JS Library 1.1.1
14559  * Copyright(c) 2006-2007, Ext JS, LLC.
14560  *
14561  * Originally Released Under LGPL - original licence link has changed is not relivant.
14562  *
14563  * Fork - LGPL
14564  * <script type="text/javascript">
14565  */
14566 /**
14567  * @class Roo.data.MemoryProxy
14568  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14569  * to the Reader when its load method is called.
14570  * @constructor
14571  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14572  */
14573 Roo.data.MemoryProxy = function(data){
14574     if (data.data) {
14575         data = data.data;
14576     }
14577     Roo.data.MemoryProxy.superclass.constructor.call(this);
14578     this.data = data;
14579 };
14580
14581 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14582     
14583     /**
14584      * Load data from the requested source (in this case an in-memory
14585      * data object passed to the constructor), read the data object into
14586      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14587      * process that block using the passed callback.
14588      * @param {Object} params This parameter is not used by the MemoryProxy class.
14589      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14590      * object into a block of Roo.data.Records.
14591      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14592      * The function must be passed <ul>
14593      * <li>The Record block object</li>
14594      * <li>The "arg" argument from the load function</li>
14595      * <li>A boolean success indicator</li>
14596      * </ul>
14597      * @param {Object} scope The scope in which to call the callback
14598      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14599      */
14600     load : function(params, reader, callback, scope, arg){
14601         params = params || {};
14602         var result;
14603         try {
14604             result = reader.readRecords(params.data ? params.data :this.data);
14605         }catch(e){
14606             this.fireEvent("loadexception", this, arg, null, e);
14607             callback.call(scope, null, arg, false);
14608             return;
14609         }
14610         callback.call(scope, result, arg, true);
14611     },
14612     
14613     // private
14614     update : function(params, records){
14615         
14616     }
14617 });/*
14618  * Based on:
14619  * Ext JS Library 1.1.1
14620  * Copyright(c) 2006-2007, Ext JS, LLC.
14621  *
14622  * Originally Released Under LGPL - original licence link has changed is not relivant.
14623  *
14624  * Fork - LGPL
14625  * <script type="text/javascript">
14626  */
14627 /**
14628  * @class Roo.data.HttpProxy
14629  * @extends Roo.data.DataProxy
14630  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14631  * configured to reference a certain URL.<br><br>
14632  * <p>
14633  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14634  * from which the running page was served.<br><br>
14635  * <p>
14636  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14637  * <p>
14638  * Be aware that to enable the browser to parse an XML document, the server must set
14639  * the Content-Type header in the HTTP response to "text/xml".
14640  * @constructor
14641  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14642  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14643  * will be used to make the request.
14644  */
14645 Roo.data.HttpProxy = function(conn){
14646     Roo.data.HttpProxy.superclass.constructor.call(this);
14647     // is conn a conn config or a real conn?
14648     this.conn = conn;
14649     this.useAjax = !conn || !conn.events;
14650   
14651 };
14652
14653 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14654     // thse are take from connection...
14655     
14656     /**
14657      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14658      */
14659     /**
14660      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14661      * extra parameters to each request made by this object. (defaults to undefined)
14662      */
14663     /**
14664      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14665      *  to each request made by this object. (defaults to undefined)
14666      */
14667     /**
14668      * @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)
14669      */
14670     /**
14671      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14672      */
14673      /**
14674      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14675      * @type Boolean
14676      */
14677   
14678
14679     /**
14680      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14681      * @type Boolean
14682      */
14683     /**
14684      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14685      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14686      * a finer-grained basis than the DataProxy events.
14687      */
14688     getConnection : function(){
14689         return this.useAjax ? Roo.Ajax : this.conn;
14690     },
14691
14692     /**
14693      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14694      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14695      * process that block using the passed callback.
14696      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14697      * for the request to the remote server.
14698      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14699      * object into a block of Roo.data.Records.
14700      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14701      * The function must be passed <ul>
14702      * <li>The Record block object</li>
14703      * <li>The "arg" argument from the load function</li>
14704      * <li>A boolean success indicator</li>
14705      * </ul>
14706      * @param {Object} scope The scope in which to call the callback
14707      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14708      */
14709     load : function(params, reader, callback, scope, arg){
14710         if(this.fireEvent("beforeload", this, params) !== false){
14711             var  o = {
14712                 params : params || {},
14713                 request: {
14714                     callback : callback,
14715                     scope : scope,
14716                     arg : arg
14717                 },
14718                 reader: reader,
14719                 callback : this.loadResponse,
14720                 scope: this
14721             };
14722             if(this.useAjax){
14723                 Roo.applyIf(o, this.conn);
14724                 if(this.activeRequest){
14725                     Roo.Ajax.abort(this.activeRequest);
14726                 }
14727                 this.activeRequest = Roo.Ajax.request(o);
14728             }else{
14729                 this.conn.request(o);
14730             }
14731         }else{
14732             callback.call(scope||this, null, arg, false);
14733         }
14734     },
14735
14736     // private
14737     loadResponse : function(o, success, response){
14738         delete this.activeRequest;
14739         if(!success){
14740             this.fireEvent("loadexception", this, o, response);
14741             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14742             return;
14743         }
14744         var result;
14745         try {
14746             result = o.reader.read(response);
14747         }catch(e){
14748             this.fireEvent("loadexception", this, o, response, e);
14749             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14750             return;
14751         }
14752         
14753         this.fireEvent("load", this, o, o.request.arg);
14754         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14755     },
14756
14757     // private
14758     update : function(dataSet){
14759
14760     },
14761
14762     // private
14763     updateResponse : function(dataSet){
14764
14765     }
14766 });/*
14767  * Based on:
14768  * Ext JS Library 1.1.1
14769  * Copyright(c) 2006-2007, Ext JS, LLC.
14770  *
14771  * Originally Released Under LGPL - original licence link has changed is not relivant.
14772  *
14773  * Fork - LGPL
14774  * <script type="text/javascript">
14775  */
14776
14777 /**
14778  * @class Roo.data.ScriptTagProxy
14779  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14780  * other than the originating domain of the running page.<br><br>
14781  * <p>
14782  * <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
14783  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14784  * <p>
14785  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14786  * source code that is used as the source inside a &lt;script> tag.<br><br>
14787  * <p>
14788  * In order for the browser to process the returned data, the server must wrap the data object
14789  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14790  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14791  * depending on whether the callback name was passed:
14792  * <p>
14793  * <pre><code>
14794 boolean scriptTag = false;
14795 String cb = request.getParameter("callback");
14796 if (cb != null) {
14797     scriptTag = true;
14798     response.setContentType("text/javascript");
14799 } else {
14800     response.setContentType("application/x-json");
14801 }
14802 Writer out = response.getWriter();
14803 if (scriptTag) {
14804     out.write(cb + "(");
14805 }
14806 out.print(dataBlock.toJsonString());
14807 if (scriptTag) {
14808     out.write(");");
14809 }
14810 </pre></code>
14811  *
14812  * @constructor
14813  * @param {Object} config A configuration object.
14814  */
14815 Roo.data.ScriptTagProxy = function(config){
14816     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14817     Roo.apply(this, config);
14818     this.head = document.getElementsByTagName("head")[0];
14819 };
14820
14821 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14822
14823 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14824     /**
14825      * @cfg {String} url The URL from which to request the data object.
14826      */
14827     /**
14828      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14829      */
14830     timeout : 30000,
14831     /**
14832      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14833      * the server the name of the callback function set up by the load call to process the returned data object.
14834      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14835      * javascript output which calls this named function passing the data object as its only parameter.
14836      */
14837     callbackParam : "callback",
14838     /**
14839      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14840      * name to the request.
14841      */
14842     nocache : true,
14843
14844     /**
14845      * Load data from the configured URL, read the data object into
14846      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14847      * process that block using the passed callback.
14848      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14849      * for the request to the remote server.
14850      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14851      * object into a block of Roo.data.Records.
14852      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14853      * The function must be passed <ul>
14854      * <li>The Record block object</li>
14855      * <li>The "arg" argument from the load function</li>
14856      * <li>A boolean success indicator</li>
14857      * </ul>
14858      * @param {Object} scope The scope in which to call the callback
14859      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14860      */
14861     load : function(params, reader, callback, scope, arg){
14862         if(this.fireEvent("beforeload", this, params) !== false){
14863
14864             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14865
14866             var url = this.url;
14867             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14868             if(this.nocache){
14869                 url += "&_dc=" + (new Date().getTime());
14870             }
14871             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14872             var trans = {
14873                 id : transId,
14874                 cb : "stcCallback"+transId,
14875                 scriptId : "stcScript"+transId,
14876                 params : params,
14877                 arg : arg,
14878                 url : url,
14879                 callback : callback,
14880                 scope : scope,
14881                 reader : reader
14882             };
14883             var conn = this;
14884
14885             window[trans.cb] = function(o){
14886                 conn.handleResponse(o, trans);
14887             };
14888
14889             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14890
14891             if(this.autoAbort !== false){
14892                 this.abort();
14893             }
14894
14895             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14896
14897             var script = document.createElement("script");
14898             script.setAttribute("src", url);
14899             script.setAttribute("type", "text/javascript");
14900             script.setAttribute("id", trans.scriptId);
14901             this.head.appendChild(script);
14902
14903             this.trans = trans;
14904         }else{
14905             callback.call(scope||this, null, arg, false);
14906         }
14907     },
14908
14909     // private
14910     isLoading : function(){
14911         return this.trans ? true : false;
14912     },
14913
14914     /**
14915      * Abort the current server request.
14916      */
14917     abort : function(){
14918         if(this.isLoading()){
14919             this.destroyTrans(this.trans);
14920         }
14921     },
14922
14923     // private
14924     destroyTrans : function(trans, isLoaded){
14925         this.head.removeChild(document.getElementById(trans.scriptId));
14926         clearTimeout(trans.timeoutId);
14927         if(isLoaded){
14928             window[trans.cb] = undefined;
14929             try{
14930                 delete window[trans.cb];
14931             }catch(e){}
14932         }else{
14933             // if hasn't been loaded, wait for load to remove it to prevent script error
14934             window[trans.cb] = function(){
14935                 window[trans.cb] = undefined;
14936                 try{
14937                     delete window[trans.cb];
14938                 }catch(e){}
14939             };
14940         }
14941     },
14942
14943     // private
14944     handleResponse : function(o, trans){
14945         this.trans = false;
14946         this.destroyTrans(trans, true);
14947         var result;
14948         try {
14949             result = trans.reader.readRecords(o);
14950         }catch(e){
14951             this.fireEvent("loadexception", this, o, trans.arg, e);
14952             trans.callback.call(trans.scope||window, null, trans.arg, false);
14953             return;
14954         }
14955         this.fireEvent("load", this, o, trans.arg);
14956         trans.callback.call(trans.scope||window, result, trans.arg, true);
14957     },
14958
14959     // private
14960     handleFailure : function(trans){
14961         this.trans = false;
14962         this.destroyTrans(trans, false);
14963         this.fireEvent("loadexception", this, null, trans.arg);
14964         trans.callback.call(trans.scope||window, null, trans.arg, false);
14965     }
14966 });/*
14967  * Based on:
14968  * Ext JS Library 1.1.1
14969  * Copyright(c) 2006-2007, Ext JS, LLC.
14970  *
14971  * Originally Released Under LGPL - original licence link has changed is not relivant.
14972  *
14973  * Fork - LGPL
14974  * <script type="text/javascript">
14975  */
14976
14977 /**
14978  * @class Roo.data.JsonReader
14979  * @extends Roo.data.DataReader
14980  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14981  * based on mappings in a provided Roo.data.Record constructor.
14982  * 
14983  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14984  * in the reply previously. 
14985  * 
14986  * <p>
14987  * Example code:
14988  * <pre><code>
14989 var RecordDef = Roo.data.Record.create([
14990     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14991     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14992 ]);
14993 var myReader = new Roo.data.JsonReader({
14994     totalProperty: "results",    // The property which contains the total dataset size (optional)
14995     root: "rows",                // The property which contains an Array of row objects
14996     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14997 }, RecordDef);
14998 </code></pre>
14999  * <p>
15000  * This would consume a JSON file like this:
15001  * <pre><code>
15002 { 'results': 2, 'rows': [
15003     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
15004     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
15005 }
15006 </code></pre>
15007  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
15008  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
15009  * paged from the remote server.
15010  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
15011  * @cfg {String} root name of the property which contains the Array of row objects.
15012  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15013  * @cfg {Array} fields Array of field definition objects
15014  * @constructor
15015  * Create a new JsonReader
15016  * @param {Object} meta Metadata configuration options
15017  * @param {Object} recordType Either an Array of field definition objects,
15018  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
15019  */
15020 Roo.data.JsonReader = function(meta, recordType){
15021     
15022     meta = meta || {};
15023     // set some defaults:
15024     Roo.applyIf(meta, {
15025         totalProperty: 'total',
15026         successProperty : 'success',
15027         root : 'data',
15028         id : 'id'
15029     });
15030     
15031     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15032 };
15033 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
15034     
15035     readerType : 'Json',
15036     
15037     /**
15038      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
15039      * Used by Store query builder to append _requestMeta to params.
15040      * 
15041      */
15042     metaFromRemote : false,
15043     /**
15044      * This method is only used by a DataProxy which has retrieved data from a remote server.
15045      * @param {Object} response The XHR object which contains the JSON data in its responseText.
15046      * @return {Object} data A data block which is used by an Roo.data.Store object as
15047      * a cache of Roo.data.Records.
15048      */
15049     read : function(response){
15050         var json = response.responseText;
15051        
15052         var o = /* eval:var:o */ eval("("+json+")");
15053         if(!o) {
15054             throw {message: "JsonReader.read: Json object not found"};
15055         }
15056         
15057         if(o.metaData){
15058             
15059             delete this.ef;
15060             this.metaFromRemote = true;
15061             this.meta = o.metaData;
15062             this.recordType = Roo.data.Record.create(o.metaData.fields);
15063             this.onMetaChange(this.meta, this.recordType, o);
15064         }
15065         return this.readRecords(o);
15066     },
15067
15068     // private function a store will implement
15069     onMetaChange : function(meta, recordType, o){
15070
15071     },
15072
15073     /**
15074          * @ignore
15075          */
15076     simpleAccess: function(obj, subsc) {
15077         return obj[subsc];
15078     },
15079
15080         /**
15081          * @ignore
15082          */
15083     getJsonAccessor: function(){
15084         var re = /[\[\.]/;
15085         return function(expr) {
15086             try {
15087                 return(re.test(expr))
15088                     ? new Function("obj", "return obj." + expr)
15089                     : function(obj){
15090                         return obj[expr];
15091                     };
15092             } catch(e){}
15093             return Roo.emptyFn;
15094         };
15095     }(),
15096
15097     /**
15098      * Create a data block containing Roo.data.Records from an XML document.
15099      * @param {Object} o An object which contains an Array of row objects in the property specified
15100      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
15101      * which contains the total size of the dataset.
15102      * @return {Object} data A data block which is used by an Roo.data.Store object as
15103      * a cache of Roo.data.Records.
15104      */
15105     readRecords : function(o){
15106         /**
15107          * After any data loads, the raw JSON data is available for further custom processing.
15108          * @type Object
15109          */
15110         this.o = o;
15111         var s = this.meta, Record = this.recordType,
15112             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
15113
15114 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
15115         if (!this.ef) {
15116             if(s.totalProperty) {
15117                     this.getTotal = this.getJsonAccessor(s.totalProperty);
15118                 }
15119                 if(s.successProperty) {
15120                     this.getSuccess = this.getJsonAccessor(s.successProperty);
15121                 }
15122                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
15123                 if (s.id) {
15124                         var g = this.getJsonAccessor(s.id);
15125                         this.getId = function(rec) {
15126                                 var r = g(rec);  
15127                                 return (r === undefined || r === "") ? null : r;
15128                         };
15129                 } else {
15130                         this.getId = function(){return null;};
15131                 }
15132             this.ef = [];
15133             for(var jj = 0; jj < fl; jj++){
15134                 f = fi[jj];
15135                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
15136                 this.ef[jj] = this.getJsonAccessor(map);
15137             }
15138         }
15139
15140         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
15141         if(s.totalProperty){
15142             var vt = parseInt(this.getTotal(o), 10);
15143             if(!isNaN(vt)){
15144                 totalRecords = vt;
15145             }
15146         }
15147         if(s.successProperty){
15148             var vs = this.getSuccess(o);
15149             if(vs === false || vs === 'false'){
15150                 success = false;
15151             }
15152         }
15153         var records = [];
15154         for(var i = 0; i < c; i++){
15155                 var n = root[i];
15156             var values = {};
15157             var id = this.getId(n);
15158             for(var j = 0; j < fl; j++){
15159                 f = fi[j];
15160             var v = this.ef[j](n);
15161             if (!f.convert) {
15162                 Roo.log('missing convert for ' + f.name);
15163                 Roo.log(f);
15164                 continue;
15165             }
15166             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
15167             }
15168             var record = new Record(values, id);
15169             record.json = n;
15170             records[i] = record;
15171         }
15172         return {
15173             raw : o,
15174             success : success,
15175             records : records,
15176             totalRecords : totalRecords
15177         };
15178     },
15179     // used when loading children.. @see loadDataFromChildren
15180     toLoadData: function(rec)
15181     {
15182         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15183         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15184         return { data : data, total : data.length };
15185         
15186     }
15187 });/*
15188  * Based on:
15189  * Ext JS Library 1.1.1
15190  * Copyright(c) 2006-2007, Ext JS, LLC.
15191  *
15192  * Originally Released Under LGPL - original licence link has changed is not relivant.
15193  *
15194  * Fork - LGPL
15195  * <script type="text/javascript">
15196  */
15197
15198 /**
15199  * @class Roo.data.ArrayReader
15200  * @extends Roo.data.DataReader
15201  * Data reader class to create an Array of Roo.data.Record objects from an Array.
15202  * Each element of that Array represents a row of data fields. The
15203  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
15204  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
15205  * <p>
15206  * Example code:.
15207  * <pre><code>
15208 var RecordDef = Roo.data.Record.create([
15209     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
15210     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
15211 ]);
15212 var myReader = new Roo.data.ArrayReader({
15213     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
15214 }, RecordDef);
15215 </code></pre>
15216  * <p>
15217  * This would consume an Array like this:
15218  * <pre><code>
15219 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
15220   </code></pre>
15221  
15222  * @constructor
15223  * Create a new JsonReader
15224  * @param {Object} meta Metadata configuration options.
15225  * @param {Object|Array} recordType Either an Array of field definition objects
15226  * 
15227  * @cfg {Array} fields Array of field definition objects
15228  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
15229  * as specified to {@link Roo.data.Record#create},
15230  * or an {@link Roo.data.Record} object
15231  *
15232  * 
15233  * created using {@link Roo.data.Record#create}.
15234  */
15235 Roo.data.ArrayReader = function(meta, recordType)
15236 {    
15237     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
15238 };
15239
15240 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
15241     
15242       /**
15243      * Create a data block containing Roo.data.Records from an XML document.
15244      * @param {Object} o An Array of row objects which represents the dataset.
15245      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
15246      * a cache of Roo.data.Records.
15247      */
15248     readRecords : function(o)
15249     {
15250         var sid = this.meta ? this.meta.id : null;
15251         var recordType = this.recordType, fields = recordType.prototype.fields;
15252         var records = [];
15253         var root = o;
15254         for(var i = 0; i < root.length; i++){
15255             var n = root[i];
15256             var values = {};
15257             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
15258             for(var j = 0, jlen = fields.length; j < jlen; j++){
15259                 var f = fields.items[j];
15260                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
15261                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
15262                 v = f.convert(v);
15263                 values[f.name] = v;
15264             }
15265             var record = new recordType(values, id);
15266             record.json = n;
15267             records[records.length] = record;
15268         }
15269         return {
15270             records : records,
15271             totalRecords : records.length
15272         };
15273     },
15274     // used when loading children.. @see loadDataFromChildren
15275     toLoadData: function(rec)
15276     {
15277         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
15278         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
15279         
15280     }
15281     
15282     
15283 });/*
15284  * - LGPL
15285  * * 
15286  */
15287
15288 /**
15289  * @class Roo.bootstrap.ComboBox
15290  * @extends Roo.bootstrap.TriggerField
15291  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
15292  * @cfg {Boolean} append (true|false) default false
15293  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
15294  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
15295  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15296  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15297  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15298  * @cfg {Boolean} animate default true
15299  * @cfg {Boolean} emptyResultText only for touch device
15300  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15301  * @cfg {String} emptyTitle default ''
15302  * @cfg {Number} width fixed with? experimental
15303  * @constructor
15304  * Create a new ComboBox.
15305  * @param {Object} config Configuration options
15306  */
15307 Roo.bootstrap.ComboBox = function(config){
15308     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15309     this.addEvents({
15310         /**
15311          * @event expand
15312          * Fires when the dropdown list is expanded
15313         * @param {Roo.bootstrap.ComboBox} combo This combo box
15314         */
15315         'expand' : true,
15316         /**
15317          * @event collapse
15318          * Fires when the dropdown list is collapsed
15319         * @param {Roo.bootstrap.ComboBox} combo This combo box
15320         */
15321         'collapse' : true,
15322         /**
15323          * @event beforeselect
15324          * Fires before a list item is selected. Return false to cancel the selection.
15325         * @param {Roo.bootstrap.ComboBox} combo This combo box
15326         * @param {Roo.data.Record} record The data record returned from the underlying store
15327         * @param {Number} index The index of the selected item in the dropdown list
15328         */
15329         'beforeselect' : true,
15330         /**
15331          * @event select
15332          * Fires when a list item is selected
15333         * @param {Roo.bootstrap.ComboBox} combo This combo box
15334         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15335         * @param {Number} index The index of the selected item in the dropdown list
15336         */
15337         'select' : true,
15338         /**
15339          * @event beforequery
15340          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15341          * The event object passed has these properties:
15342         * @param {Roo.bootstrap.ComboBox} combo This combo box
15343         * @param {String} query The query
15344         * @param {Boolean} forceAll true to force "all" query
15345         * @param {Boolean} cancel true to cancel the query
15346         * @param {Object} e The query event object
15347         */
15348         'beforequery': true,
15349          /**
15350          * @event add
15351          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15352         * @param {Roo.bootstrap.ComboBox} combo This combo box
15353         */
15354         'add' : true,
15355         /**
15356          * @event edit
15357          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15358         * @param {Roo.bootstrap.ComboBox} combo This combo box
15359         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15360         */
15361         'edit' : true,
15362         /**
15363          * @event remove
15364          * Fires when the remove value from the combobox array
15365         * @param {Roo.bootstrap.ComboBox} combo This combo box
15366         */
15367         'remove' : true,
15368         /**
15369          * @event afterremove
15370          * Fires when the remove value from the combobox array
15371         * @param {Roo.bootstrap.ComboBox} combo This combo box
15372         */
15373         'afterremove' : true,
15374         /**
15375          * @event specialfilter
15376          * Fires when specialfilter
15377             * @param {Roo.bootstrap.ComboBox} combo This combo box
15378             */
15379         'specialfilter' : true,
15380         /**
15381          * @event tick
15382          * Fires when tick the element
15383             * @param {Roo.bootstrap.ComboBox} combo This combo box
15384             */
15385         'tick' : true,
15386         /**
15387          * @event touchviewdisplay
15388          * Fires when touch view require special display (default is using displayField)
15389             * @param {Roo.bootstrap.ComboBox} combo This combo box
15390             * @param {Object} cfg set html .
15391             */
15392         'touchviewdisplay' : true
15393         
15394     });
15395     
15396     this.item = [];
15397     this.tickItems = [];
15398     
15399     this.selectedIndex = -1;
15400     if(this.mode == 'local'){
15401         if(config.queryDelay === undefined){
15402             this.queryDelay = 10;
15403         }
15404         if(config.minChars === undefined){
15405             this.minChars = 0;
15406         }
15407     }
15408 };
15409
15410 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15411      
15412     /**
15413      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15414      * rendering into an Roo.Editor, defaults to false)
15415      */
15416     /**
15417      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15418      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15419      */
15420     /**
15421      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15422      */
15423     /**
15424      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15425      * the dropdown list (defaults to undefined, with no header element)
15426      */
15427
15428      /**
15429      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15430      */
15431      
15432      /**
15433      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15434      */
15435     listWidth: undefined,
15436     /**
15437      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15438      * mode = 'remote' or 'text' if mode = 'local')
15439      */
15440     displayField: undefined,
15441     
15442     /**
15443      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15444      * mode = 'remote' or 'value' if mode = 'local'). 
15445      * Note: use of a valueField requires the user make a selection
15446      * in order for a value to be mapped.
15447      */
15448     valueField: undefined,
15449     /**
15450      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15451      */
15452     modalTitle : '',
15453     
15454     /**
15455      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15456      * field's data value (defaults to the underlying DOM element's name)
15457      */
15458     hiddenName: undefined,
15459     /**
15460      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15461      */
15462     listClass: '',
15463     /**
15464      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15465      */
15466     selectedClass: 'active',
15467     
15468     /**
15469      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15470      */
15471     shadow:'sides',
15472     /**
15473      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15474      * anchor positions (defaults to 'tl-bl')
15475      */
15476     listAlign: 'tl-bl?',
15477     /**
15478      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15479      */
15480     maxHeight: 300,
15481     /**
15482      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15483      * query specified by the allQuery config option (defaults to 'query')
15484      */
15485     triggerAction: 'query',
15486     /**
15487      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15488      * (defaults to 4, does not apply if editable = false)
15489      */
15490     minChars : 4,
15491     /**
15492      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15493      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15494      */
15495     typeAhead: false,
15496     /**
15497      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15498      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15499      */
15500     queryDelay: 500,
15501     /**
15502      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15503      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15504      */
15505     pageSize: 0,
15506     /**
15507      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15508      * when editable = true (defaults to false)
15509      */
15510     selectOnFocus:false,
15511     /**
15512      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15513      */
15514     queryParam: 'query',
15515     /**
15516      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15517      * when mode = 'remote' (defaults to 'Loading...')
15518      */
15519     loadingText: 'Loading...',
15520     /**
15521      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15522      */
15523     resizable: false,
15524     /**
15525      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15526      */
15527     handleHeight : 8,
15528     /**
15529      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15530      * traditional select (defaults to true)
15531      */
15532     editable: true,
15533     /**
15534      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15535      */
15536     allQuery: '',
15537     /**
15538      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15539      */
15540     mode: 'remote',
15541     /**
15542      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15543      * listWidth has a higher value)
15544      */
15545     minListWidth : 70,
15546     /**
15547      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15548      * allow the user to set arbitrary text into the field (defaults to false)
15549      */
15550     forceSelection:false,
15551     /**
15552      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15553      * if typeAhead = true (defaults to 250)
15554      */
15555     typeAheadDelay : 250,
15556     /**
15557      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15558      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15559      */
15560     valueNotFoundText : undefined,
15561     /**
15562      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15563      */
15564     blockFocus : false,
15565     
15566     /**
15567      * @cfg {Boolean} disableClear Disable showing of clear button.
15568      */
15569     disableClear : false,
15570     /**
15571      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15572      */
15573     alwaysQuery : false,
15574     
15575     /**
15576      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15577      */
15578     multiple : false,
15579     
15580     /**
15581      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15582      */
15583     invalidClass : "has-warning",
15584     
15585     /**
15586      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15587      */
15588     validClass : "has-success",
15589     
15590     /**
15591      * @cfg {Boolean} specialFilter (true|false) special filter default false
15592      */
15593     specialFilter : false,
15594     
15595     /**
15596      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15597      */
15598     mobileTouchView : true,
15599     
15600     /**
15601      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15602      */
15603     useNativeIOS : false,
15604     
15605     /**
15606      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15607      */
15608     mobile_restrict_height : false,
15609     
15610     ios_options : false,
15611     
15612     //private
15613     addicon : false,
15614     editicon: false,
15615     
15616     page: 0,
15617     hasQuery: false,
15618     append: false,
15619     loadNext: false,
15620     autoFocus : true,
15621     tickable : false,
15622     btnPosition : 'right',
15623     triggerList : true,
15624     showToggleBtn : true,
15625     animate : true,
15626     emptyResultText: 'Empty',
15627     triggerText : 'Select',
15628     emptyTitle : '',
15629     width : false,
15630     
15631     // element that contains real text value.. (when hidden is used..)
15632     
15633     getAutoCreate : function()
15634     {   
15635         var cfg = false;
15636         //render
15637         /*
15638          * Render classic select for iso
15639          */
15640         
15641         if(Roo.isIOS && this.useNativeIOS){
15642             cfg = this.getAutoCreateNativeIOS();
15643             return cfg;
15644         }
15645         
15646         /*
15647          * Touch Devices
15648          */
15649         
15650         if(Roo.isTouch && this.mobileTouchView){
15651             cfg = this.getAutoCreateTouchView();
15652             return cfg;;
15653         }
15654         
15655         /*
15656          *  Normal ComboBox
15657          */
15658         if(!this.tickable){
15659             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15660             return cfg;
15661         }
15662         
15663         /*
15664          *  ComboBox with tickable selections
15665          */
15666              
15667         var align = this.labelAlign || this.parentLabelAlign();
15668         
15669         cfg = {
15670             cls : 'form-group roo-combobox-tickable' //input-group
15671         };
15672         
15673         var btn_text_select = '';
15674         var btn_text_done = '';
15675         var btn_text_cancel = '';
15676         
15677         if (this.btn_text_show) {
15678             btn_text_select = 'Select';
15679             btn_text_done = 'Done';
15680             btn_text_cancel = 'Cancel'; 
15681         }
15682         
15683         var buttons = {
15684             tag : 'div',
15685             cls : 'tickable-buttons',
15686             cn : [
15687                 {
15688                     tag : 'button',
15689                     type : 'button',
15690                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15691                     //html : this.triggerText
15692                     html: btn_text_select
15693                 },
15694                 {
15695                     tag : 'button',
15696                     type : 'button',
15697                     name : 'ok',
15698                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15699                     //html : 'Done'
15700                     html: btn_text_done
15701                 },
15702                 {
15703                     tag : 'button',
15704                     type : 'button',
15705                     name : 'cancel',
15706                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15707                     //html : 'Cancel'
15708                     html: btn_text_cancel
15709                 }
15710             ]
15711         };
15712         
15713         if(this.editable){
15714             buttons.cn.unshift({
15715                 tag: 'input',
15716                 cls: 'roo-select2-search-field-input'
15717             });
15718         }
15719         
15720         var _this = this;
15721         
15722         Roo.each(buttons.cn, function(c){
15723             if (_this.size) {
15724                 c.cls += ' btn-' + _this.size;
15725             }
15726
15727             if (_this.disabled) {
15728                 c.disabled = true;
15729             }
15730         });
15731         
15732         var box = {
15733             tag: 'div',
15734             style : 'display: contents',
15735             cn: [
15736                 {
15737                     tag: 'input',
15738                     type : 'hidden',
15739                     cls: 'form-hidden-field'
15740                 },
15741                 {
15742                     tag: 'ul',
15743                     cls: 'roo-select2-choices',
15744                     cn:[
15745                         {
15746                             tag: 'li',
15747                             cls: 'roo-select2-search-field',
15748                             cn: [
15749                                 buttons
15750                             ]
15751                         }
15752                     ]
15753                 }
15754             ]
15755         };
15756         
15757         var combobox = {
15758             cls: 'roo-select2-container input-group roo-select2-container-multi',
15759             cn: [
15760                 
15761                 box
15762 //                {
15763 //                    tag: 'ul',
15764 //                    cls: 'typeahead typeahead-long dropdown-menu',
15765 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15766 //                }
15767             ]
15768         };
15769         
15770         if(this.hasFeedback && !this.allowBlank){
15771             
15772             var feedback = {
15773                 tag: 'span',
15774                 cls: 'glyphicon form-control-feedback'
15775             };
15776
15777             combobox.cn.push(feedback);
15778         }
15779         
15780         
15781         
15782         var indicator = {
15783             tag : 'i',
15784             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15785             tooltip : 'This field is required'
15786         };
15787         if (Roo.bootstrap.version == 4) {
15788             indicator = {
15789                 tag : 'i',
15790                 style : 'display:none'
15791             };
15792         }
15793         if (align ==='left' && this.fieldLabel.length) {
15794             
15795             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15796             
15797             cfg.cn = [
15798                 indicator,
15799                 {
15800                     tag: 'label',
15801                     'for' :  id,
15802                     cls : 'control-label col-form-label',
15803                     html : this.fieldLabel
15804
15805                 },
15806                 {
15807                     cls : "", 
15808                     cn: [
15809                         combobox
15810                     ]
15811                 }
15812
15813             ];
15814             
15815             var labelCfg = cfg.cn[1];
15816             var contentCfg = cfg.cn[2];
15817             
15818
15819             if(this.indicatorpos == 'right'){
15820                 
15821                 cfg.cn = [
15822                     {
15823                         tag: 'label',
15824                         'for' :  id,
15825                         cls : 'control-label col-form-label',
15826                         cn : [
15827                             {
15828                                 tag : 'span',
15829                                 html : this.fieldLabel
15830                             },
15831                             indicator
15832                         ]
15833                     },
15834                     {
15835                         cls : "",
15836                         cn: [
15837                             combobox
15838                         ]
15839                     }
15840
15841                 ];
15842                 
15843                 
15844                 
15845                 labelCfg = cfg.cn[0];
15846                 contentCfg = cfg.cn[1];
15847             
15848             }
15849             
15850             if(this.labelWidth > 12){
15851                 labelCfg.style = "width: " + this.labelWidth + 'px';
15852             }
15853             if(this.width * 1 > 0){
15854                 contentCfg.style = "width: " + this.width + 'px';
15855             }
15856             if(this.labelWidth < 13 && this.labelmd == 0){
15857                 this.labelmd = this.labelWidth;
15858             }
15859             
15860             if(this.labellg > 0){
15861                 labelCfg.cls += ' col-lg-' + this.labellg;
15862                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15863             }
15864             
15865             if(this.labelmd > 0){
15866                 labelCfg.cls += ' col-md-' + this.labelmd;
15867                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15868             }
15869             
15870             if(this.labelsm > 0){
15871                 labelCfg.cls += ' col-sm-' + this.labelsm;
15872                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15873             }
15874             
15875             if(this.labelxs > 0){
15876                 labelCfg.cls += ' col-xs-' + this.labelxs;
15877                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15878             }
15879                 
15880                 
15881         } else if ( this.fieldLabel.length) {
15882 //                Roo.log(" label");
15883                  cfg.cn = [
15884                    indicator,
15885                     {
15886                         tag: 'label',
15887                         //cls : 'input-group-addon',
15888                         html : this.fieldLabel
15889                     },
15890                     combobox
15891                 ];
15892                 
15893                 if(this.indicatorpos == 'right'){
15894                     cfg.cn = [
15895                         {
15896                             tag: 'label',
15897                             //cls : 'input-group-addon',
15898                             html : this.fieldLabel
15899                         },
15900                         indicator,
15901                         combobox
15902                     ];
15903                     
15904                 }
15905
15906         } else {
15907             
15908 //                Roo.log(" no label && no align");
15909                 cfg = combobox
15910                      
15911                 
15912         }
15913          
15914         var settings=this;
15915         ['xs','sm','md','lg'].map(function(size){
15916             if (settings[size]) {
15917                 cfg.cls += ' col-' + size + '-' + settings[size];
15918             }
15919         });
15920         
15921         return cfg;
15922         
15923     },
15924     
15925     _initEventsCalled : false,
15926     
15927     // private
15928     initEvents: function()
15929     {   
15930         if (this._initEventsCalled) { // as we call render... prevent looping...
15931             return;
15932         }
15933         this._initEventsCalled = true;
15934         
15935         if (!this.store) {
15936             throw "can not find store for combo";
15937         }
15938         
15939         this.indicator = this.indicatorEl();
15940         
15941         this.store = Roo.factory(this.store, Roo.data);
15942         this.store.parent = this;
15943         
15944         // if we are building from html. then this element is so complex, that we can not really
15945         // use the rendered HTML.
15946         // so we have to trash and replace the previous code.
15947         if (Roo.XComponent.build_from_html) {
15948             // remove this element....
15949             var e = this.el.dom, k=0;
15950             while (e ) { e = e.previousSibling;  ++k;}
15951
15952             this.el.remove();
15953             
15954             this.el=false;
15955             this.rendered = false;
15956             
15957             this.render(this.parent().getChildContainer(true), k);
15958         }
15959         
15960         if(Roo.isIOS && this.useNativeIOS){
15961             this.initIOSView();
15962             return;
15963         }
15964         
15965         /*
15966          * Touch Devices
15967          */
15968         
15969         if(Roo.isTouch && this.mobileTouchView){
15970             this.initTouchView();
15971             return;
15972         }
15973         
15974         if(this.tickable){
15975             this.initTickableEvents();
15976             return;
15977         }
15978         
15979         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15980         
15981         if(this.hiddenName){
15982             
15983             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15984             
15985             this.hiddenField.dom.value =
15986                 this.hiddenValue !== undefined ? this.hiddenValue :
15987                 this.value !== undefined ? this.value : '';
15988
15989             // prevent input submission
15990             this.el.dom.removeAttribute('name');
15991             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15992              
15993              
15994         }
15995         //if(Roo.isGecko){
15996         //    this.el.dom.setAttribute('autocomplete', 'off');
15997         //}
15998         
15999         var cls = 'x-combo-list';
16000         
16001         //this.list = new Roo.Layer({
16002         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
16003         //});
16004         
16005         var _this = this;
16006         
16007         (function(){
16008             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16009             _this.list.setWidth(lw);
16010         }).defer(100);
16011         
16012         this.list.on('mouseover', this.onViewOver, this);
16013         this.list.on('mousemove', this.onViewMove, this);
16014         this.list.on('scroll', this.onViewScroll, this);
16015         
16016         /*
16017         this.list.swallowEvent('mousewheel');
16018         this.assetHeight = 0;
16019
16020         if(this.title){
16021             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
16022             this.assetHeight += this.header.getHeight();
16023         }
16024
16025         this.innerList = this.list.createChild({cls:cls+'-inner'});
16026         this.innerList.on('mouseover', this.onViewOver, this);
16027         this.innerList.on('mousemove', this.onViewMove, this);
16028         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16029         
16030         if(this.allowBlank && !this.pageSize && !this.disableClear){
16031             this.footer = this.list.createChild({cls:cls+'-ft'});
16032             this.pageTb = new Roo.Toolbar(this.footer);
16033            
16034         }
16035         if(this.pageSize){
16036             this.footer = this.list.createChild({cls:cls+'-ft'});
16037             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
16038                     {pageSize: this.pageSize});
16039             
16040         }
16041         
16042         if (this.pageTb && this.allowBlank && !this.disableClear) {
16043             var _this = this;
16044             this.pageTb.add(new Roo.Toolbar.Fill(), {
16045                 cls: 'x-btn-icon x-btn-clear',
16046                 text: '&#160;',
16047                 handler: function()
16048                 {
16049                     _this.collapse();
16050                     _this.clearValue();
16051                     _this.onSelect(false, -1);
16052                 }
16053             });
16054         }
16055         if (this.footer) {
16056             this.assetHeight += this.footer.getHeight();
16057         }
16058         */
16059             
16060         if(!this.tpl){
16061             this.tpl = Roo.bootstrap.version == 4 ?
16062                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
16063                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
16064         }
16065
16066         this.view = new Roo.View(this.list, this.tpl, {
16067             singleSelect:true, store: this.store, selectedClass: this.selectedClass
16068         });
16069         //this.view.wrapEl.setDisplayed(false);
16070         this.view.on('click', this.onViewClick, this);
16071         
16072         
16073         this.store.on('beforeload', this.onBeforeLoad, this);
16074         this.store.on('load', this.onLoad, this);
16075         this.store.on('loadexception', this.onLoadException, this);
16076         /*
16077         if(this.resizable){
16078             this.resizer = new Roo.Resizable(this.list,  {
16079                pinned:true, handles:'se'
16080             });
16081             this.resizer.on('resize', function(r, w, h){
16082                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
16083                 this.listWidth = w;
16084                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
16085                 this.restrictHeight();
16086             }, this);
16087             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
16088         }
16089         */
16090         if(!this.editable){
16091             this.editable = true;
16092             this.setEditable(false);
16093         }
16094         
16095         /*
16096         
16097         if (typeof(this.events.add.listeners) != 'undefined') {
16098             
16099             this.addicon = this.wrap.createChild(
16100                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
16101        
16102             this.addicon.on('click', function(e) {
16103                 this.fireEvent('add', this);
16104             }, this);
16105         }
16106         if (typeof(this.events.edit.listeners) != 'undefined') {
16107             
16108             this.editicon = this.wrap.createChild(
16109                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
16110             if (this.addicon) {
16111                 this.editicon.setStyle('margin-left', '40px');
16112             }
16113             this.editicon.on('click', function(e) {
16114                 
16115                 // we fire even  if inothing is selected..
16116                 this.fireEvent('edit', this, this.lastData );
16117                 
16118             }, this);
16119         }
16120         */
16121         
16122         this.keyNav = new Roo.KeyNav(this.inputEl(), {
16123             "up" : function(e){
16124                 this.inKeyMode = true;
16125                 this.selectPrev();
16126             },
16127
16128             "down" : function(e){
16129                 if(!this.isExpanded()){
16130                     this.onTriggerClick();
16131                 }else{
16132                     this.inKeyMode = true;
16133                     this.selectNext();
16134                 }
16135             },
16136
16137             "enter" : function(e){
16138 //                this.onViewClick();
16139                 //return true;
16140                 this.collapse();
16141                 
16142                 if(this.fireEvent("specialkey", this, e)){
16143                     this.onViewClick(false);
16144                 }
16145                 
16146                 return true;
16147             },
16148
16149             "esc" : function(e){
16150                 this.collapse();
16151             },
16152
16153             "tab" : function(e){
16154                 this.collapse();
16155                 
16156                 if(this.fireEvent("specialkey", this, e)){
16157                     this.onViewClick(false);
16158                 }
16159                 
16160                 return true;
16161             },
16162
16163             scope : this,
16164
16165             doRelay : function(foo, bar, hname){
16166                 if(hname == 'down' || this.scope.isExpanded()){
16167                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16168                 }
16169                 return true;
16170             },
16171
16172             forceKeyDown: true
16173         });
16174         
16175         
16176         this.queryDelay = Math.max(this.queryDelay || 10,
16177                 this.mode == 'local' ? 10 : 250);
16178         
16179         
16180         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16181         
16182         if(this.typeAhead){
16183             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16184         }
16185         if(this.editable !== false){
16186             this.inputEl().on("keyup", this.onKeyUp, this);
16187         }
16188         if(this.forceSelection){
16189             this.inputEl().on('blur', this.doForce, this);
16190         }
16191         
16192         if(this.multiple){
16193             this.choices = this.el.select('ul.roo-select2-choices', true).first();
16194             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16195         }
16196     },
16197     
16198     initTickableEvents: function()
16199     {   
16200         this.createList();
16201         
16202         if(this.hiddenName){
16203             
16204             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
16205             
16206             this.hiddenField.dom.value =
16207                 this.hiddenValue !== undefined ? this.hiddenValue :
16208                 this.value !== undefined ? this.value : '';
16209
16210             // prevent input submission
16211             this.el.dom.removeAttribute('name');
16212             this.hiddenField.dom.setAttribute('name', this.hiddenName);
16213              
16214              
16215         }
16216         
16217 //        this.list = this.el.select('ul.dropdown-menu',true).first();
16218         
16219         this.choices = this.el.select('ul.roo-select2-choices', true).first();
16220         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
16221         if(this.triggerList){
16222             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
16223         }
16224          
16225         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
16226         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
16227         
16228         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
16229         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
16230         
16231         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
16232         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
16233         
16234         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
16235         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
16236         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
16237         
16238         this.okBtn.hide();
16239         this.cancelBtn.hide();
16240         
16241         var _this = this;
16242         
16243         (function(){
16244             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
16245             _this.list.setWidth(lw);
16246         }).defer(100);
16247         
16248         this.list.on('mouseover', this.onViewOver, this);
16249         this.list.on('mousemove', this.onViewMove, this);
16250         
16251         this.list.on('scroll', this.onViewScroll, this);
16252         
16253         if(!this.tpl){
16254             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
16255                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
16256         }
16257
16258         this.view = new Roo.View(this.list, this.tpl, {
16259             singleSelect:true,
16260             tickable:true,
16261             parent:this,
16262             store: this.store,
16263             selectedClass: this.selectedClass
16264         });
16265         
16266         //this.view.wrapEl.setDisplayed(false);
16267         this.view.on('click', this.onViewClick, this);
16268         
16269         
16270         
16271         this.store.on('beforeload', this.onBeforeLoad, this);
16272         this.store.on('load', this.onLoad, this);
16273         this.store.on('loadexception', this.onLoadException, this);
16274         
16275         if(this.editable){
16276             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
16277                 "up" : function(e){
16278                     this.inKeyMode = true;
16279                     this.selectPrev();
16280                 },
16281
16282                 "down" : function(e){
16283                     this.inKeyMode = true;
16284                     this.selectNext();
16285                 },
16286
16287                 "enter" : function(e){
16288                     if(this.fireEvent("specialkey", this, e)){
16289                         this.onViewClick(false);
16290                     }
16291                     
16292                     return true;
16293                 },
16294
16295                 "esc" : function(e){
16296                     this.onTickableFooterButtonClick(e, false, false);
16297                 },
16298
16299                 "tab" : function(e){
16300                     this.fireEvent("specialkey", this, e);
16301                     
16302                     this.onTickableFooterButtonClick(e, false, false);
16303                     
16304                     return true;
16305                 },
16306
16307                 scope : this,
16308
16309                 doRelay : function(e, fn, key){
16310                     if(this.scope.isExpanded()){
16311                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16312                     }
16313                     return true;
16314                 },
16315
16316                 forceKeyDown: true
16317             });
16318         }
16319         
16320         this.queryDelay = Math.max(this.queryDelay || 10,
16321                 this.mode == 'local' ? 10 : 250);
16322         
16323         
16324         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16325         
16326         if(this.typeAhead){
16327             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16328         }
16329         
16330         if(this.editable !== false){
16331             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16332         }
16333         
16334         this.indicator = this.indicatorEl();
16335         
16336         if(this.indicator){
16337             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16338             this.indicator.hide();
16339         }
16340         
16341     },
16342
16343     onDestroy : function(){
16344         if(this.view){
16345             this.view.setStore(null);
16346             this.view.el.removeAllListeners();
16347             this.view.el.remove();
16348             this.view.purgeListeners();
16349         }
16350         if(this.list){
16351             this.list.dom.innerHTML  = '';
16352         }
16353         
16354         if(this.store){
16355             this.store.un('beforeload', this.onBeforeLoad, this);
16356             this.store.un('load', this.onLoad, this);
16357             this.store.un('loadexception', this.onLoadException, this);
16358         }
16359         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16360     },
16361
16362     // private
16363     fireKey : function(e){
16364         if(e.isNavKeyPress() && !this.list.isVisible()){
16365             this.fireEvent("specialkey", this, e);
16366         }
16367     },
16368
16369     // private
16370     onResize: function(w, h)
16371     {
16372         
16373         
16374 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16375 //        
16376 //        if(typeof w != 'number'){
16377 //            // we do not handle it!?!?
16378 //            return;
16379 //        }
16380 //        var tw = this.trigger.getWidth();
16381 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16382 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16383 //        var x = w - tw;
16384 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16385 //            
16386 //        //this.trigger.setStyle('left', x+'px');
16387 //        
16388 //        if(this.list && this.listWidth === undefined){
16389 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16390 //            this.list.setWidth(lw);
16391 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16392 //        }
16393         
16394     
16395         
16396     },
16397
16398     /**
16399      * Allow or prevent the user from directly editing the field text.  If false is passed,
16400      * the user will only be able to select from the items defined in the dropdown list.  This method
16401      * is the runtime equivalent of setting the 'editable' config option at config time.
16402      * @param {Boolean} value True to allow the user to directly edit the field text
16403      */
16404     setEditable : function(value){
16405         if(value == this.editable){
16406             return;
16407         }
16408         this.editable = value;
16409         if(!value){
16410             this.inputEl().dom.setAttribute('readOnly', true);
16411             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16412             this.inputEl().addClass('x-combo-noedit');
16413         }else{
16414             this.inputEl().dom.removeAttribute('readOnly');
16415             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16416             this.inputEl().removeClass('x-combo-noedit');
16417         }
16418     },
16419
16420     // private
16421     
16422     onBeforeLoad : function(combo,opts){
16423         if(!this.hasFocus){
16424             return;
16425         }
16426          if (!opts.add) {
16427             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16428          }
16429         this.restrictHeight();
16430         this.selectedIndex = -1;
16431     },
16432
16433     // private
16434     onLoad : function(){
16435         
16436         this.hasQuery = false;
16437         
16438         if(!this.hasFocus){
16439             return;
16440         }
16441         
16442         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16443             this.loading.hide();
16444         }
16445         
16446         if(this.store.getCount() > 0){
16447             
16448             this.expand();
16449             this.restrictHeight();
16450             if(this.lastQuery == this.allQuery){
16451                 if(this.editable && !this.tickable){
16452                     this.inputEl().dom.select();
16453                 }
16454                 
16455                 if(
16456                     !this.selectByValue(this.value, true) &&
16457                     this.autoFocus && 
16458                     (
16459                         !this.store.lastOptions ||
16460                         typeof(this.store.lastOptions.add) == 'undefined' || 
16461                         this.store.lastOptions.add != true
16462                     )
16463                 ){
16464                     this.select(0, true);
16465                 }
16466             }else{
16467                 if(this.autoFocus){
16468                     this.selectNext();
16469                 }
16470                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16471                     this.taTask.delay(this.typeAheadDelay);
16472                 }
16473             }
16474         }else{
16475             this.onEmptyResults();
16476         }
16477         
16478         //this.el.focus();
16479     },
16480     // private
16481     onLoadException : function()
16482     {
16483         this.hasQuery = false;
16484         
16485         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16486             this.loading.hide();
16487         }
16488         
16489         if(this.tickable && this.editable){
16490             return;
16491         }
16492         
16493         this.collapse();
16494         // only causes errors at present
16495         //Roo.log(this.store.reader.jsonData);
16496         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16497             // fixme
16498             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16499         //}
16500         
16501         
16502     },
16503     // private
16504     onTypeAhead : function(){
16505         if(this.store.getCount() > 0){
16506             var r = this.store.getAt(0);
16507             var newValue = r.data[this.displayField];
16508             var len = newValue.length;
16509             var selStart = this.getRawValue().length;
16510             
16511             if(selStart != len){
16512                 this.setRawValue(newValue);
16513                 this.selectText(selStart, newValue.length);
16514             }
16515         }
16516     },
16517
16518     // private
16519     onSelect : function(record, index){
16520         
16521         if(this.fireEvent('beforeselect', this, record, index) !== false){
16522         
16523             this.setFromData(index > -1 ? record.data : false);
16524             
16525             this.collapse();
16526             this.fireEvent('select', this, record, index);
16527         }
16528     },
16529
16530     /**
16531      * Returns the currently selected field value or empty string if no value is set.
16532      * @return {String} value The selected value
16533      */
16534     getValue : function()
16535     {
16536         if(Roo.isIOS && this.useNativeIOS){
16537             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16538         }
16539         
16540         if(this.multiple){
16541             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16542         }
16543         
16544         if(this.valueField){
16545             return typeof this.value != 'undefined' ? this.value : '';
16546         }else{
16547             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16548         }
16549     },
16550     
16551     getRawValue : function()
16552     {
16553         if(Roo.isIOS && this.useNativeIOS){
16554             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16555         }
16556         
16557         var v = this.inputEl().getValue();
16558         
16559         return v;
16560     },
16561
16562     /**
16563      * Clears any text/value currently set in the field
16564      */
16565     clearValue : function(){
16566         
16567         if(this.hiddenField){
16568             this.hiddenField.dom.value = '';
16569         }
16570         this.value = '';
16571         this.setRawValue('');
16572         this.lastSelectionText = '';
16573         this.lastData = false;
16574         
16575         var close = this.closeTriggerEl();
16576         
16577         if(close){
16578             close.hide();
16579         }
16580         
16581         this.validate();
16582         
16583     },
16584
16585     /**
16586      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16587      * will be displayed in the field.  If the value does not match the data value of an existing item,
16588      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16589      * Otherwise the field will be blank (although the value will still be set).
16590      * @param {String} value The value to match
16591      */
16592     setValue : function(v)
16593     {
16594         if(Roo.isIOS && this.useNativeIOS){
16595             this.setIOSValue(v);
16596             return;
16597         }
16598         
16599         if(this.multiple){
16600             this.syncValue();
16601             return;
16602         }
16603         
16604         var text = v;
16605         if(this.valueField){
16606             var r = this.findRecord(this.valueField, v);
16607             if(r){
16608                 text = r.data[this.displayField];
16609             }else if(this.valueNotFoundText !== undefined){
16610                 text = this.valueNotFoundText;
16611             }
16612         }
16613         this.lastSelectionText = text;
16614         if(this.hiddenField){
16615             this.hiddenField.dom.value = v;
16616         }
16617         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16618         this.value = v;
16619         
16620         var close = this.closeTriggerEl();
16621         
16622         if(close){
16623             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16624         }
16625         
16626         this.validate();
16627     },
16628     /**
16629      * @property {Object} the last set data for the element
16630      */
16631     
16632     lastData : false,
16633     /**
16634      * Sets the value of the field based on a object which is related to the record format for the store.
16635      * @param {Object} value the value to set as. or false on reset?
16636      */
16637     setFromData : function(o){
16638         
16639         if(this.multiple){
16640             this.addItem(o);
16641             return;
16642         }
16643             
16644         var dv = ''; // display value
16645         var vv = ''; // value value..
16646         this.lastData = o;
16647         if (this.displayField) {
16648             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16649         } else {
16650             // this is an error condition!!!
16651             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16652         }
16653         
16654         if(this.valueField){
16655             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16656         }
16657         
16658         var close = this.closeTriggerEl();
16659         
16660         if(close){
16661             if(dv.length || vv * 1 > 0){
16662                 close.show() ;
16663                 this.blockFocus=true;
16664             } else {
16665                 close.hide();
16666             }             
16667         }
16668         
16669         if(this.hiddenField){
16670             this.hiddenField.dom.value = vv;
16671             
16672             this.lastSelectionText = dv;
16673             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16674             this.value = vv;
16675             return;
16676         }
16677         // no hidden field.. - we store the value in 'value', but still display
16678         // display field!!!!
16679         this.lastSelectionText = dv;
16680         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16681         this.value = vv;
16682         
16683         
16684         
16685     },
16686     // private
16687     reset : function(){
16688         // overridden so that last data is reset..
16689         
16690         if(this.multiple){
16691             this.clearItem();
16692             return;
16693         }
16694         
16695         this.setValue(this.originalValue);
16696         //this.clearInvalid();
16697         this.lastData = false;
16698         if (this.view) {
16699             this.view.clearSelections();
16700         }
16701         
16702         this.validate();
16703     },
16704     // private
16705     findRecord : function(prop, value){
16706         var record;
16707         if(this.store.getCount() > 0){
16708             this.store.each(function(r){
16709                 if(r.data[prop] == value){
16710                     record = r;
16711                     return false;
16712                 }
16713                 return true;
16714             });
16715         }
16716         return record;
16717     },
16718     
16719     getName: function()
16720     {
16721         // returns hidden if it's set..
16722         if (!this.rendered) {return ''};
16723         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16724         
16725     },
16726     // private
16727     onViewMove : function(e, t){
16728         this.inKeyMode = false;
16729     },
16730
16731     // private
16732     onViewOver : function(e, t){
16733         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16734             return;
16735         }
16736         var item = this.view.findItemFromChild(t);
16737         
16738         if(item){
16739             var index = this.view.indexOf(item);
16740             this.select(index, false);
16741         }
16742     },
16743
16744     // private
16745     onViewClick : function(view, doFocus, el, e)
16746     {
16747         var index = this.view.getSelectedIndexes()[0];
16748         
16749         var r = this.store.getAt(index);
16750         
16751         if(this.tickable){
16752             
16753             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16754                 return;
16755             }
16756             
16757             var rm = false;
16758             var _this = this;
16759             
16760             Roo.each(this.tickItems, function(v,k){
16761                 
16762                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16763                     Roo.log(v);
16764                     _this.tickItems.splice(k, 1);
16765                     
16766                     if(typeof(e) == 'undefined' && view == false){
16767                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16768                     }
16769                     
16770                     rm = true;
16771                     return;
16772                 }
16773             });
16774             
16775             if(rm){
16776                 return;
16777             }
16778             
16779             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16780                 this.tickItems.push(r.data);
16781             }
16782             
16783             if(typeof(e) == 'undefined' && view == false){
16784                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16785             }
16786                     
16787             return;
16788         }
16789         
16790         if(r){
16791             this.onSelect(r, index);
16792         }
16793         if(doFocus !== false && !this.blockFocus){
16794             this.inputEl().focus();
16795         }
16796     },
16797
16798     // private
16799     restrictHeight : function(){
16800         //this.innerList.dom.style.height = '';
16801         //var inner = this.innerList.dom;
16802         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16803         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16804         //this.list.beginUpdate();
16805         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16806         this.list.alignTo(this.inputEl(), this.listAlign);
16807         this.list.alignTo(this.inputEl(), this.listAlign);
16808         //this.list.endUpdate();
16809     },
16810
16811     // private
16812     onEmptyResults : function(){
16813         
16814         if(this.tickable && this.editable){
16815             this.hasFocus = false;
16816             this.restrictHeight();
16817             return;
16818         }
16819         
16820         this.collapse();
16821     },
16822
16823     /**
16824      * Returns true if the dropdown list is expanded, else false.
16825      */
16826     isExpanded : function(){
16827         return this.list.isVisible();
16828     },
16829
16830     /**
16831      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16832      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16833      * @param {String} value The data value of the item to select
16834      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16835      * selected item if it is not currently in view (defaults to true)
16836      * @return {Boolean} True if the value matched an item in the list, else false
16837      */
16838     selectByValue : function(v, scrollIntoView){
16839         if(v !== undefined && v !== null){
16840             var r = this.findRecord(this.valueField || this.displayField, v);
16841             if(r){
16842                 this.select(this.store.indexOf(r), scrollIntoView);
16843                 return true;
16844             }
16845         }
16846         return false;
16847     },
16848
16849     /**
16850      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16851      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16852      * @param {Number} index The zero-based index of the list item to select
16853      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16854      * selected item if it is not currently in view (defaults to true)
16855      */
16856     select : function(index, scrollIntoView){
16857         this.selectedIndex = index;
16858         this.view.select(index);
16859         if(scrollIntoView !== false){
16860             var el = this.view.getNode(index);
16861             /*
16862              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16863              */
16864             if(el){
16865                 this.list.scrollChildIntoView(el, false);
16866             }
16867         }
16868     },
16869
16870     // private
16871     selectNext : function(){
16872         var ct = this.store.getCount();
16873         if(ct > 0){
16874             if(this.selectedIndex == -1){
16875                 this.select(0);
16876             }else if(this.selectedIndex < ct-1){
16877                 this.select(this.selectedIndex+1);
16878             }
16879         }
16880     },
16881
16882     // private
16883     selectPrev : function(){
16884         var ct = this.store.getCount();
16885         if(ct > 0){
16886             if(this.selectedIndex == -1){
16887                 this.select(0);
16888             }else if(this.selectedIndex != 0){
16889                 this.select(this.selectedIndex-1);
16890             }
16891         }
16892     },
16893
16894     // private
16895     onKeyUp : function(e){
16896         if(this.editable !== false && !e.isSpecialKey()){
16897             this.lastKey = e.getKey();
16898             this.dqTask.delay(this.queryDelay);
16899         }
16900     },
16901
16902     // private
16903     validateBlur : function(){
16904         return !this.list || !this.list.isVisible();   
16905     },
16906
16907     // private
16908     initQuery : function(){
16909         
16910         var v = this.getRawValue();
16911         
16912         if(this.tickable && this.editable){
16913             v = this.tickableInputEl().getValue();
16914         }
16915         
16916         this.doQuery(v);
16917     },
16918
16919     // private
16920     doForce : function(){
16921         if(this.inputEl().dom.value.length > 0){
16922             this.inputEl().dom.value =
16923                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16924              
16925         }
16926     },
16927
16928     /**
16929      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16930      * query allowing the query action to be canceled if needed.
16931      * @param {String} query The SQL query to execute
16932      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16933      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16934      * saved in the current store (defaults to false)
16935      */
16936     doQuery : function(q, forceAll){
16937         
16938         if(q === undefined || q === null){
16939             q = '';
16940         }
16941         var qe = {
16942             query: q,
16943             forceAll: forceAll,
16944             combo: this,
16945             cancel:false
16946         };
16947         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16948             return false;
16949         }
16950         q = qe.query;
16951         
16952         forceAll = qe.forceAll;
16953         if(forceAll === true || (q.length >= this.minChars)){
16954             
16955             this.hasQuery = true;
16956             
16957             if(this.lastQuery != q || this.alwaysQuery){
16958                 this.lastQuery = q;
16959                 if(this.mode == 'local'){
16960                     this.selectedIndex = -1;
16961                     if(forceAll){
16962                         this.store.clearFilter();
16963                     }else{
16964                         
16965                         if(this.specialFilter){
16966                             this.fireEvent('specialfilter', this);
16967                             this.onLoad();
16968                             return;
16969                         }
16970                         
16971                         this.store.filter(this.displayField, q);
16972                     }
16973                     
16974                     this.store.fireEvent("datachanged", this.store);
16975                     
16976                     this.onLoad();
16977                     
16978                     
16979                 }else{
16980                     
16981                     this.store.baseParams[this.queryParam] = q;
16982                     
16983                     var options = {params : this.getParams(q)};
16984                     
16985                     if(this.loadNext){
16986                         options.add = true;
16987                         options.params.start = this.page * this.pageSize;
16988                     }
16989                     
16990                     this.store.load(options);
16991                     
16992                     /*
16993                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16994                      *  we should expand the list on onLoad
16995                      *  so command out it
16996                      */
16997 //                    this.expand();
16998                 }
16999             }else{
17000                 this.selectedIndex = -1;
17001                 this.onLoad();   
17002             }
17003         }
17004         
17005         this.loadNext = false;
17006     },
17007     
17008     // private
17009     getParams : function(q){
17010         var p = {};
17011         //p[this.queryParam] = q;
17012         
17013         if(this.pageSize){
17014             p.start = 0;
17015             p.limit = this.pageSize;
17016         }
17017         return p;
17018     },
17019
17020     /**
17021      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
17022      */
17023     collapse : function(){
17024         if(!this.isExpanded()){
17025             return;
17026         }
17027         
17028         this.list.hide();
17029         
17030         this.hasFocus = false;
17031         
17032         if(this.tickable){
17033             this.okBtn.hide();
17034             this.cancelBtn.hide();
17035             this.trigger.show();
17036             
17037             if(this.editable){
17038                 this.tickableInputEl().dom.value = '';
17039                 this.tickableInputEl().blur();
17040             }
17041             
17042         }
17043         
17044         Roo.get(document).un('mousedown', this.collapseIf, this);
17045         Roo.get(document).un('mousewheel', this.collapseIf, this);
17046         if (!this.editable) {
17047             Roo.get(document).un('keydown', this.listKeyPress, this);
17048         }
17049         this.fireEvent('collapse', this);
17050         
17051         this.validate();
17052     },
17053
17054     // private
17055     collapseIf : function(e){
17056         var in_combo  = e.within(this.el);
17057         var in_list =  e.within(this.list);
17058         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
17059         
17060         if (in_combo || in_list || is_list) {
17061             //e.stopPropagation();
17062             return;
17063         }
17064         
17065         if(this.tickable){
17066             this.onTickableFooterButtonClick(e, false, false);
17067         }
17068
17069         this.collapse();
17070         
17071     },
17072
17073     /**
17074      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
17075      */
17076     expand : function(){
17077        
17078         if(this.isExpanded() || !this.hasFocus){
17079             return;
17080         }
17081         
17082         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
17083         this.list.setWidth(lw);
17084         
17085         Roo.log('expand');
17086         
17087         this.list.show();
17088         
17089         this.restrictHeight();
17090         
17091         if(this.tickable){
17092             
17093             this.tickItems = Roo.apply([], this.item);
17094             
17095             this.okBtn.show();
17096             this.cancelBtn.show();
17097             this.trigger.hide();
17098             
17099             if(this.editable){
17100                 this.tickableInputEl().focus();
17101             }
17102             
17103         }
17104         
17105         Roo.get(document).on('mousedown', this.collapseIf, this);
17106         Roo.get(document).on('mousewheel', this.collapseIf, this);
17107         if (!this.editable) {
17108             Roo.get(document).on('keydown', this.listKeyPress, this);
17109         }
17110         
17111         this.fireEvent('expand', this);
17112     },
17113
17114     // private
17115     // Implements the default empty TriggerField.onTriggerClick function
17116     onTriggerClick : function(e)
17117     {
17118         Roo.log('trigger click');
17119         
17120         if(this.disabled || !this.triggerList){
17121             return;
17122         }
17123         
17124         this.page = 0;
17125         this.loadNext = false;
17126         
17127         if(this.isExpanded()){
17128             this.collapse();
17129             if (!this.blockFocus) {
17130                 this.inputEl().focus();
17131             }
17132             
17133         }else {
17134             this.hasFocus = true;
17135             if(this.triggerAction == 'all') {
17136                 this.doQuery(this.allQuery, true);
17137             } else {
17138                 this.doQuery(this.getRawValue());
17139             }
17140             if (!this.blockFocus) {
17141                 this.inputEl().focus();
17142             }
17143         }
17144     },
17145     
17146     onTickableTriggerClick : function(e)
17147     {
17148         if(this.disabled){
17149             return;
17150         }
17151         
17152         this.page = 0;
17153         this.loadNext = false;
17154         this.hasFocus = true;
17155         
17156         if(this.triggerAction == 'all') {
17157             this.doQuery(this.allQuery, true);
17158         } else {
17159             this.doQuery(this.getRawValue());
17160         }
17161     },
17162     
17163     onSearchFieldClick : function(e)
17164     {
17165         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
17166             this.onTickableFooterButtonClick(e, false, false);
17167             return;
17168         }
17169         
17170         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
17171             return;
17172         }
17173         
17174         this.page = 0;
17175         this.loadNext = false;
17176         this.hasFocus = true;
17177         
17178         if(this.triggerAction == 'all') {
17179             this.doQuery(this.allQuery, true);
17180         } else {
17181             this.doQuery(this.getRawValue());
17182         }
17183     },
17184     
17185     listKeyPress : function(e)
17186     {
17187         //Roo.log('listkeypress');
17188         // scroll to first matching element based on key pres..
17189         if (e.isSpecialKey()) {
17190             return false;
17191         }
17192         var k = String.fromCharCode(e.getKey()).toUpperCase();
17193         //Roo.log(k);
17194         var match  = false;
17195         var csel = this.view.getSelectedNodes();
17196         var cselitem = false;
17197         if (csel.length) {
17198             var ix = this.view.indexOf(csel[0]);
17199             cselitem  = this.store.getAt(ix);
17200             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
17201                 cselitem = false;
17202             }
17203             
17204         }
17205         
17206         this.store.each(function(v) { 
17207             if (cselitem) {
17208                 // start at existing selection.
17209                 if (cselitem.id == v.id) {
17210                     cselitem = false;
17211                 }
17212                 return true;
17213             }
17214                 
17215             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
17216                 match = this.store.indexOf(v);
17217                 return false;
17218             }
17219             return true;
17220         }, this);
17221         
17222         if (match === false) {
17223             return true; // no more action?
17224         }
17225         // scroll to?
17226         this.view.select(match);
17227         var sn = Roo.get(this.view.getSelectedNodes()[0]);
17228         sn.scrollIntoView(sn.dom.parentNode, false);
17229     },
17230     
17231     onViewScroll : function(e, t){
17232         
17233         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){
17234             return;
17235         }
17236         
17237         this.hasQuery = true;
17238         
17239         this.loading = this.list.select('.loading', true).first();
17240         
17241         if(this.loading === null){
17242             this.list.createChild({
17243                 tag: 'div',
17244                 cls: 'loading roo-select2-more-results roo-select2-active',
17245                 html: 'Loading more results...'
17246             });
17247             
17248             this.loading = this.list.select('.loading', true).first();
17249             
17250             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
17251             
17252             this.loading.hide();
17253         }
17254         
17255         this.loading.show();
17256         
17257         var _combo = this;
17258         
17259         this.page++;
17260         this.loadNext = true;
17261         
17262         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
17263         
17264         return;
17265     },
17266     
17267     addItem : function(o)
17268     {   
17269         var dv = ''; // display value
17270         
17271         if (this.displayField) {
17272             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
17273         } else {
17274             // this is an error condition!!!
17275             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
17276         }
17277         
17278         if(!dv.length){
17279             return;
17280         }
17281         
17282         var choice = this.choices.createChild({
17283             tag: 'li',
17284             cls: 'roo-select2-search-choice',
17285             cn: [
17286                 {
17287                     tag: 'div',
17288                     html: dv
17289                 },
17290                 {
17291                     tag: 'a',
17292                     href: '#',
17293                     cls: 'roo-select2-search-choice-close fa fa-times',
17294                     tabindex: '-1'
17295                 }
17296             ]
17297             
17298         }, this.searchField);
17299         
17300         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17301         
17302         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17303         
17304         this.item.push(o);
17305         
17306         this.lastData = o;
17307         
17308         this.syncValue();
17309         
17310         this.inputEl().dom.value = '';
17311         
17312         this.validate();
17313     },
17314     
17315     onRemoveItem : function(e, _self, o)
17316     {
17317         e.preventDefault();
17318         
17319         this.lastItem = Roo.apply([], this.item);
17320         
17321         var index = this.item.indexOf(o.data) * 1;
17322         
17323         if( index < 0){
17324             Roo.log('not this item?!');
17325             return;
17326         }
17327         
17328         this.item.splice(index, 1);
17329         o.item.remove();
17330         
17331         this.syncValue();
17332         
17333         this.fireEvent('remove', this, e);
17334         
17335         this.validate();
17336         
17337     },
17338     
17339     syncValue : function()
17340     {
17341         if(!this.item.length){
17342             this.clearValue();
17343             return;
17344         }
17345             
17346         var value = [];
17347         var _this = this;
17348         Roo.each(this.item, function(i){
17349             if(_this.valueField){
17350                 value.push(i[_this.valueField]);
17351                 return;
17352             }
17353
17354             value.push(i);
17355         });
17356
17357         this.value = value.join(',');
17358
17359         if(this.hiddenField){
17360             this.hiddenField.dom.value = this.value;
17361         }
17362         
17363         this.store.fireEvent("datachanged", this.store);
17364         
17365         this.validate();
17366     },
17367     
17368     clearItem : function()
17369     {
17370         if(!this.multiple){
17371             return;
17372         }
17373         
17374         this.item = [];
17375         
17376         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17377            c.remove();
17378         });
17379         
17380         this.syncValue();
17381         
17382         this.validate();
17383         
17384         if(this.tickable && !Roo.isTouch){
17385             this.view.refresh();
17386         }
17387     },
17388     
17389     inputEl: function ()
17390     {
17391         if(Roo.isIOS && this.useNativeIOS){
17392             return this.el.select('select.roo-ios-select', true).first();
17393         }
17394         
17395         if(Roo.isTouch && this.mobileTouchView){
17396             return this.el.select('input.form-control',true).first();
17397         }
17398         
17399         if(this.tickable){
17400             return this.searchField;
17401         }
17402         
17403         return this.el.select('input.form-control',true).first();
17404     },
17405     
17406     onTickableFooterButtonClick : function(e, btn, el)
17407     {
17408         e.preventDefault();
17409         
17410         this.lastItem = Roo.apply([], this.item);
17411         
17412         if(btn && btn.name == 'cancel'){
17413             this.tickItems = Roo.apply([], this.item);
17414             this.collapse();
17415             return;
17416         }
17417         
17418         this.clearItem();
17419         
17420         var _this = this;
17421         
17422         Roo.each(this.tickItems, function(o){
17423             _this.addItem(o);
17424         });
17425         
17426         this.collapse();
17427         
17428     },
17429     
17430     validate : function()
17431     {
17432         if(this.getVisibilityEl().hasClass('hidden')){
17433             return true;
17434         }
17435         
17436         var v = this.getRawValue();
17437         
17438         if(this.multiple){
17439             v = this.getValue();
17440         }
17441         
17442         if(this.disabled || this.allowBlank || v.length){
17443             this.markValid();
17444             return true;
17445         }
17446         
17447         this.markInvalid();
17448         return false;
17449     },
17450     
17451     tickableInputEl : function()
17452     {
17453         if(!this.tickable || !this.editable){
17454             return this.inputEl();
17455         }
17456         
17457         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17458     },
17459     
17460     
17461     getAutoCreateTouchView : function()
17462     {
17463         var id = Roo.id();
17464         
17465         var cfg = {
17466             cls: 'form-group' //input-group
17467         };
17468         
17469         var input =  {
17470             tag: 'input',
17471             id : id,
17472             type : this.inputType,
17473             cls : 'form-control x-combo-noedit',
17474             autocomplete: 'new-password',
17475             placeholder : this.placeholder || '',
17476             readonly : true
17477         };
17478         
17479         if (this.name) {
17480             input.name = this.name;
17481         }
17482         
17483         if (this.size) {
17484             input.cls += ' input-' + this.size;
17485         }
17486         
17487         if (this.disabled) {
17488             input.disabled = true;
17489         }
17490         
17491         var inputblock = {
17492             cls : 'roo-combobox-wrap',
17493             cn : [
17494                 input
17495             ]
17496         };
17497         
17498         if(this.before){
17499             inputblock.cls += ' input-group';
17500             
17501             inputblock.cn.unshift({
17502                 tag :'span',
17503                 cls : 'input-group-addon input-group-prepend input-group-text',
17504                 html : this.before
17505             });
17506         }
17507         
17508         if(this.removable && !this.multiple){
17509             inputblock.cls += ' roo-removable';
17510             
17511             inputblock.cn.push({
17512                 tag: 'button',
17513                 html : 'x',
17514                 cls : 'roo-combo-removable-btn close'
17515             });
17516         }
17517
17518         if(this.hasFeedback && !this.allowBlank){
17519             
17520             inputblock.cls += ' has-feedback';
17521             
17522             inputblock.cn.push({
17523                 tag: 'span',
17524                 cls: 'glyphicon form-control-feedback'
17525             });
17526             
17527         }
17528         
17529         if (this.after) {
17530             
17531             inputblock.cls += (this.before) ? '' : ' input-group';
17532             
17533             inputblock.cn.push({
17534                 tag :'span',
17535                 cls : 'input-group-addon input-group-append input-group-text',
17536                 html : this.after
17537             });
17538         }
17539
17540         
17541         var ibwrap = inputblock;
17542         
17543         if(this.multiple){
17544             ibwrap = {
17545                 tag: 'ul',
17546                 cls: 'roo-select2-choices',
17547                 cn:[
17548                     {
17549                         tag: 'li',
17550                         cls: 'roo-select2-search-field',
17551                         cn: [
17552
17553                             inputblock
17554                         ]
17555                     }
17556                 ]
17557             };
17558         
17559             
17560         }
17561         
17562         var combobox = {
17563             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17564             cn: [
17565                 {
17566                     tag: 'input',
17567                     type : 'hidden',
17568                     cls: 'form-hidden-field'
17569                 },
17570                 ibwrap
17571             ]
17572         };
17573         
17574         if(!this.multiple && this.showToggleBtn){
17575             
17576             var caret = {
17577                 cls: 'caret'
17578             };
17579             
17580             if (this.caret != false) {
17581                 caret = {
17582                      tag: 'i',
17583                      cls: 'fa fa-' + this.caret
17584                 };
17585                 
17586             }
17587             
17588             combobox.cn.push({
17589                 tag :'span',
17590                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17591                 cn : [
17592                     Roo.bootstrap.version == 3 ? caret : '',
17593                     {
17594                         tag: 'span',
17595                         cls: 'combobox-clear',
17596                         cn  : [
17597                             {
17598                                 tag : 'i',
17599                                 cls: 'icon-remove'
17600                             }
17601                         ]
17602                     }
17603                 ]
17604
17605             })
17606         }
17607         
17608         if(this.multiple){
17609             combobox.cls += ' roo-select2-container-multi';
17610         }
17611         
17612         var required =  this.allowBlank ?  {
17613                     tag : 'i',
17614                     style: 'display: none'
17615                 } : {
17616                    tag : 'i',
17617                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17618                    tooltip : 'This field is required'
17619                 };
17620         
17621         var align = this.labelAlign || this.parentLabelAlign();
17622         
17623         if (align ==='left' && this.fieldLabel.length) {
17624
17625             cfg.cn = [
17626                 required,
17627                 {
17628                     tag: 'label',
17629                     cls : 'control-label col-form-label',
17630                     html : this.fieldLabel
17631
17632                 },
17633                 {
17634                     cls : 'roo-combobox-wrap ', 
17635                     cn: [
17636                         combobox
17637                     ]
17638                 }
17639             ];
17640             
17641             var labelCfg = cfg.cn[1];
17642             var contentCfg = cfg.cn[2];
17643             
17644
17645             if(this.indicatorpos == 'right'){
17646                 cfg.cn = [
17647                     {
17648                         tag: 'label',
17649                         'for' :  id,
17650                         cls : 'control-label col-form-label',
17651                         cn : [
17652                             {
17653                                 tag : 'span',
17654                                 html : this.fieldLabel
17655                             },
17656                             required
17657                         ]
17658                     },
17659                     {
17660                         cls : "roo-combobox-wrap ",
17661                         cn: [
17662                             combobox
17663                         ]
17664                     }
17665
17666                 ];
17667                 
17668                 labelCfg = cfg.cn[0];
17669                 contentCfg = cfg.cn[1];
17670             }
17671             
17672            
17673             
17674             if(this.labelWidth > 12){
17675                 labelCfg.style = "width: " + this.labelWidth + 'px';
17676             }
17677            
17678             if(this.labelWidth < 13 && this.labelmd == 0){
17679                 this.labelmd = this.labelWidth;
17680             }
17681             
17682             if(this.labellg > 0){
17683                 labelCfg.cls += ' col-lg-' + this.labellg;
17684                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17685             }
17686             
17687             if(this.labelmd > 0){
17688                 labelCfg.cls += ' col-md-' + this.labelmd;
17689                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17690             }
17691             
17692             if(this.labelsm > 0){
17693                 labelCfg.cls += ' col-sm-' + this.labelsm;
17694                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17695             }
17696             
17697             if(this.labelxs > 0){
17698                 labelCfg.cls += ' col-xs-' + this.labelxs;
17699                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17700             }
17701                 
17702                 
17703         } else if ( this.fieldLabel.length) {
17704             cfg.cn = [
17705                required,
17706                 {
17707                     tag: 'label',
17708                     cls : 'control-label',
17709                     html : this.fieldLabel
17710
17711                 },
17712                 {
17713                     cls : '', 
17714                     cn: [
17715                         combobox
17716                     ]
17717                 }
17718             ];
17719             
17720             if(this.indicatorpos == 'right'){
17721                 cfg.cn = [
17722                     {
17723                         tag: 'label',
17724                         cls : 'control-label',
17725                         html : this.fieldLabel,
17726                         cn : [
17727                             required
17728                         ]
17729                     },
17730                     {
17731                         cls : '', 
17732                         cn: [
17733                             combobox
17734                         ]
17735                     }
17736                 ];
17737             }
17738         } else {
17739             cfg.cn = combobox;    
17740         }
17741         
17742         
17743         var settings = this;
17744         
17745         ['xs','sm','md','lg'].map(function(size){
17746             if (settings[size]) {
17747                 cfg.cls += ' col-' + size + '-' + settings[size];
17748             }
17749         });
17750         
17751         return cfg;
17752     },
17753     
17754     initTouchView : function()
17755     {
17756         this.renderTouchView();
17757         
17758         this.touchViewEl.on('scroll', function(){
17759             this.el.dom.scrollTop = 0;
17760         }, this);
17761         
17762         this.originalValue = this.getValue();
17763         
17764         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17765         
17766         this.inputEl().on("click", this.showTouchView, this);
17767         if (this.triggerEl) {
17768             this.triggerEl.on("click", this.showTouchView, this);
17769         }
17770         
17771         
17772         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17773         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17774         
17775         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17776         
17777         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17778         this.store.on('load', this.onTouchViewLoad, this);
17779         this.store.on('loadexception', this.onTouchViewLoadException, this);
17780         
17781         if(this.hiddenName){
17782             
17783             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17784             
17785             this.hiddenField.dom.value =
17786                 this.hiddenValue !== undefined ? this.hiddenValue :
17787                 this.value !== undefined ? this.value : '';
17788         
17789             this.el.dom.removeAttribute('name');
17790             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17791         }
17792         
17793         if(this.multiple){
17794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17796         }
17797         
17798         if(this.removable && !this.multiple){
17799             var close = this.closeTriggerEl();
17800             if(close){
17801                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17802                 close.on('click', this.removeBtnClick, this, close);
17803             }
17804         }
17805         /*
17806          * fix the bug in Safari iOS8
17807          */
17808         this.inputEl().on("focus", function(e){
17809             document.activeElement.blur();
17810         }, this);
17811         
17812         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17813         
17814         return;
17815         
17816         
17817     },
17818     
17819     renderTouchView : function()
17820     {
17821         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17822         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17823         
17824         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17825         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17826         
17827         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17828         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17829         this.touchViewBodyEl.setStyle('overflow', 'auto');
17830         
17831         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17832         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17833         
17834         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17835         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17836         
17837     },
17838     
17839     showTouchView : function()
17840     {
17841         if(this.disabled){
17842             return;
17843         }
17844         
17845         this.touchViewHeaderEl.hide();
17846
17847         if(this.modalTitle.length){
17848             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17849             this.touchViewHeaderEl.show();
17850         }
17851
17852         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17853         this.touchViewEl.show();
17854
17855         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17856         
17857         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17858         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17859
17860         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17861
17862         if(this.modalTitle.length){
17863             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17864         }
17865         
17866         this.touchViewBodyEl.setHeight(bodyHeight);
17867
17868         if(this.animate){
17869             var _this = this;
17870             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17871         }else{
17872             this.touchViewEl.addClass(['in','show']);
17873         }
17874         
17875         if(this._touchViewMask){
17876             Roo.get(document.body).addClass("x-body-masked");
17877             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17878             this._touchViewMask.setStyle('z-index', 10000);
17879             this._touchViewMask.addClass('show');
17880         }
17881         
17882         this.doTouchViewQuery();
17883         
17884     },
17885     
17886     hideTouchView : function()
17887     {
17888         this.touchViewEl.removeClass(['in','show']);
17889
17890         if(this.animate){
17891             var _this = this;
17892             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17893         }else{
17894             this.touchViewEl.setStyle('display', 'none');
17895         }
17896         
17897         if(this._touchViewMask){
17898             this._touchViewMask.removeClass('show');
17899             Roo.get(document.body).removeClass("x-body-masked");
17900         }
17901     },
17902     
17903     setTouchViewValue : function()
17904     {
17905         if(this.multiple){
17906             this.clearItem();
17907         
17908             var _this = this;
17909
17910             Roo.each(this.tickItems, function(o){
17911                 this.addItem(o);
17912             }, this);
17913         }
17914         
17915         this.hideTouchView();
17916     },
17917     
17918     doTouchViewQuery : function()
17919     {
17920         var qe = {
17921             query: '',
17922             forceAll: true,
17923             combo: this,
17924             cancel:false
17925         };
17926         
17927         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17928             return false;
17929         }
17930         
17931         if(!this.alwaysQuery || this.mode == 'local'){
17932             this.onTouchViewLoad();
17933             return;
17934         }
17935         
17936         this.store.load();
17937     },
17938     
17939     onTouchViewBeforeLoad : function(combo,opts)
17940     {
17941         return;
17942     },
17943
17944     // private
17945     onTouchViewLoad : function()
17946     {
17947         if(this.store.getCount() < 1){
17948             this.onTouchViewEmptyResults();
17949             return;
17950         }
17951         
17952         this.clearTouchView();
17953         
17954         var rawValue = this.getRawValue();
17955         
17956         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17957         
17958         this.tickItems = [];
17959         
17960         this.store.data.each(function(d, rowIndex){
17961             var row = this.touchViewListGroup.createChild(template);
17962             
17963             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17964                 row.addClass(d.data.cls);
17965             }
17966             
17967             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17968                 var cfg = {
17969                     data : d.data,
17970                     html : d.data[this.displayField]
17971                 };
17972                 
17973                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17974                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17975                 }
17976             }
17977             row.removeClass('selected');
17978             if(!this.multiple && this.valueField &&
17979                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17980             {
17981                 // radio buttons..
17982                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17983                 row.addClass('selected');
17984             }
17985             
17986             if(this.multiple && this.valueField &&
17987                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17988             {
17989                 
17990                 // checkboxes...
17991                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17992                 this.tickItems.push(d.data);
17993             }
17994             
17995             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17996             
17997         }, this);
17998         
17999         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
18000         
18001         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
18002
18003         if(this.modalTitle.length){
18004             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
18005         }
18006
18007         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
18008         
18009         if(this.mobile_restrict_height && listHeight < bodyHeight){
18010             this.touchViewBodyEl.setHeight(listHeight);
18011         }
18012         
18013         var _this = this;
18014         
18015         if(firstChecked && listHeight > bodyHeight){
18016             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
18017         }
18018         
18019     },
18020     
18021     onTouchViewLoadException : function()
18022     {
18023         this.hideTouchView();
18024     },
18025     
18026     onTouchViewEmptyResults : function()
18027     {
18028         this.clearTouchView();
18029         
18030         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
18031         
18032         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
18033         
18034     },
18035     
18036     clearTouchView : function()
18037     {
18038         this.touchViewListGroup.dom.innerHTML = '';
18039     },
18040     
18041     onTouchViewClick : function(e, el, o)
18042     {
18043         e.preventDefault();
18044         
18045         var row = o.row;
18046         var rowIndex = o.rowIndex;
18047         
18048         var r = this.store.getAt(rowIndex);
18049         
18050         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
18051             
18052             if(!this.multiple){
18053                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
18054                     c.dom.removeAttribute('checked');
18055                 }, this);
18056
18057                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18058
18059                 this.setFromData(r.data);
18060
18061                 var close = this.closeTriggerEl();
18062
18063                 if(close){
18064                     close.show();
18065                 }
18066
18067                 this.hideTouchView();
18068
18069                 this.fireEvent('select', this, r, rowIndex);
18070
18071                 return;
18072             }
18073
18074             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
18075                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
18076                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
18077                 return;
18078             }
18079
18080             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
18081             this.addItem(r.data);
18082             this.tickItems.push(r.data);
18083         }
18084     },
18085     
18086     getAutoCreateNativeIOS : function()
18087     {
18088         var cfg = {
18089             cls: 'form-group' //input-group,
18090         };
18091         
18092         var combobox =  {
18093             tag: 'select',
18094             cls : 'roo-ios-select'
18095         };
18096         
18097         if (this.name) {
18098             combobox.name = this.name;
18099         }
18100         
18101         if (this.disabled) {
18102             combobox.disabled = true;
18103         }
18104         
18105         var settings = this;
18106         
18107         ['xs','sm','md','lg'].map(function(size){
18108             if (settings[size]) {
18109                 cfg.cls += ' col-' + size + '-' + settings[size];
18110             }
18111         });
18112         
18113         cfg.cn = combobox;
18114         
18115         return cfg;
18116         
18117     },
18118     
18119     initIOSView : function()
18120     {
18121         this.store.on('load', this.onIOSViewLoad, this);
18122         
18123         return;
18124     },
18125     
18126     onIOSViewLoad : function()
18127     {
18128         if(this.store.getCount() < 1){
18129             return;
18130         }
18131         
18132         this.clearIOSView();
18133         
18134         if(this.allowBlank) {
18135             
18136             var default_text = '-- SELECT --';
18137             
18138             if(this.placeholder.length){
18139                 default_text = this.placeholder;
18140             }
18141             
18142             if(this.emptyTitle.length){
18143                 default_text += ' - ' + this.emptyTitle + ' -';
18144             }
18145             
18146             var opt = this.inputEl().createChild({
18147                 tag: 'option',
18148                 value : 0,
18149                 html : default_text
18150             });
18151             
18152             var o = {};
18153             o[this.valueField] = 0;
18154             o[this.displayField] = default_text;
18155             
18156             this.ios_options.push({
18157                 data : o,
18158                 el : opt
18159             });
18160             
18161         }
18162         
18163         this.store.data.each(function(d, rowIndex){
18164             
18165             var html = '';
18166             
18167             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
18168                 html = d.data[this.displayField];
18169             }
18170             
18171             var value = '';
18172             
18173             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
18174                 value = d.data[this.valueField];
18175             }
18176             
18177             var option = {
18178                 tag: 'option',
18179                 value : value,
18180                 html : html
18181             };
18182             
18183             if(this.value == d.data[this.valueField]){
18184                 option['selected'] = true;
18185             }
18186             
18187             var opt = this.inputEl().createChild(option);
18188             
18189             this.ios_options.push({
18190                 data : d.data,
18191                 el : opt
18192             });
18193             
18194         }, this);
18195         
18196         this.inputEl().on('change', function(){
18197            this.fireEvent('select', this);
18198         }, this);
18199         
18200     },
18201     
18202     clearIOSView: function()
18203     {
18204         this.inputEl().dom.innerHTML = '';
18205         
18206         this.ios_options = [];
18207     },
18208     
18209     setIOSValue: function(v)
18210     {
18211         this.value = v;
18212         
18213         if(!this.ios_options){
18214             return;
18215         }
18216         
18217         Roo.each(this.ios_options, function(opts){
18218            
18219            opts.el.dom.removeAttribute('selected');
18220            
18221            if(opts.data[this.valueField] != v){
18222                return;
18223            }
18224            
18225            opts.el.dom.setAttribute('selected', true);
18226            
18227         }, this);
18228     }
18229
18230     /** 
18231     * @cfg {Boolean} grow 
18232     * @hide 
18233     */
18234     /** 
18235     * @cfg {Number} growMin 
18236     * @hide 
18237     */
18238     /** 
18239     * @cfg {Number} growMax 
18240     * @hide 
18241     */
18242     /**
18243      * @hide
18244      * @method autoSize
18245      */
18246 });
18247
18248 Roo.apply(Roo.bootstrap.ComboBox,  {
18249     
18250     header : {
18251         tag: 'div',
18252         cls: 'modal-header',
18253         cn: [
18254             {
18255                 tag: 'h4',
18256                 cls: 'modal-title'
18257             }
18258         ]
18259     },
18260     
18261     body : {
18262         tag: 'div',
18263         cls: 'modal-body',
18264         cn: [
18265             {
18266                 tag: 'ul',
18267                 cls: 'list-group'
18268             }
18269         ]
18270     },
18271     
18272     listItemRadio : {
18273         tag: 'li',
18274         cls: 'list-group-item',
18275         cn: [
18276             {
18277                 tag: 'span',
18278                 cls: 'roo-combobox-list-group-item-value'
18279             },
18280             {
18281                 tag: 'div',
18282                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
18283                 cn: [
18284                     {
18285                         tag: 'input',
18286                         type: 'radio'
18287                     },
18288                     {
18289                         tag: 'label'
18290                     }
18291                 ]
18292             }
18293         ]
18294     },
18295     
18296     listItemCheckbox : {
18297         tag: 'li',
18298         cls: 'list-group-item',
18299         cn: [
18300             {
18301                 tag: 'span',
18302                 cls: 'roo-combobox-list-group-item-value'
18303             },
18304             {
18305                 tag: 'div',
18306                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18307                 cn: [
18308                     {
18309                         tag: 'input',
18310                         type: 'checkbox'
18311                     },
18312                     {
18313                         tag: 'label'
18314                     }
18315                 ]
18316             }
18317         ]
18318     },
18319     
18320     emptyResult : {
18321         tag: 'div',
18322         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18323     },
18324     
18325     footer : {
18326         tag: 'div',
18327         cls: 'modal-footer',
18328         cn: [
18329             {
18330                 tag: 'div',
18331                 cls: 'row',
18332                 cn: [
18333                     {
18334                         tag: 'div',
18335                         cls: 'col-xs-6 text-left',
18336                         cn: {
18337                             tag: 'button',
18338                             cls: 'btn btn-danger roo-touch-view-cancel',
18339                             html: 'Cancel'
18340                         }
18341                     },
18342                     {
18343                         tag: 'div',
18344                         cls: 'col-xs-6 text-right',
18345                         cn: {
18346                             tag: 'button',
18347                             cls: 'btn btn-success roo-touch-view-ok',
18348                             html: 'OK'
18349                         }
18350                     }
18351                 ]
18352             }
18353         ]
18354         
18355     }
18356 });
18357
18358 Roo.apply(Roo.bootstrap.ComboBox,  {
18359     
18360     touchViewTemplate : {
18361         tag: 'div',
18362         cls: 'modal fade roo-combobox-touch-view',
18363         cn: [
18364             {
18365                 tag: 'div',
18366                 cls: 'modal-dialog',
18367                 style : 'position:fixed', // we have to fix position....
18368                 cn: [
18369                     {
18370                         tag: 'div',
18371                         cls: 'modal-content',
18372                         cn: [
18373                             Roo.bootstrap.ComboBox.header,
18374                             Roo.bootstrap.ComboBox.body,
18375                             Roo.bootstrap.ComboBox.footer
18376                         ]
18377                     }
18378                 ]
18379             }
18380         ]
18381     }
18382 });/*
18383  * Based on:
18384  * Ext JS Library 1.1.1
18385  * Copyright(c) 2006-2007, Ext JS, LLC.
18386  *
18387  * Originally Released Under LGPL - original licence link has changed is not relivant.
18388  *
18389  * Fork - LGPL
18390  * <script type="text/javascript">
18391  */
18392
18393 /**
18394  * @class Roo.View
18395  * @extends Roo.util.Observable
18396  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18397  * This class also supports single and multi selection modes. <br>
18398  * Create a data model bound view:
18399  <pre><code>
18400  var store = new Roo.data.Store(...);
18401
18402  var view = new Roo.View({
18403     el : "my-element",
18404     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18405  
18406     singleSelect: true,
18407     selectedClass: "ydataview-selected",
18408     store: store
18409  });
18410
18411  // listen for node click?
18412  view.on("click", function(vw, index, node, e){
18413  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18414  });
18415
18416  // load XML data
18417  dataModel.load("foobar.xml");
18418  </code></pre>
18419  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18420  * <br><br>
18421  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18422  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18423  * 
18424  * Note: old style constructor is still suported (container, template, config)
18425  * 
18426  * @constructor
18427  * Create a new View
18428  * @param {Object} config The config object
18429  * 
18430  */
18431 Roo.View = function(config, depreciated_tpl, depreciated_config){
18432     
18433     this.parent = false;
18434     
18435     if (typeof(depreciated_tpl) == 'undefined') {
18436         // new way.. - universal constructor.
18437         Roo.apply(this, config);
18438         this.el  = Roo.get(this.el);
18439     } else {
18440         // old format..
18441         this.el  = Roo.get(config);
18442         this.tpl = depreciated_tpl;
18443         Roo.apply(this, depreciated_config);
18444     }
18445     this.wrapEl  = this.el.wrap().wrap();
18446     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18447     
18448     
18449     if(typeof(this.tpl) == "string"){
18450         this.tpl = new Roo.Template(this.tpl);
18451     } else {
18452         // support xtype ctors..
18453         this.tpl = new Roo.factory(this.tpl, Roo);
18454     }
18455     
18456     
18457     this.tpl.compile();
18458     
18459     /** @private */
18460     this.addEvents({
18461         /**
18462          * @event beforeclick
18463          * Fires before a click is processed. Returns false to cancel the default action.
18464          * @param {Roo.View} this
18465          * @param {Number} index The index of the target node
18466          * @param {HTMLElement} node The target node
18467          * @param {Roo.EventObject} e The raw event object
18468          */
18469             "beforeclick" : true,
18470         /**
18471          * @event click
18472          * Fires when a template node is clicked.
18473          * @param {Roo.View} this
18474          * @param {Number} index The index of the target node
18475          * @param {HTMLElement} node The target node
18476          * @param {Roo.EventObject} e The raw event object
18477          */
18478             "click" : true,
18479         /**
18480          * @event dblclick
18481          * Fires when a template node is double clicked.
18482          * @param {Roo.View} this
18483          * @param {Number} index The index of the target node
18484          * @param {HTMLElement} node The target node
18485          * @param {Roo.EventObject} e The raw event object
18486          */
18487             "dblclick" : true,
18488         /**
18489          * @event contextmenu
18490          * Fires when a template node is right clicked.
18491          * @param {Roo.View} this
18492          * @param {Number} index The index of the target node
18493          * @param {HTMLElement} node The target node
18494          * @param {Roo.EventObject} e The raw event object
18495          */
18496             "contextmenu" : true,
18497         /**
18498          * @event selectionchange
18499          * Fires when the selected nodes change.
18500          * @param {Roo.View} this
18501          * @param {Array} selections Array of the selected nodes
18502          */
18503             "selectionchange" : true,
18504     
18505         /**
18506          * @event beforeselect
18507          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18508          * @param {Roo.View} this
18509          * @param {HTMLElement} node The node to be selected
18510          * @param {Array} selections Array of currently selected nodes
18511          */
18512             "beforeselect" : true,
18513         /**
18514          * @event preparedata
18515          * Fires on every row to render, to allow you to change the data.
18516          * @param {Roo.View} this
18517          * @param {Object} data to be rendered (change this)
18518          */
18519           "preparedata" : true
18520           
18521           
18522         });
18523
18524
18525
18526     this.el.on({
18527         "click": this.onClick,
18528         "dblclick": this.onDblClick,
18529         "contextmenu": this.onContextMenu,
18530         scope:this
18531     });
18532
18533     this.selections = [];
18534     this.nodes = [];
18535     this.cmp = new Roo.CompositeElementLite([]);
18536     if(this.store){
18537         this.store = Roo.factory(this.store, Roo.data);
18538         this.setStore(this.store, true);
18539     }
18540     
18541     if ( this.footer && this.footer.xtype) {
18542            
18543          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18544         
18545         this.footer.dataSource = this.store;
18546         this.footer.container = fctr;
18547         this.footer = Roo.factory(this.footer, Roo);
18548         fctr.insertFirst(this.el);
18549         
18550         // this is a bit insane - as the paging toolbar seems to detach the el..
18551 //        dom.parentNode.parentNode.parentNode
18552          // they get detached?
18553     }
18554     
18555     
18556     Roo.View.superclass.constructor.call(this);
18557     
18558     
18559 };
18560
18561 Roo.extend(Roo.View, Roo.util.Observable, {
18562     
18563      /**
18564      * @cfg {Roo.data.Store} store Data store to load data from.
18565      */
18566     store : false,
18567     
18568     /**
18569      * @cfg {String|Roo.Element} el The container element.
18570      */
18571     el : '',
18572     
18573     /**
18574      * @cfg {String|Roo.Template} tpl The template used by this View 
18575      */
18576     tpl : false,
18577     /**
18578      * @cfg {String} dataName the named area of the template to use as the data area
18579      *                          Works with domtemplates roo-name="name"
18580      */
18581     dataName: false,
18582     /**
18583      * @cfg {String} selectedClass The css class to add to selected nodes
18584      */
18585     selectedClass : "x-view-selected",
18586      /**
18587      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18588      */
18589     emptyText : "",
18590     
18591     /**
18592      * @cfg {String} text to display on mask (default Loading)
18593      */
18594     mask : false,
18595     /**
18596      * @cfg {Boolean} multiSelect Allow multiple selection
18597      */
18598     multiSelect : false,
18599     /**
18600      * @cfg {Boolean} singleSelect Allow single selection
18601      */
18602     singleSelect:  false,
18603     
18604     /**
18605      * @cfg {Boolean} toggleSelect - selecting 
18606      */
18607     toggleSelect : false,
18608     
18609     /**
18610      * @cfg {Boolean} tickable - selecting 
18611      */
18612     tickable : false,
18613     
18614     /**
18615      * Returns the element this view is bound to.
18616      * @return {Roo.Element}
18617      */
18618     getEl : function(){
18619         return this.wrapEl;
18620     },
18621     
18622     
18623
18624     /**
18625      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18626      */
18627     refresh : function(){
18628         //Roo.log('refresh');
18629         var t = this.tpl;
18630         
18631         // if we are using something like 'domtemplate', then
18632         // the what gets used is:
18633         // t.applySubtemplate(NAME, data, wrapping data..)
18634         // the outer template then get' applied with
18635         //     the store 'extra data'
18636         // and the body get's added to the
18637         //      roo-name="data" node?
18638         //      <span class='roo-tpl-{name}'></span> ?????
18639         
18640         
18641         
18642         this.clearSelections();
18643         this.el.update("");
18644         var html = [];
18645         var records = this.store.getRange();
18646         if(records.length < 1) {
18647             
18648             // is this valid??  = should it render a template??
18649             
18650             this.el.update(this.emptyText);
18651             return;
18652         }
18653         var el = this.el;
18654         if (this.dataName) {
18655             this.el.update(t.apply(this.store.meta)); //????
18656             el = this.el.child('.roo-tpl-' + this.dataName);
18657         }
18658         
18659         for(var i = 0, len = records.length; i < len; i++){
18660             var data = this.prepareData(records[i].data, i, records[i]);
18661             this.fireEvent("preparedata", this, data, i, records[i]);
18662             
18663             var d = Roo.apply({}, data);
18664             
18665             if(this.tickable){
18666                 Roo.apply(d, {'roo-id' : Roo.id()});
18667                 
18668                 var _this = this;
18669             
18670                 Roo.each(this.parent.item, function(item){
18671                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18672                         return;
18673                     }
18674                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18675                 });
18676             }
18677             
18678             html[html.length] = Roo.util.Format.trim(
18679                 this.dataName ?
18680                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18681                     t.apply(d)
18682             );
18683         }
18684         
18685         
18686         
18687         el.update(html.join(""));
18688         this.nodes = el.dom.childNodes;
18689         this.updateIndexes(0);
18690     },
18691     
18692
18693     /**
18694      * Function to override to reformat the data that is sent to
18695      * the template for each node.
18696      * DEPRICATED - use the preparedata event handler.
18697      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18698      * a JSON object for an UpdateManager bound view).
18699      */
18700     prepareData : function(data, index, record)
18701     {
18702         this.fireEvent("preparedata", this, data, index, record);
18703         return data;
18704     },
18705
18706     onUpdate : function(ds, record){
18707         // Roo.log('on update');   
18708         this.clearSelections();
18709         var index = this.store.indexOf(record);
18710         var n = this.nodes[index];
18711         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18712         n.parentNode.removeChild(n);
18713         this.updateIndexes(index, index);
18714     },
18715
18716     
18717     
18718 // --------- FIXME     
18719     onAdd : function(ds, records, index)
18720     {
18721         //Roo.log(['on Add', ds, records, index] );        
18722         this.clearSelections();
18723         if(this.nodes.length == 0){
18724             this.refresh();
18725             return;
18726         }
18727         var n = this.nodes[index];
18728         for(var i = 0, len = records.length; i < len; i++){
18729             var d = this.prepareData(records[i].data, i, records[i]);
18730             if(n){
18731                 this.tpl.insertBefore(n, d);
18732             }else{
18733                 
18734                 this.tpl.append(this.el, d);
18735             }
18736         }
18737         this.updateIndexes(index);
18738     },
18739
18740     onRemove : function(ds, record, index){
18741        // Roo.log('onRemove');
18742         this.clearSelections();
18743         var el = this.dataName  ?
18744             this.el.child('.roo-tpl-' + this.dataName) :
18745             this.el; 
18746         
18747         el.dom.removeChild(this.nodes[index]);
18748         this.updateIndexes(index);
18749     },
18750
18751     /**
18752      * Refresh an individual node.
18753      * @param {Number} index
18754      */
18755     refreshNode : function(index){
18756         this.onUpdate(this.store, this.store.getAt(index));
18757     },
18758
18759     updateIndexes : function(startIndex, endIndex){
18760         var ns = this.nodes;
18761         startIndex = startIndex || 0;
18762         endIndex = endIndex || ns.length - 1;
18763         for(var i = startIndex; i <= endIndex; i++){
18764             ns[i].nodeIndex = i;
18765         }
18766     },
18767
18768     /**
18769      * Changes the data store this view uses and refresh the view.
18770      * @param {Store} store
18771      */
18772     setStore : function(store, initial){
18773         if(!initial && this.store){
18774             this.store.un("datachanged", this.refresh);
18775             this.store.un("add", this.onAdd);
18776             this.store.un("remove", this.onRemove);
18777             this.store.un("update", this.onUpdate);
18778             this.store.un("clear", this.refresh);
18779             this.store.un("beforeload", this.onBeforeLoad);
18780             this.store.un("load", this.onLoad);
18781             this.store.un("loadexception", this.onLoad);
18782         }
18783         if(store){
18784           
18785             store.on("datachanged", this.refresh, this);
18786             store.on("add", this.onAdd, this);
18787             store.on("remove", this.onRemove, this);
18788             store.on("update", this.onUpdate, this);
18789             store.on("clear", this.refresh, this);
18790             store.on("beforeload", this.onBeforeLoad, this);
18791             store.on("load", this.onLoad, this);
18792             store.on("loadexception", this.onLoad, this);
18793         }
18794         
18795         if(store){
18796             this.refresh();
18797         }
18798     },
18799     /**
18800      * onbeforeLoad - masks the loading area.
18801      *
18802      */
18803     onBeforeLoad : function(store,opts)
18804     {
18805          //Roo.log('onBeforeLoad');   
18806         if (!opts.add) {
18807             this.el.update("");
18808         }
18809         this.el.mask(this.mask ? this.mask : "Loading" ); 
18810     },
18811     onLoad : function ()
18812     {
18813         this.el.unmask();
18814     },
18815     
18816
18817     /**
18818      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18819      * @param {HTMLElement} node
18820      * @return {HTMLElement} The template node
18821      */
18822     findItemFromChild : function(node){
18823         var el = this.dataName  ?
18824             this.el.child('.roo-tpl-' + this.dataName,true) :
18825             this.el.dom; 
18826         
18827         if(!node || node.parentNode == el){
18828                     return node;
18829             }
18830             var p = node.parentNode;
18831             while(p && p != el){
18832             if(p.parentNode == el){
18833                 return p;
18834             }
18835             p = p.parentNode;
18836         }
18837             return null;
18838     },
18839
18840     /** @ignore */
18841     onClick : function(e){
18842         var item = this.findItemFromChild(e.getTarget());
18843         if(item){
18844             var index = this.indexOf(item);
18845             if(this.onItemClick(item, index, e) !== false){
18846                 this.fireEvent("click", this, index, item, e);
18847             }
18848         }else{
18849             this.clearSelections();
18850         }
18851     },
18852
18853     /** @ignore */
18854     onContextMenu : function(e){
18855         var item = this.findItemFromChild(e.getTarget());
18856         if(item){
18857             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18858         }
18859     },
18860
18861     /** @ignore */
18862     onDblClick : function(e){
18863         var item = this.findItemFromChild(e.getTarget());
18864         if(item){
18865             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18866         }
18867     },
18868
18869     onItemClick : function(item, index, e)
18870     {
18871         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18872             return false;
18873         }
18874         if (this.toggleSelect) {
18875             var m = this.isSelected(item) ? 'unselect' : 'select';
18876             //Roo.log(m);
18877             var _t = this;
18878             _t[m](item, true, false);
18879             return true;
18880         }
18881         if(this.multiSelect || this.singleSelect){
18882             if(this.multiSelect && e.shiftKey && this.lastSelection){
18883                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18884             }else{
18885                 this.select(item, this.multiSelect && e.ctrlKey);
18886                 this.lastSelection = item;
18887             }
18888             
18889             if(!this.tickable){
18890                 e.preventDefault();
18891             }
18892             
18893         }
18894         return true;
18895     },
18896
18897     /**
18898      * Get the number of selected nodes.
18899      * @return {Number}
18900      */
18901     getSelectionCount : function(){
18902         return this.selections.length;
18903     },
18904
18905     /**
18906      * Get the currently selected nodes.
18907      * @return {Array} An array of HTMLElements
18908      */
18909     getSelectedNodes : function(){
18910         return this.selections;
18911     },
18912
18913     /**
18914      * Get the indexes of the selected nodes.
18915      * @return {Array}
18916      */
18917     getSelectedIndexes : function(){
18918         var indexes = [], s = this.selections;
18919         for(var i = 0, len = s.length; i < len; i++){
18920             indexes.push(s[i].nodeIndex);
18921         }
18922         return indexes;
18923     },
18924
18925     /**
18926      * Clear all selections
18927      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18928      */
18929     clearSelections : function(suppressEvent){
18930         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18931             this.cmp.elements = this.selections;
18932             this.cmp.removeClass(this.selectedClass);
18933             this.selections = [];
18934             if(!suppressEvent){
18935                 this.fireEvent("selectionchange", this, this.selections);
18936             }
18937         }
18938     },
18939
18940     /**
18941      * Returns true if the passed node is selected
18942      * @param {HTMLElement/Number} node The node or node index
18943      * @return {Boolean}
18944      */
18945     isSelected : function(node){
18946         var s = this.selections;
18947         if(s.length < 1){
18948             return false;
18949         }
18950         node = this.getNode(node);
18951         return s.indexOf(node) !== -1;
18952     },
18953
18954     /**
18955      * Selects nodes.
18956      * @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
18957      * @param {Boolean} keepExisting (optional) true to keep existing selections
18958      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18959      */
18960     select : function(nodeInfo, keepExisting, suppressEvent){
18961         if(nodeInfo instanceof Array){
18962             if(!keepExisting){
18963                 this.clearSelections(true);
18964             }
18965             for(var i = 0, len = nodeInfo.length; i < len; i++){
18966                 this.select(nodeInfo[i], true, true);
18967             }
18968             return;
18969         } 
18970         var node = this.getNode(nodeInfo);
18971         if(!node || this.isSelected(node)){
18972             return; // already selected.
18973         }
18974         if(!keepExisting){
18975             this.clearSelections(true);
18976         }
18977         
18978         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18979             Roo.fly(node).addClass(this.selectedClass);
18980             this.selections.push(node);
18981             if(!suppressEvent){
18982                 this.fireEvent("selectionchange", this, this.selections);
18983             }
18984         }
18985         
18986         
18987     },
18988       /**
18989      * Unselects nodes.
18990      * @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
18991      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18992      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18993      */
18994     unselect : function(nodeInfo, keepExisting, suppressEvent)
18995     {
18996         if(nodeInfo instanceof Array){
18997             Roo.each(this.selections, function(s) {
18998                 this.unselect(s, nodeInfo);
18999             }, this);
19000             return;
19001         }
19002         var node = this.getNode(nodeInfo);
19003         if(!node || !this.isSelected(node)){
19004             //Roo.log("not selected");
19005             return; // not selected.
19006         }
19007         // fireevent???
19008         var ns = [];
19009         Roo.each(this.selections, function(s) {
19010             if (s == node ) {
19011                 Roo.fly(node).removeClass(this.selectedClass);
19012
19013                 return;
19014             }
19015             ns.push(s);
19016         },this);
19017         
19018         this.selections= ns;
19019         this.fireEvent("selectionchange", this, this.selections);
19020     },
19021
19022     /**
19023      * Gets a template node.
19024      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19025      * @return {HTMLElement} The node or null if it wasn't found
19026      */
19027     getNode : function(nodeInfo){
19028         if(typeof nodeInfo == "string"){
19029             return document.getElementById(nodeInfo);
19030         }else if(typeof nodeInfo == "number"){
19031             return this.nodes[nodeInfo];
19032         }
19033         return nodeInfo;
19034     },
19035
19036     /**
19037      * Gets a range template nodes.
19038      * @param {Number} startIndex
19039      * @param {Number} endIndex
19040      * @return {Array} An array of nodes
19041      */
19042     getNodes : function(start, end){
19043         var ns = this.nodes;
19044         start = start || 0;
19045         end = typeof end == "undefined" ? ns.length - 1 : end;
19046         var nodes = [];
19047         if(start <= end){
19048             for(var i = start; i <= end; i++){
19049                 nodes.push(ns[i]);
19050             }
19051         } else{
19052             for(var i = start; i >= end; i--){
19053                 nodes.push(ns[i]);
19054             }
19055         }
19056         return nodes;
19057     },
19058
19059     /**
19060      * Finds the index of the passed node
19061      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
19062      * @return {Number} The index of the node or -1
19063      */
19064     indexOf : function(node){
19065         node = this.getNode(node);
19066         if(typeof node.nodeIndex == "number"){
19067             return node.nodeIndex;
19068         }
19069         var ns = this.nodes;
19070         for(var i = 0, len = ns.length; i < len; i++){
19071             if(ns[i] == node){
19072                 return i;
19073             }
19074         }
19075         return -1;
19076     }
19077 });
19078 /*
19079  * - LGPL
19080  *
19081  * based on jquery fullcalendar
19082  * 
19083  */
19084
19085 Roo.bootstrap = Roo.bootstrap || {};
19086 /**
19087  * @class Roo.bootstrap.Calendar
19088  * @extends Roo.bootstrap.Component
19089  * Bootstrap Calendar class
19090  * @cfg {Boolean} loadMask (true|false) default false
19091  * @cfg {Object} header generate the user specific header of the calendar, default false
19092
19093  * @constructor
19094  * Create a new Container
19095  * @param {Object} config The config object
19096  */
19097
19098
19099
19100 Roo.bootstrap.Calendar = function(config){
19101     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
19102      this.addEvents({
19103         /**
19104              * @event select
19105              * Fires when a date is selected
19106              * @param {DatePicker} this
19107              * @param {Date} date The selected date
19108              */
19109         'select': true,
19110         /**
19111              * @event monthchange
19112              * Fires when the displayed month changes 
19113              * @param {DatePicker} this
19114              * @param {Date} date The selected month
19115              */
19116         'monthchange': true,
19117         /**
19118              * @event evententer
19119              * Fires when mouse over an event
19120              * @param {Calendar} this
19121              * @param {event} Event
19122              */
19123         'evententer': true,
19124         /**
19125              * @event eventleave
19126              * Fires when the mouse leaves an
19127              * @param {Calendar} this
19128              * @param {event}
19129              */
19130         'eventleave': true,
19131         /**
19132              * @event eventclick
19133              * Fires when the mouse click an
19134              * @param {Calendar} this
19135              * @param {event}
19136              */
19137         'eventclick': true
19138         
19139     });
19140
19141 };
19142
19143 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
19144     
19145      /**
19146      * @cfg {Number} startDay
19147      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
19148      */
19149     startDay : 0,
19150     
19151     loadMask : false,
19152     
19153     header : false,
19154       
19155     getAutoCreate : function(){
19156         
19157         
19158         var fc_button = function(name, corner, style, content ) {
19159             return Roo.apply({},{
19160                 tag : 'span',
19161                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
19162                          (corner.length ?
19163                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
19164                             ''
19165                         ),
19166                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
19167                 unselectable: 'on'
19168             });
19169         };
19170         
19171         var header = {};
19172         
19173         if(!this.header){
19174             header = {
19175                 tag : 'table',
19176                 cls : 'fc-header',
19177                 style : 'width:100%',
19178                 cn : [
19179                     {
19180                         tag: 'tr',
19181                         cn : [
19182                             {
19183                                 tag : 'td',
19184                                 cls : 'fc-header-left',
19185                                 cn : [
19186                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
19187                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
19188                                     { tag: 'span', cls: 'fc-header-space' },
19189                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
19190
19191
19192                                 ]
19193                             },
19194
19195                             {
19196                                 tag : 'td',
19197                                 cls : 'fc-header-center',
19198                                 cn : [
19199                                     {
19200                                         tag: 'span',
19201                                         cls: 'fc-header-title',
19202                                         cn : {
19203                                             tag: 'H2',
19204                                             html : 'month / year'
19205                                         }
19206                                     }
19207
19208                                 ]
19209                             },
19210                             {
19211                                 tag : 'td',
19212                                 cls : 'fc-header-right',
19213                                 cn : [
19214                               /*      fc_button('month', 'left', '', 'month' ),
19215                                     fc_button('week', '', '', 'week' ),
19216                                     fc_button('day', 'right', '', 'day' )
19217                                 */    
19218
19219                                 ]
19220                             }
19221
19222                         ]
19223                     }
19224                 ]
19225             };
19226         }
19227         
19228         header = this.header;
19229         
19230        
19231         var cal_heads = function() {
19232             var ret = [];
19233             // fixme - handle this.
19234             
19235             for (var i =0; i < Date.dayNames.length; i++) {
19236                 var d = Date.dayNames[i];
19237                 ret.push({
19238                     tag: 'th',
19239                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
19240                     html : d.substring(0,3)
19241                 });
19242                 
19243             }
19244             ret[0].cls += ' fc-first';
19245             ret[6].cls += ' fc-last';
19246             return ret;
19247         };
19248         var cal_cell = function(n) {
19249             return  {
19250                 tag: 'td',
19251                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
19252                 cn : [
19253                     {
19254                         cn : [
19255                             {
19256                                 cls: 'fc-day-number',
19257                                 html: 'D'
19258                             },
19259                             {
19260                                 cls: 'fc-day-content',
19261                              
19262                                 cn : [
19263                                      {
19264                                         style: 'position: relative;' // height: 17px;
19265                                     }
19266                                 ]
19267                             }
19268                             
19269                             
19270                         ]
19271                     }
19272                 ]
19273                 
19274             }
19275         };
19276         var cal_rows = function() {
19277             
19278             var ret = [];
19279             for (var r = 0; r < 6; r++) {
19280                 var row= {
19281                     tag : 'tr',
19282                     cls : 'fc-week',
19283                     cn : []
19284                 };
19285                 
19286                 for (var i =0; i < Date.dayNames.length; i++) {
19287                     var d = Date.dayNames[i];
19288                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19289
19290                 }
19291                 row.cn[0].cls+=' fc-first';
19292                 row.cn[0].cn[0].style = 'min-height:90px';
19293                 row.cn[6].cls+=' fc-last';
19294                 ret.push(row);
19295                 
19296             }
19297             ret[0].cls += ' fc-first';
19298             ret[4].cls += ' fc-prev-last';
19299             ret[5].cls += ' fc-last';
19300             return ret;
19301             
19302         };
19303         
19304         var cal_table = {
19305             tag: 'table',
19306             cls: 'fc-border-separate',
19307             style : 'width:100%',
19308             cellspacing  : 0,
19309             cn : [
19310                 { 
19311                     tag: 'thead',
19312                     cn : [
19313                         { 
19314                             tag: 'tr',
19315                             cls : 'fc-first fc-last',
19316                             cn : cal_heads()
19317                         }
19318                     ]
19319                 },
19320                 { 
19321                     tag: 'tbody',
19322                     cn : cal_rows()
19323                 }
19324                   
19325             ]
19326         };
19327          
19328          var cfg = {
19329             cls : 'fc fc-ltr',
19330             cn : [
19331                 header,
19332                 {
19333                     cls : 'fc-content',
19334                     style : "position: relative;",
19335                     cn : [
19336                         {
19337                             cls : 'fc-view fc-view-month fc-grid',
19338                             style : 'position: relative',
19339                             unselectable : 'on',
19340                             cn : [
19341                                 {
19342                                     cls : 'fc-event-container',
19343                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19344                                 },
19345                                 cal_table
19346                             ]
19347                         }
19348                     ]
19349     
19350                 }
19351            ] 
19352             
19353         };
19354         
19355          
19356         
19357         return cfg;
19358     },
19359     
19360     
19361     initEvents : function()
19362     {
19363         if(!this.store){
19364             throw "can not find store for calendar";
19365         }
19366         
19367         var mark = {
19368             tag: "div",
19369             cls:"x-dlg-mask",
19370             style: "text-align:center",
19371             cn: [
19372                 {
19373                     tag: "div",
19374                     style: "background-color:white;width:50%;margin:250 auto",
19375                     cn: [
19376                         {
19377                             tag: "img",
19378                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19379                         },
19380                         {
19381                             tag: "span",
19382                             html: "Loading"
19383                         }
19384                         
19385                     ]
19386                 }
19387             ]
19388         };
19389         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19390         
19391         var size = this.el.select('.fc-content', true).first().getSize();
19392         this.maskEl.setSize(size.width, size.height);
19393         this.maskEl.enableDisplayMode("block");
19394         if(!this.loadMask){
19395             this.maskEl.hide();
19396         }
19397         
19398         this.store = Roo.factory(this.store, Roo.data);
19399         this.store.on('load', this.onLoad, this);
19400         this.store.on('beforeload', this.onBeforeLoad, this);
19401         
19402         this.resize();
19403         
19404         this.cells = this.el.select('.fc-day',true);
19405         //Roo.log(this.cells);
19406         this.textNodes = this.el.query('.fc-day-number');
19407         this.cells.addClassOnOver('fc-state-hover');
19408         
19409         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19410         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19411         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19412         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19413         
19414         this.on('monthchange', this.onMonthChange, this);
19415         
19416         this.update(new Date().clearTime());
19417     },
19418     
19419     resize : function() {
19420         var sz  = this.el.getSize();
19421         
19422         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19423         this.el.select('.fc-day-content div',true).setHeight(34);
19424     },
19425     
19426     
19427     // private
19428     showPrevMonth : function(e){
19429         this.update(this.activeDate.add("mo", -1));
19430     },
19431     showToday : function(e){
19432         this.update(new Date().clearTime());
19433     },
19434     // private
19435     showNextMonth : function(e){
19436         this.update(this.activeDate.add("mo", 1));
19437     },
19438
19439     // private
19440     showPrevYear : function(){
19441         this.update(this.activeDate.add("y", -1));
19442     },
19443
19444     // private
19445     showNextYear : function(){
19446         this.update(this.activeDate.add("y", 1));
19447     },
19448
19449     
19450    // private
19451     update : function(date)
19452     {
19453         var vd = this.activeDate;
19454         this.activeDate = date;
19455 //        if(vd && this.el){
19456 //            var t = date.getTime();
19457 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19458 //                Roo.log('using add remove');
19459 //                
19460 //                this.fireEvent('monthchange', this, date);
19461 //                
19462 //                this.cells.removeClass("fc-state-highlight");
19463 //                this.cells.each(function(c){
19464 //                   if(c.dateValue == t){
19465 //                       c.addClass("fc-state-highlight");
19466 //                       setTimeout(function(){
19467 //                            try{c.dom.firstChild.focus();}catch(e){}
19468 //                       }, 50);
19469 //                       return false;
19470 //                   }
19471 //                   return true;
19472 //                });
19473 //                return;
19474 //            }
19475 //        }
19476         
19477         var days = date.getDaysInMonth();
19478         
19479         var firstOfMonth = date.getFirstDateOfMonth();
19480         var startingPos = firstOfMonth.getDay()-this.startDay;
19481         
19482         if(startingPos < this.startDay){
19483             startingPos += 7;
19484         }
19485         
19486         var pm = date.add(Date.MONTH, -1);
19487         var prevStart = pm.getDaysInMonth()-startingPos;
19488 //        
19489         this.cells = this.el.select('.fc-day',true);
19490         this.textNodes = this.el.query('.fc-day-number');
19491         this.cells.addClassOnOver('fc-state-hover');
19492         
19493         var cells = this.cells.elements;
19494         var textEls = this.textNodes;
19495         
19496         Roo.each(cells, function(cell){
19497             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19498         });
19499         
19500         days += startingPos;
19501
19502         // convert everything to numbers so it's fast
19503         var day = 86400000;
19504         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19505         //Roo.log(d);
19506         //Roo.log(pm);
19507         //Roo.log(prevStart);
19508         
19509         var today = new Date().clearTime().getTime();
19510         var sel = date.clearTime().getTime();
19511         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19512         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19513         var ddMatch = this.disabledDatesRE;
19514         var ddText = this.disabledDatesText;
19515         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19516         var ddaysText = this.disabledDaysText;
19517         var format = this.format;
19518         
19519         var setCellClass = function(cal, cell){
19520             cell.row = 0;
19521             cell.events = [];
19522             cell.more = [];
19523             //Roo.log('set Cell Class');
19524             cell.title = "";
19525             var t = d.getTime();
19526             
19527             //Roo.log(d);
19528             
19529             cell.dateValue = t;
19530             if(t == today){
19531                 cell.className += " fc-today";
19532                 cell.className += " fc-state-highlight";
19533                 cell.title = cal.todayText;
19534             }
19535             if(t == sel){
19536                 // disable highlight in other month..
19537                 //cell.className += " fc-state-highlight";
19538                 
19539             }
19540             // disabling
19541             if(t < min) {
19542                 cell.className = " fc-state-disabled";
19543                 cell.title = cal.minText;
19544                 return;
19545             }
19546             if(t > max) {
19547                 cell.className = " fc-state-disabled";
19548                 cell.title = cal.maxText;
19549                 return;
19550             }
19551             if(ddays){
19552                 if(ddays.indexOf(d.getDay()) != -1){
19553                     cell.title = ddaysText;
19554                     cell.className = " fc-state-disabled";
19555                 }
19556             }
19557             if(ddMatch && format){
19558                 var fvalue = d.dateFormat(format);
19559                 if(ddMatch.test(fvalue)){
19560                     cell.title = ddText.replace("%0", fvalue);
19561                     cell.className = " fc-state-disabled";
19562                 }
19563             }
19564             
19565             if (!cell.initialClassName) {
19566                 cell.initialClassName = cell.dom.className;
19567             }
19568             
19569             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19570         };
19571
19572         var i = 0;
19573         
19574         for(; i < startingPos; i++) {
19575             textEls[i].innerHTML = (++prevStart);
19576             d.setDate(d.getDate()+1);
19577             
19578             cells[i].className = "fc-past fc-other-month";
19579             setCellClass(this, cells[i]);
19580         }
19581         
19582         var intDay = 0;
19583         
19584         for(; i < days; i++){
19585             intDay = i - startingPos + 1;
19586             textEls[i].innerHTML = (intDay);
19587             d.setDate(d.getDate()+1);
19588             
19589             cells[i].className = ''; // "x-date-active";
19590             setCellClass(this, cells[i]);
19591         }
19592         var extraDays = 0;
19593         
19594         for(; i < 42; i++) {
19595             textEls[i].innerHTML = (++extraDays);
19596             d.setDate(d.getDate()+1);
19597             
19598             cells[i].className = "fc-future fc-other-month";
19599             setCellClass(this, cells[i]);
19600         }
19601         
19602         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19603         
19604         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19605         
19606         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19607         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19608         
19609         if(totalRows != 6){
19610             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19611             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19612         }
19613         
19614         this.fireEvent('monthchange', this, date);
19615         
19616         
19617         /*
19618         if(!this.internalRender){
19619             var main = this.el.dom.firstChild;
19620             var w = main.offsetWidth;
19621             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19622             Roo.fly(main).setWidth(w);
19623             this.internalRender = true;
19624             // opera does not respect the auto grow header center column
19625             // then, after it gets a width opera refuses to recalculate
19626             // without a second pass
19627             if(Roo.isOpera && !this.secondPass){
19628                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19629                 this.secondPass = true;
19630                 this.update.defer(10, this, [date]);
19631             }
19632         }
19633         */
19634         
19635     },
19636     
19637     findCell : function(dt) {
19638         dt = dt.clearTime().getTime();
19639         var ret = false;
19640         this.cells.each(function(c){
19641             //Roo.log("check " +c.dateValue + '?=' + dt);
19642             if(c.dateValue == dt){
19643                 ret = c;
19644                 return false;
19645             }
19646             return true;
19647         });
19648         
19649         return ret;
19650     },
19651     
19652     findCells : function(ev) {
19653         var s = ev.start.clone().clearTime().getTime();
19654        // Roo.log(s);
19655         var e= ev.end.clone().clearTime().getTime();
19656        // Roo.log(e);
19657         var ret = [];
19658         this.cells.each(function(c){
19659              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19660             
19661             if(c.dateValue > e){
19662                 return ;
19663             }
19664             if(c.dateValue < s){
19665                 return ;
19666             }
19667             ret.push(c);
19668         });
19669         
19670         return ret;    
19671     },
19672     
19673 //    findBestRow: function(cells)
19674 //    {
19675 //        var ret = 0;
19676 //        
19677 //        for (var i =0 ; i < cells.length;i++) {
19678 //            ret  = Math.max(cells[i].rows || 0,ret);
19679 //        }
19680 //        return ret;
19681 //        
19682 //    },
19683     
19684     
19685     addItem : function(ev)
19686     {
19687         // look for vertical location slot in
19688         var cells = this.findCells(ev);
19689         
19690 //        ev.row = this.findBestRow(cells);
19691         
19692         // work out the location.
19693         
19694         var crow = false;
19695         var rows = [];
19696         for(var i =0; i < cells.length; i++) {
19697             
19698             cells[i].row = cells[0].row;
19699             
19700             if(i == 0){
19701                 cells[i].row = cells[i].row + 1;
19702             }
19703             
19704             if (!crow) {
19705                 crow = {
19706                     start : cells[i],
19707                     end :  cells[i]
19708                 };
19709                 continue;
19710             }
19711             if (crow.start.getY() == cells[i].getY()) {
19712                 // on same row.
19713                 crow.end = cells[i];
19714                 continue;
19715             }
19716             // different row.
19717             rows.push(crow);
19718             crow = {
19719                 start: cells[i],
19720                 end : cells[i]
19721             };
19722             
19723         }
19724         
19725         rows.push(crow);
19726         ev.els = [];
19727         ev.rows = rows;
19728         ev.cells = cells;
19729         
19730         cells[0].events.push(ev);
19731         
19732         this.calevents.push(ev);
19733     },
19734     
19735     clearEvents: function() {
19736         
19737         if(!this.calevents){
19738             return;
19739         }
19740         
19741         Roo.each(this.cells.elements, function(c){
19742             c.row = 0;
19743             c.events = [];
19744             c.more = [];
19745         });
19746         
19747         Roo.each(this.calevents, function(e) {
19748             Roo.each(e.els, function(el) {
19749                 el.un('mouseenter' ,this.onEventEnter, this);
19750                 el.un('mouseleave' ,this.onEventLeave, this);
19751                 el.remove();
19752             },this);
19753         },this);
19754         
19755         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19756             e.remove();
19757         });
19758         
19759     },
19760     
19761     renderEvents: function()
19762     {   
19763         var _this = this;
19764         
19765         this.cells.each(function(c) {
19766             
19767             if(c.row < 5){
19768                 return;
19769             }
19770             
19771             var ev = c.events;
19772             
19773             var r = 4;
19774             if(c.row != c.events.length){
19775                 r = 4 - (4 - (c.row - c.events.length));
19776             }
19777             
19778             c.events = ev.slice(0, r);
19779             c.more = ev.slice(r);
19780             
19781             if(c.more.length && c.more.length == 1){
19782                 c.events.push(c.more.pop());
19783             }
19784             
19785             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19786             
19787         });
19788             
19789         this.cells.each(function(c) {
19790             
19791             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19792             
19793             
19794             for (var e = 0; e < c.events.length; e++){
19795                 var ev = c.events[e];
19796                 var rows = ev.rows;
19797                 
19798                 for(var i = 0; i < rows.length; i++) {
19799                 
19800                     // how many rows should it span..
19801
19802                     var  cfg = {
19803                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19804                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19805
19806                         unselectable : "on",
19807                         cn : [
19808                             {
19809                                 cls: 'fc-event-inner',
19810                                 cn : [
19811     //                                {
19812     //                                  tag:'span',
19813     //                                  cls: 'fc-event-time',
19814     //                                  html : cells.length > 1 ? '' : ev.time
19815     //                                },
19816                                     {
19817                                       tag:'span',
19818                                       cls: 'fc-event-title',
19819                                       html : String.format('{0}', ev.title)
19820                                     }
19821
19822
19823                                 ]
19824                             },
19825                             {
19826                                 cls: 'ui-resizable-handle ui-resizable-e',
19827                                 html : '&nbsp;&nbsp;&nbsp'
19828                             }
19829
19830                         ]
19831                     };
19832
19833                     if (i == 0) {
19834                         cfg.cls += ' fc-event-start';
19835                     }
19836                     if ((i+1) == rows.length) {
19837                         cfg.cls += ' fc-event-end';
19838                     }
19839
19840                     var ctr = _this.el.select('.fc-event-container',true).first();
19841                     var cg = ctr.createChild(cfg);
19842
19843                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19844                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19845
19846                     var r = (c.more.length) ? 1 : 0;
19847                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19848                     cg.setWidth(ebox.right - sbox.x -2);
19849
19850                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19851                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19852                     cg.on('click', _this.onEventClick, _this, ev);
19853
19854                     ev.els.push(cg);
19855                     
19856                 }
19857                 
19858             }
19859             
19860             
19861             if(c.more.length){
19862                 var  cfg = {
19863                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19864                     style : 'position: absolute',
19865                     unselectable : "on",
19866                     cn : [
19867                         {
19868                             cls: 'fc-event-inner',
19869                             cn : [
19870                                 {
19871                                   tag:'span',
19872                                   cls: 'fc-event-title',
19873                                   html : 'More'
19874                                 }
19875
19876
19877                             ]
19878                         },
19879                         {
19880                             cls: 'ui-resizable-handle ui-resizable-e',
19881                             html : '&nbsp;&nbsp;&nbsp'
19882                         }
19883
19884                     ]
19885                 };
19886
19887                 var ctr = _this.el.select('.fc-event-container',true).first();
19888                 var cg = ctr.createChild(cfg);
19889
19890                 var sbox = c.select('.fc-day-content',true).first().getBox();
19891                 var ebox = c.select('.fc-day-content',true).first().getBox();
19892                 //Roo.log(cg);
19893                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19894                 cg.setWidth(ebox.right - sbox.x -2);
19895
19896                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19897                 
19898             }
19899             
19900         });
19901         
19902         
19903         
19904     },
19905     
19906     onEventEnter: function (e, el,event,d) {
19907         this.fireEvent('evententer', this, el, event);
19908     },
19909     
19910     onEventLeave: function (e, el,event,d) {
19911         this.fireEvent('eventleave', this, el, event);
19912     },
19913     
19914     onEventClick: function (e, el,event,d) {
19915         this.fireEvent('eventclick', this, el, event);
19916     },
19917     
19918     onMonthChange: function () {
19919         this.store.load();
19920     },
19921     
19922     onMoreEventClick: function(e, el, more)
19923     {
19924         var _this = this;
19925         
19926         this.calpopover.placement = 'right';
19927         this.calpopover.setTitle('More');
19928         
19929         this.calpopover.setContent('');
19930         
19931         var ctr = this.calpopover.el.select('.popover-content', true).first();
19932         
19933         Roo.each(more, function(m){
19934             var cfg = {
19935                 cls : 'fc-event-hori fc-event-draggable',
19936                 html : m.title
19937             };
19938             var cg = ctr.createChild(cfg);
19939             
19940             cg.on('click', _this.onEventClick, _this, m);
19941         });
19942         
19943         this.calpopover.show(el);
19944         
19945         
19946     },
19947     
19948     onLoad: function () 
19949     {   
19950         this.calevents = [];
19951         var cal = this;
19952         
19953         if(this.store.getCount() > 0){
19954             this.store.data.each(function(d){
19955                cal.addItem({
19956                     id : d.data.id,
19957                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19958                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19959                     time : d.data.start_time,
19960                     title : d.data.title,
19961                     description : d.data.description,
19962                     venue : d.data.venue
19963                 });
19964             });
19965         }
19966         
19967         this.renderEvents();
19968         
19969         if(this.calevents.length && this.loadMask){
19970             this.maskEl.hide();
19971         }
19972     },
19973     
19974     onBeforeLoad: function()
19975     {
19976         this.clearEvents();
19977         if(this.loadMask){
19978             this.maskEl.show();
19979         }
19980     }
19981 });
19982
19983  
19984  /*
19985  * - LGPL
19986  *
19987  * element
19988  * 
19989  */
19990
19991 /**
19992  * @class Roo.bootstrap.Popover
19993  * @extends Roo.bootstrap.Component
19994  * Bootstrap Popover class
19995  * @cfg {String} html contents of the popover   (or false to use children..)
19996  * @cfg {String} title of popover (or false to hide)
19997  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19998  * @cfg {String} trigger click || hover (or false to trigger manually)
19999  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
20000  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
20001  *      - if false and it has a 'parent' then it will be automatically added to that element
20002  *      - if string - Roo.get  will be called 
20003  * @cfg {Number} delay - delay before showing
20004  
20005  * @constructor
20006  * Create a new Popover
20007  * @param {Object} config The config object
20008  */
20009
20010 Roo.bootstrap.Popover = function(config){
20011     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
20012     
20013     this.addEvents({
20014         // raw events
20015          /**
20016          * @event show
20017          * After the popover show
20018          * 
20019          * @param {Roo.bootstrap.Popover} this
20020          */
20021         "show" : true,
20022         /**
20023          * @event hide
20024          * After the popover hide
20025          * 
20026          * @param {Roo.bootstrap.Popover} this
20027          */
20028         "hide" : true
20029     });
20030 };
20031
20032 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
20033     
20034     title: false,
20035     html: false,
20036     
20037     placement : 'right',
20038     trigger : 'hover', // hover
20039     modal : false,
20040     delay : 0,
20041     
20042     over: false,
20043     
20044     can_build_overlaid : false,
20045     
20046     maskEl : false, // the mask element
20047     headerEl : false,
20048     contentEl : false,
20049     alignEl : false, // when show is called with an element - this get's stored.
20050     
20051     getChildContainer : function()
20052     {
20053         return this.contentEl;
20054         
20055     },
20056     getPopoverHeader : function()
20057     {
20058         this.title = true; // flag not to hide it..
20059         this.headerEl.addClass('p-0');
20060         return this.headerEl
20061     },
20062     
20063     
20064     getAutoCreate : function(){
20065          
20066         var cfg = {
20067            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
20068            style: 'display:block',
20069            cn : [
20070                 {
20071                     cls : 'arrow'
20072                 },
20073                 {
20074                     cls : 'popover-inner ',
20075                     cn : [
20076                         {
20077                             tag: 'h3',
20078                             cls: 'popover-title popover-header',
20079                             html : this.title === false ? '' : this.title
20080                         },
20081                         {
20082                             cls : 'popover-content popover-body '  + (this.cls || ''),
20083                             html : this.html || ''
20084                         }
20085                     ]
20086                     
20087                 }
20088            ]
20089         };
20090         
20091         return cfg;
20092     },
20093     /**
20094      * @param {string} the title
20095      */
20096     setTitle: function(str)
20097     {
20098         this.title = str;
20099         if (this.el) {
20100             this.headerEl.dom.innerHTML = str;
20101         }
20102         
20103     },
20104     /**
20105      * @param {string} the body content
20106      */
20107     setContent: function(str)
20108     {
20109         this.html = str;
20110         if (this.contentEl) {
20111             this.contentEl.dom.innerHTML = str;
20112         }
20113         
20114     },
20115     // as it get's added to the bottom of the page.
20116     onRender : function(ct, position)
20117     {
20118         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
20119         
20120         
20121         
20122         if(!this.el){
20123             var cfg = Roo.apply({},  this.getAutoCreate());
20124             cfg.id = Roo.id();
20125             
20126             if (this.cls) {
20127                 cfg.cls += ' ' + this.cls;
20128             }
20129             if (this.style) {
20130                 cfg.style = this.style;
20131             }
20132             //Roo.log("adding to ");
20133             this.el = Roo.get(document.body).createChild(cfg, position);
20134 //            Roo.log(this.el);
20135         }
20136         
20137         this.contentEl = this.el.select('.popover-content',true).first();
20138         this.headerEl =  this.el.select('.popover-title',true).first();
20139         
20140         var nitems = [];
20141         if(typeof(this.items) != 'undefined'){
20142             var items = this.items;
20143             delete this.items;
20144
20145             for(var i =0;i < items.length;i++) {
20146                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
20147             }
20148         }
20149
20150         this.items = nitems;
20151         
20152         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
20153         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
20154         
20155         
20156         
20157         this.initEvents();
20158     },
20159     
20160     resizeMask : function()
20161     {
20162         this.maskEl.setSize(
20163             Roo.lib.Dom.getViewWidth(true),
20164             Roo.lib.Dom.getViewHeight(true)
20165         );
20166     },
20167     
20168     initEvents : function()
20169     {
20170         
20171         if (!this.modal) { 
20172             Roo.bootstrap.Popover.register(this);
20173         }
20174          
20175         this.arrowEl = this.el.select('.arrow',true).first();
20176         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
20177         this.el.enableDisplayMode('block');
20178         this.el.hide();
20179  
20180         
20181         if (this.over === false && !this.parent()) {
20182             return; 
20183         }
20184         if (this.triggers === false) {
20185             return;
20186         }
20187          
20188         // support parent
20189         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
20190         var triggers = this.trigger ? this.trigger.split(' ') : [];
20191         Roo.each(triggers, function(trigger) {
20192         
20193             if (trigger == 'click') {
20194                 on_el.on('click', this.toggle, this);
20195             } else if (trigger != 'manual') {
20196                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
20197                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
20198       
20199                 on_el.on(eventIn  ,this.enter, this);
20200                 on_el.on(eventOut, this.leave, this);
20201             }
20202         }, this);
20203     },
20204     
20205     
20206     // private
20207     timeout : null,
20208     hoverState : null,
20209     
20210     toggle : function () {
20211         this.hoverState == 'in' ? this.leave() : this.enter();
20212     },
20213     
20214     enter : function () {
20215         
20216         clearTimeout(this.timeout);
20217     
20218         this.hoverState = 'in';
20219     
20220         if (!this.delay || !this.delay.show) {
20221             this.show();
20222             return;
20223         }
20224         var _t = this;
20225         this.timeout = setTimeout(function () {
20226             if (_t.hoverState == 'in') {
20227                 _t.show();
20228             }
20229         }, this.delay.show)
20230     },
20231     
20232     leave : function() {
20233         clearTimeout(this.timeout);
20234     
20235         this.hoverState = 'out';
20236     
20237         if (!this.delay || !this.delay.hide) {
20238             this.hide();
20239             return;
20240         }
20241         var _t = this;
20242         this.timeout = setTimeout(function () {
20243             if (_t.hoverState == 'out') {
20244                 _t.hide();
20245             }
20246         }, this.delay.hide)
20247     },
20248     /**
20249      * Show the popover
20250      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
20251      * @param {string} (left|right|top|bottom) position
20252      */
20253     show : function (on_el, placement)
20254     {
20255         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
20256         on_el = on_el || false; // default to false
20257          
20258         if (!on_el) {
20259             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
20260                 on_el = this.parent().el;
20261             } else if (this.over) {
20262                 on_el = Roo.get(this.over);
20263             }
20264             
20265         }
20266         
20267         this.alignEl = Roo.get( on_el );
20268
20269         if (!this.el) {
20270             this.render(document.body);
20271         }
20272         
20273         
20274          
20275         
20276         if (this.title === false) {
20277             this.headerEl.hide();
20278         }
20279         
20280        
20281         this.el.show();
20282         this.el.dom.style.display = 'block';
20283          
20284  
20285         if (this.alignEl) {
20286             this.updatePosition(this.placement, true);
20287              
20288         } else {
20289             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20290             var es = this.el.getSize();
20291             var x = Roo.lib.Dom.getViewWidth()/2;
20292             var y = Roo.lib.Dom.getViewHeight()/2;
20293             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20294             
20295         }
20296
20297         
20298         //var arrow = this.el.select('.arrow',true).first();
20299         //arrow.set(align[2], 
20300         
20301         this.el.addClass('in');
20302         
20303          
20304         
20305         this.hoverState = 'in';
20306         
20307         if (this.modal) {
20308             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20309             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20310             this.maskEl.dom.style.display = 'block';
20311             this.maskEl.addClass('show');
20312         }
20313         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20314  
20315         this.fireEvent('show', this);
20316         
20317     },
20318     /**
20319      * fire this manually after loading a grid in the table for example
20320      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20321      * @param {Boolean} try and move it if we cant get right position.
20322      */
20323     updatePosition : function(placement, try_move)
20324     {
20325         // allow for calling with no parameters
20326         placement = placement   ? placement :  this.placement;
20327         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20328         
20329         this.el.removeClass([
20330             'fade','top','bottom', 'left', 'right','in',
20331             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20332         ]);
20333         this.el.addClass(placement + ' bs-popover-' + placement);
20334         
20335         if (!this.alignEl ) {
20336             return false;
20337         }
20338         
20339         switch (placement) {
20340             case 'right':
20341                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20342                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20343                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20344                     //normal display... or moved up/down.
20345                     this.el.setXY(offset);
20346                     var xy = this.alignEl.getAnchorXY('tr', false);
20347                     xy[0]+=2;xy[1]+=5;
20348                     this.arrowEl.setXY(xy);
20349                     return true;
20350                 }
20351                 // continue through...
20352                 return this.updatePosition('left', false);
20353                 
20354             
20355             case 'left':
20356                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20357                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20358                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20359                     //normal display... or moved up/down.
20360                     this.el.setXY(offset);
20361                     var xy = this.alignEl.getAnchorXY('tl', false);
20362                     xy[0]-=10;xy[1]+=5; // << fix me
20363                     this.arrowEl.setXY(xy);
20364                     return true;
20365                 }
20366                 // call self...
20367                 return this.updatePosition('right', false);
20368             
20369             case 'top':
20370                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20371                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20372                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20373                     //normal display... or moved up/down.
20374                     this.el.setXY(offset);
20375                     var xy = this.alignEl.getAnchorXY('t', false);
20376                     xy[1]-=10; // << fix me
20377                     this.arrowEl.setXY(xy);
20378                     return true;
20379                 }
20380                 // fall through
20381                return this.updatePosition('bottom', false);
20382             
20383             case 'bottom':
20384                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20385                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20386                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20387                     //normal display... or moved up/down.
20388                     this.el.setXY(offset);
20389                     var xy = this.alignEl.getAnchorXY('b', false);
20390                      xy[1]+=2; // << fix me
20391                     this.arrowEl.setXY(xy);
20392                     return true;
20393                 }
20394                 // fall through
20395                 return this.updatePosition('top', false);
20396                 
20397             
20398         }
20399         
20400         
20401         return false;
20402     },
20403     
20404     hide : function()
20405     {
20406         this.el.setXY([0,0]);
20407         this.el.removeClass('in');
20408         this.el.hide();
20409         this.hoverState = null;
20410         this.maskEl.hide(); // always..
20411         this.fireEvent('hide', this);
20412     }
20413     
20414 });
20415
20416
20417 Roo.apply(Roo.bootstrap.Popover, {
20418
20419     alignment : {
20420         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20421         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20422         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20423         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20424     },
20425     
20426     zIndex : 20001,
20427
20428     clickHander : false,
20429     
20430     
20431
20432     onMouseDown : function(e)
20433     {
20434         if (this.popups.length &&  !e.getTarget(".roo-popover")) {
20435             /// what is nothing is showing..
20436             this.hideAll();
20437         }
20438          
20439     },
20440     
20441     
20442     popups : [],
20443     
20444     register : function(popup)
20445     {
20446         if (!Roo.bootstrap.Popover.clickHandler) {
20447             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20448         }
20449         // hide other popups.
20450         popup.on('show', Roo.bootstrap.Popover.onShow,  popup);
20451         popup.on('hide', Roo.bootstrap.Popover.onHide,  popup);
20452         this.hideAll(); //<< why?
20453         //this.popups.push(popup);
20454     },
20455     hideAll : function()
20456     {
20457         this.popups.forEach(function(p) {
20458             p.hide();
20459         });
20460     },
20461     onShow : function() {
20462         Roo.bootstrap.Popover.popups.push(this);
20463     },
20464     onHide : function() {
20465         Roo.bootstrap.Popover.popups.remove(this);
20466     } 
20467
20468 });/*
20469  * - LGPL
20470  *
20471  * Card header - holder for the card header elements.
20472  * 
20473  */
20474
20475 /**
20476  * @class Roo.bootstrap.PopoverNav
20477  * @extends Roo.bootstrap.NavGroup
20478  * Bootstrap Popover header navigation class
20479  * @constructor
20480  * Create a new Popover Header Navigation 
20481  * @param {Object} config The config object
20482  */
20483
20484 Roo.bootstrap.PopoverNav = function(config){
20485     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20486 };
20487
20488 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20489     
20490     
20491     container_method : 'getPopoverHeader' 
20492     
20493      
20494     
20495     
20496    
20497 });
20498
20499  
20500
20501  /*
20502  * - LGPL
20503  *
20504  * Progress
20505  * 
20506  */
20507
20508 /**
20509  * @class Roo.bootstrap.Progress
20510  * @extends Roo.bootstrap.Component
20511  * Bootstrap Progress class
20512  * @cfg {Boolean} striped striped of the progress bar
20513  * @cfg {Boolean} active animated of the progress bar
20514  * 
20515  * 
20516  * @constructor
20517  * Create a new Progress
20518  * @param {Object} config The config object
20519  */
20520
20521 Roo.bootstrap.Progress = function(config){
20522     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20523 };
20524
20525 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20526     
20527     striped : false,
20528     active: false,
20529     
20530     getAutoCreate : function(){
20531         var cfg = {
20532             tag: 'div',
20533             cls: 'progress'
20534         };
20535         
20536         
20537         if(this.striped){
20538             cfg.cls += ' progress-striped';
20539         }
20540       
20541         if(this.active){
20542             cfg.cls += ' active';
20543         }
20544         
20545         
20546         return cfg;
20547     }
20548    
20549 });
20550
20551  
20552
20553  /*
20554  * - LGPL
20555  *
20556  * ProgressBar
20557  * 
20558  */
20559
20560 /**
20561  * @class Roo.bootstrap.ProgressBar
20562  * @extends Roo.bootstrap.Component
20563  * Bootstrap ProgressBar class
20564  * @cfg {Number} aria_valuenow aria-value now
20565  * @cfg {Number} aria_valuemin aria-value min
20566  * @cfg {Number} aria_valuemax aria-value max
20567  * @cfg {String} label label for the progress bar
20568  * @cfg {String} panel (success | info | warning | danger )
20569  * @cfg {String} role role of the progress bar
20570  * @cfg {String} sr_only text
20571  * 
20572  * 
20573  * @constructor
20574  * Create a new ProgressBar
20575  * @param {Object} config The config object
20576  */
20577
20578 Roo.bootstrap.ProgressBar = function(config){
20579     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20580 };
20581
20582 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20583     
20584     aria_valuenow : 0,
20585     aria_valuemin : 0,
20586     aria_valuemax : 100,
20587     label : false,
20588     panel : false,
20589     role : false,
20590     sr_only: false,
20591     
20592     getAutoCreate : function()
20593     {
20594         
20595         var cfg = {
20596             tag: 'div',
20597             cls: 'progress-bar',
20598             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20599         };
20600         
20601         if(this.sr_only){
20602             cfg.cn = {
20603                 tag: 'span',
20604                 cls: 'sr-only',
20605                 html: this.sr_only
20606             }
20607         }
20608         
20609         if(this.role){
20610             cfg.role = this.role;
20611         }
20612         
20613         if(this.aria_valuenow){
20614             cfg['aria-valuenow'] = this.aria_valuenow;
20615         }
20616         
20617         if(this.aria_valuemin){
20618             cfg['aria-valuemin'] = this.aria_valuemin;
20619         }
20620         
20621         if(this.aria_valuemax){
20622             cfg['aria-valuemax'] = this.aria_valuemax;
20623         }
20624         
20625         if(this.label && !this.sr_only){
20626             cfg.html = this.label;
20627         }
20628         
20629         if(this.panel){
20630             cfg.cls += ' progress-bar-' + this.panel;
20631         }
20632         
20633         return cfg;
20634     },
20635     
20636     update : function(aria_valuenow)
20637     {
20638         this.aria_valuenow = aria_valuenow;
20639         
20640         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20641     }
20642    
20643 });
20644
20645  
20646
20647  /*
20648  * - LGPL
20649  *
20650  * column
20651  * 
20652  */
20653
20654 /**
20655  * @class Roo.bootstrap.TabGroup
20656  * @extends Roo.bootstrap.Column
20657  * Bootstrap Column class
20658  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20659  * @cfg {Boolean} carousel true to make the group behave like a carousel
20660  * @cfg {Boolean} bullets show bullets for the panels
20661  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20662  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20663  * @cfg {Boolean} showarrow (true|false) show arrow default true
20664  * 
20665  * @constructor
20666  * Create a new TabGroup
20667  * @param {Object} config The config object
20668  */
20669
20670 Roo.bootstrap.TabGroup = function(config){
20671     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20672     if (!this.navId) {
20673         this.navId = Roo.id();
20674     }
20675     this.tabs = [];
20676     Roo.bootstrap.TabGroup.register(this);
20677     
20678 };
20679
20680 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20681     
20682     carousel : false,
20683     transition : false,
20684     bullets : 0,
20685     timer : 0,
20686     autoslide : false,
20687     slideFn : false,
20688     slideOnTouch : false,
20689     showarrow : true,
20690     
20691     getAutoCreate : function()
20692     {
20693         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20694         
20695         cfg.cls += ' tab-content';
20696         
20697         if (this.carousel) {
20698             cfg.cls += ' carousel slide';
20699             
20700             cfg.cn = [{
20701                cls : 'carousel-inner',
20702                cn : []
20703             }];
20704         
20705             if(this.bullets  && !Roo.isTouch){
20706                 
20707                 var bullets = {
20708                     cls : 'carousel-bullets',
20709                     cn : []
20710                 };
20711                
20712                 if(this.bullets_cls){
20713                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20714                 }
20715                 
20716                 bullets.cn.push({
20717                     cls : 'clear'
20718                 });
20719                 
20720                 cfg.cn[0].cn.push(bullets);
20721             }
20722             
20723             if(this.showarrow){
20724                 cfg.cn[0].cn.push({
20725                     tag : 'div',
20726                     class : 'carousel-arrow',
20727                     cn : [
20728                         {
20729                             tag : 'div',
20730                             class : 'carousel-prev',
20731                             cn : [
20732                                 {
20733                                     tag : 'i',
20734                                     class : 'fa fa-chevron-left'
20735                                 }
20736                             ]
20737                         },
20738                         {
20739                             tag : 'div',
20740                             class : 'carousel-next',
20741                             cn : [
20742                                 {
20743                                     tag : 'i',
20744                                     class : 'fa fa-chevron-right'
20745                                 }
20746                             ]
20747                         }
20748                     ]
20749                 });
20750             }
20751             
20752         }
20753         
20754         return cfg;
20755     },
20756     
20757     initEvents:  function()
20758     {
20759 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20760 //            this.el.on("touchstart", this.onTouchStart, this);
20761 //        }
20762         
20763         if(this.autoslide){
20764             var _this = this;
20765             
20766             this.slideFn = window.setInterval(function() {
20767                 _this.showPanelNext();
20768             }, this.timer);
20769         }
20770         
20771         if(this.showarrow){
20772             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20773             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20774         }
20775         
20776         
20777     },
20778     
20779 //    onTouchStart : function(e, el, o)
20780 //    {
20781 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20782 //            return;
20783 //        }
20784 //        
20785 //        this.showPanelNext();
20786 //    },
20787     
20788     
20789     getChildContainer : function()
20790     {
20791         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20792     },
20793     
20794     /**
20795     * register a Navigation item
20796     * @param {Roo.bootstrap.NavItem} the navitem to add
20797     */
20798     register : function(item)
20799     {
20800         this.tabs.push( item);
20801         item.navId = this.navId; // not really needed..
20802         this.addBullet();
20803     
20804     },
20805     
20806     getActivePanel : function()
20807     {
20808         var r = false;
20809         Roo.each(this.tabs, function(t) {
20810             if (t.active) {
20811                 r = t;
20812                 return false;
20813             }
20814             return null;
20815         });
20816         return r;
20817         
20818     },
20819     getPanelByName : function(n)
20820     {
20821         var r = false;
20822         Roo.each(this.tabs, function(t) {
20823             if (t.tabId == n) {
20824                 r = t;
20825                 return false;
20826             }
20827             return null;
20828         });
20829         return r;
20830     },
20831     indexOfPanel : function(p)
20832     {
20833         var r = false;
20834         Roo.each(this.tabs, function(t,i) {
20835             if (t.tabId == p.tabId) {
20836                 r = i;
20837                 return false;
20838             }
20839             return null;
20840         });
20841         return r;
20842     },
20843     /**
20844      * show a specific panel
20845      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20846      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20847      */
20848     showPanel : function (pan)
20849     {
20850         if(this.transition || typeof(pan) == 'undefined'){
20851             Roo.log("waiting for the transitionend");
20852             return false;
20853         }
20854         
20855         if (typeof(pan) == 'number') {
20856             pan = this.tabs[pan];
20857         }
20858         
20859         if (typeof(pan) == 'string') {
20860             pan = this.getPanelByName(pan);
20861         }
20862         
20863         var cur = this.getActivePanel();
20864         
20865         if(!pan || !cur){
20866             Roo.log('pan or acitve pan is undefined');
20867             return false;
20868         }
20869         
20870         if (pan.tabId == this.getActivePanel().tabId) {
20871             return true;
20872         }
20873         
20874         if (false === cur.fireEvent('beforedeactivate')) {
20875             return false;
20876         }
20877         
20878         if(this.bullets > 0 && !Roo.isTouch){
20879             this.setActiveBullet(this.indexOfPanel(pan));
20880         }
20881         
20882         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20883             
20884             //class="carousel-item carousel-item-next carousel-item-left"
20885             
20886             this.transition = true;
20887             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20888             var lr = dir == 'next' ? 'left' : 'right';
20889             pan.el.addClass(dir); // or prev
20890             pan.el.addClass('carousel-item-' + dir); // or prev
20891             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20892             cur.el.addClass(lr); // or right
20893             pan.el.addClass(lr);
20894             cur.el.addClass('carousel-item-' +lr); // or right
20895             pan.el.addClass('carousel-item-' +lr);
20896             
20897             
20898             var _this = this;
20899             cur.el.on('transitionend', function() {
20900                 Roo.log("trans end?");
20901                 
20902                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20903                 pan.setActive(true);
20904                 
20905                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20906                 cur.setActive(false);
20907                 
20908                 _this.transition = false;
20909                 
20910             }, this, { single:  true } );
20911             
20912             return true;
20913         }
20914         
20915         cur.setActive(false);
20916         pan.setActive(true);
20917         
20918         return true;
20919         
20920     },
20921     showPanelNext : function()
20922     {
20923         var i = this.indexOfPanel(this.getActivePanel());
20924         
20925         if (i >= this.tabs.length - 1 && !this.autoslide) {
20926             return;
20927         }
20928         
20929         if (i >= this.tabs.length - 1 && this.autoslide) {
20930             i = -1;
20931         }
20932         
20933         this.showPanel(this.tabs[i+1]);
20934     },
20935     
20936     showPanelPrev : function()
20937     {
20938         var i = this.indexOfPanel(this.getActivePanel());
20939         
20940         if (i  < 1 && !this.autoslide) {
20941             return;
20942         }
20943         
20944         if (i < 1 && this.autoslide) {
20945             i = this.tabs.length;
20946         }
20947         
20948         this.showPanel(this.tabs[i-1]);
20949     },
20950     
20951     
20952     addBullet: function()
20953     {
20954         if(!this.bullets || Roo.isTouch){
20955             return;
20956         }
20957         var ctr = this.el.select('.carousel-bullets',true).first();
20958         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20959         var bullet = ctr.createChild({
20960             cls : 'bullet bullet-' + i
20961         },ctr.dom.lastChild);
20962         
20963         
20964         var _this = this;
20965         
20966         bullet.on('click', (function(e, el, o, ii, t){
20967
20968             e.preventDefault();
20969
20970             this.showPanel(ii);
20971
20972             if(this.autoslide && this.slideFn){
20973                 clearInterval(this.slideFn);
20974                 this.slideFn = window.setInterval(function() {
20975                     _this.showPanelNext();
20976                 }, this.timer);
20977             }
20978
20979         }).createDelegate(this, [i, bullet], true));
20980                 
20981         
20982     },
20983      
20984     setActiveBullet : function(i)
20985     {
20986         if(Roo.isTouch){
20987             return;
20988         }
20989         
20990         Roo.each(this.el.select('.bullet', true).elements, function(el){
20991             el.removeClass('selected');
20992         });
20993
20994         var bullet = this.el.select('.bullet-' + i, true).first();
20995         
20996         if(!bullet){
20997             return;
20998         }
20999         
21000         bullet.addClass('selected');
21001     }
21002     
21003     
21004   
21005 });
21006
21007  
21008
21009  
21010  
21011 Roo.apply(Roo.bootstrap.TabGroup, {
21012     
21013     groups: {},
21014      /**
21015     * register a Navigation Group
21016     * @param {Roo.bootstrap.NavGroup} the navgroup to add
21017     */
21018     register : function(navgrp)
21019     {
21020         this.groups[navgrp.navId] = navgrp;
21021         
21022     },
21023     /**
21024     * fetch a Navigation Group based on the navigation ID
21025     * if one does not exist , it will get created.
21026     * @param {string} the navgroup to add
21027     * @returns {Roo.bootstrap.NavGroup} the navgroup 
21028     */
21029     get: function(navId) {
21030         if (typeof(this.groups[navId]) == 'undefined') {
21031             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
21032         }
21033         return this.groups[navId] ;
21034     }
21035     
21036     
21037     
21038 });
21039
21040  /*
21041  * - LGPL
21042  *
21043  * TabPanel
21044  * 
21045  */
21046
21047 /**
21048  * @class Roo.bootstrap.TabPanel
21049  * @extends Roo.bootstrap.Component
21050  * Bootstrap TabPanel class
21051  * @cfg {Boolean} active panel active
21052  * @cfg {String} html panel content
21053  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
21054  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
21055  * @cfg {String} href click to link..
21056  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
21057  * 
21058  * 
21059  * @constructor
21060  * Create a new TabPanel
21061  * @param {Object} config The config object
21062  */
21063
21064 Roo.bootstrap.TabPanel = function(config){
21065     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
21066     this.addEvents({
21067         /**
21068              * @event changed
21069              * Fires when the active status changes
21070              * @param {Roo.bootstrap.TabPanel} this
21071              * @param {Boolean} state the new state
21072             
21073          */
21074         'changed': true,
21075         /**
21076              * @event beforedeactivate
21077              * Fires before a tab is de-activated - can be used to do validation on a form.
21078              * @param {Roo.bootstrap.TabPanel} this
21079              * @return {Boolean} false if there is an error
21080             
21081          */
21082         'beforedeactivate': true
21083      });
21084     
21085     this.tabId = this.tabId || Roo.id();
21086   
21087 };
21088
21089 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
21090     
21091     active: false,
21092     html: false,
21093     tabId: false,
21094     navId : false,
21095     href : '',
21096     touchSlide : false,
21097     getAutoCreate : function(){
21098         
21099         
21100         var cfg = {
21101             tag: 'div',
21102             // item is needed for carousel - not sure if it has any effect otherwise
21103             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
21104             html: this.html || ''
21105         };
21106         
21107         if(this.active){
21108             cfg.cls += ' active';
21109         }
21110         
21111         if(this.tabId){
21112             cfg.tabId = this.tabId;
21113         }
21114         
21115         
21116         
21117         return cfg;
21118     },
21119     
21120     initEvents:  function()
21121     {
21122         var p = this.parent();
21123         
21124         this.navId = this.navId || p.navId;
21125         
21126         if (typeof(this.navId) != 'undefined') {
21127             // not really needed.. but just in case.. parent should be a NavGroup.
21128             var tg = Roo.bootstrap.TabGroup.get(this.navId);
21129             
21130             tg.register(this);
21131             
21132             var i = tg.tabs.length - 1;
21133             
21134             if(this.active && tg.bullets > 0 && i < tg.bullets){
21135                 tg.setActiveBullet(i);
21136             }
21137         }
21138         
21139         this.el.on('click', this.onClick, this);
21140         
21141         if(Roo.isTouch && this.touchSlide){
21142             this.el.on("touchstart", this.onTouchStart, this);
21143             this.el.on("touchmove", this.onTouchMove, this);
21144             this.el.on("touchend", this.onTouchEnd, this);
21145         }
21146         
21147     },
21148     
21149     onRender : function(ct, position)
21150     {
21151         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
21152     },
21153     
21154     setActive : function(state)
21155     {
21156         Roo.log("panel - set active " + this.tabId + "=" + state);
21157         
21158         this.active = state;
21159         if (!state) {
21160             this.el.removeClass('active');
21161             
21162         } else  if (!this.el.hasClass('active')) {
21163             this.el.addClass('active');
21164         }
21165         
21166         this.fireEvent('changed', this, state);
21167     },
21168     
21169     onClick : function(e)
21170     {
21171         e.preventDefault();
21172         
21173         if(!this.href.length){
21174             return;
21175         }
21176         
21177         window.location.href = this.href;
21178     },
21179     
21180     startX : 0,
21181     startY : 0,
21182     endX : 0,
21183     endY : 0,
21184     swiping : false,
21185     
21186     onTouchStart : function(e)
21187     {
21188         this.swiping = false;
21189         
21190         this.startX = e.browserEvent.touches[0].clientX;
21191         this.startY = e.browserEvent.touches[0].clientY;
21192     },
21193     
21194     onTouchMove : function(e)
21195     {
21196         this.swiping = true;
21197         
21198         this.endX = e.browserEvent.touches[0].clientX;
21199         this.endY = e.browserEvent.touches[0].clientY;
21200     },
21201     
21202     onTouchEnd : function(e)
21203     {
21204         if(!this.swiping){
21205             this.onClick(e);
21206             return;
21207         }
21208         
21209         var tabGroup = this.parent();
21210         
21211         if(this.endX > this.startX){ // swiping right
21212             tabGroup.showPanelPrev();
21213             return;
21214         }
21215         
21216         if(this.startX > this.endX){ // swiping left
21217             tabGroup.showPanelNext();
21218             return;
21219         }
21220     }
21221     
21222     
21223 });
21224  
21225
21226  
21227
21228  /*
21229  * - LGPL
21230  *
21231  * DateField
21232  * 
21233  */
21234
21235 /**
21236  * @class Roo.bootstrap.DateField
21237  * @extends Roo.bootstrap.Input
21238  * Bootstrap DateField class
21239  * @cfg {Number} weekStart default 0
21240  * @cfg {String} viewMode default empty, (months|years)
21241  * @cfg {String} minViewMode default empty, (months|years)
21242  * @cfg {Number} startDate default -Infinity
21243  * @cfg {Number} endDate default Infinity
21244  * @cfg {Boolean} todayHighlight default false
21245  * @cfg {Boolean} todayBtn default false
21246  * @cfg {Boolean} calendarWeeks default false
21247  * @cfg {Object} daysOfWeekDisabled default empty
21248  * @cfg {Boolean} singleMode default false (true | false)
21249  * 
21250  * @cfg {Boolean} keyboardNavigation default true
21251  * @cfg {String} language default en
21252  * 
21253  * @constructor
21254  * Create a new DateField
21255  * @param {Object} config The config object
21256  */
21257
21258 Roo.bootstrap.DateField = function(config){
21259     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
21260      this.addEvents({
21261             /**
21262              * @event show
21263              * Fires when this field show.
21264              * @param {Roo.bootstrap.DateField} this
21265              * @param {Mixed} date The date value
21266              */
21267             show : true,
21268             /**
21269              * @event show
21270              * Fires when this field hide.
21271              * @param {Roo.bootstrap.DateField} this
21272              * @param {Mixed} date The date value
21273              */
21274             hide : true,
21275             /**
21276              * @event select
21277              * Fires when select a date.
21278              * @param {Roo.bootstrap.DateField} this
21279              * @param {Mixed} date The date value
21280              */
21281             select : true,
21282             /**
21283              * @event beforeselect
21284              * Fires when before select a date.
21285              * @param {Roo.bootstrap.DateField} this
21286              * @param {Mixed} date The date value
21287              */
21288             beforeselect : true
21289         });
21290 };
21291
21292 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
21293     
21294     /**
21295      * @cfg {String} format
21296      * The default date format string which can be overriden for localization support.  The format must be
21297      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
21298      */
21299     format : "m/d/y",
21300     /**
21301      * @cfg {String} altFormats
21302      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21303      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21304      */
21305     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21306     
21307     weekStart : 0,
21308     
21309     viewMode : '',
21310     
21311     minViewMode : '',
21312     
21313     todayHighlight : false,
21314     
21315     todayBtn: false,
21316     
21317     language: 'en',
21318     
21319     keyboardNavigation: true,
21320     
21321     calendarWeeks: false,
21322     
21323     startDate: -Infinity,
21324     
21325     endDate: Infinity,
21326     
21327     daysOfWeekDisabled: [],
21328     
21329     _events: [],
21330     
21331     singleMode : false,
21332     
21333     UTCDate: function()
21334     {
21335         return new Date(Date.UTC.apply(Date, arguments));
21336     },
21337     
21338     UTCToday: function()
21339     {
21340         var today = new Date();
21341         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21342     },
21343     
21344     getDate: function() {
21345             var d = this.getUTCDate();
21346             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21347     },
21348     
21349     getUTCDate: function() {
21350             return this.date;
21351     },
21352     
21353     setDate: function(d) {
21354             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21355     },
21356     
21357     setUTCDate: function(d) {
21358             this.date = d;
21359             this.setValue(this.formatDate(this.date));
21360     },
21361         
21362     onRender: function(ct, position)
21363     {
21364         
21365         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21366         
21367         this.language = this.language || 'en';
21368         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21369         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21370         
21371         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21372         this.format = this.format || 'm/d/y';
21373         this.isInline = false;
21374         this.isInput = true;
21375         this.component = this.el.select('.add-on', true).first() || false;
21376         this.component = (this.component && this.component.length === 0) ? false : this.component;
21377         this.hasInput = this.component && this.inputEl().length;
21378         
21379         if (typeof(this.minViewMode === 'string')) {
21380             switch (this.minViewMode) {
21381                 case 'months':
21382                     this.minViewMode = 1;
21383                     break;
21384                 case 'years':
21385                     this.minViewMode = 2;
21386                     break;
21387                 default:
21388                     this.minViewMode = 0;
21389                     break;
21390             }
21391         }
21392         
21393         if (typeof(this.viewMode === 'string')) {
21394             switch (this.viewMode) {
21395                 case 'months':
21396                     this.viewMode = 1;
21397                     break;
21398                 case 'years':
21399                     this.viewMode = 2;
21400                     break;
21401                 default:
21402                     this.viewMode = 0;
21403                     break;
21404             }
21405         }
21406                 
21407         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21408         
21409 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21410         
21411         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21412         
21413         this.picker().on('mousedown', this.onMousedown, this);
21414         this.picker().on('click', this.onClick, this);
21415         
21416         this.picker().addClass('datepicker-dropdown');
21417         
21418         this.startViewMode = this.viewMode;
21419         
21420         if(this.singleMode){
21421             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21422                 v.setVisibilityMode(Roo.Element.DISPLAY);
21423                 v.hide();
21424             });
21425             
21426             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21427                 v.setStyle('width', '189px');
21428             });
21429         }
21430         
21431         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21432             if(!this.calendarWeeks){
21433                 v.remove();
21434                 return;
21435             }
21436             
21437             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21438             v.attr('colspan', function(i, val){
21439                 return parseInt(val) + 1;
21440             });
21441         });
21442                         
21443         
21444         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21445         
21446         this.setStartDate(this.startDate);
21447         this.setEndDate(this.endDate);
21448         
21449         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21450         
21451         this.fillDow();
21452         this.fillMonths();
21453         this.update();
21454         this.showMode();
21455         
21456         if(this.isInline) {
21457             this.showPopup();
21458         }
21459     },
21460     
21461     picker : function()
21462     {
21463         return this.pickerEl;
21464 //        return this.el.select('.datepicker', true).first();
21465     },
21466     
21467     fillDow: function()
21468     {
21469         var dowCnt = this.weekStart;
21470         
21471         var dow = {
21472             tag: 'tr',
21473             cn: [
21474                 
21475             ]
21476         };
21477         
21478         if(this.calendarWeeks){
21479             dow.cn.push({
21480                 tag: 'th',
21481                 cls: 'cw',
21482                 html: '&nbsp;'
21483             })
21484         }
21485         
21486         while (dowCnt < this.weekStart + 7) {
21487             dow.cn.push({
21488                 tag: 'th',
21489                 cls: 'dow',
21490                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21491             });
21492         }
21493         
21494         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21495     },
21496     
21497     fillMonths: function()
21498     {    
21499         var i = 0;
21500         var months = this.picker().select('>.datepicker-months td', true).first();
21501         
21502         months.dom.innerHTML = '';
21503         
21504         while (i < 12) {
21505             var month = {
21506                 tag: 'span',
21507                 cls: 'month',
21508                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21509             };
21510             
21511             months.createChild(month);
21512         }
21513         
21514     },
21515     
21516     update: function()
21517     {
21518         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;
21519         
21520         if (this.date < this.startDate) {
21521             this.viewDate = new Date(this.startDate);
21522         } else if (this.date > this.endDate) {
21523             this.viewDate = new Date(this.endDate);
21524         } else {
21525             this.viewDate = new Date(this.date);
21526         }
21527         
21528         this.fill();
21529     },
21530     
21531     fill: function() 
21532     {
21533         var d = new Date(this.viewDate),
21534                 year = d.getUTCFullYear(),
21535                 month = d.getUTCMonth(),
21536                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21537                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21538                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21539                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21540                 currentDate = this.date && this.date.valueOf(),
21541                 today = this.UTCToday();
21542         
21543         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21544         
21545 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21546         
21547 //        this.picker.select('>tfoot th.today').
21548 //                                              .text(dates[this.language].today)
21549 //                                              .toggle(this.todayBtn !== false);
21550     
21551         this.updateNavArrows();
21552         this.fillMonths();
21553                                                 
21554         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21555         
21556         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21557          
21558         prevMonth.setUTCDate(day);
21559         
21560         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21561         
21562         var nextMonth = new Date(prevMonth);
21563         
21564         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21565         
21566         nextMonth = nextMonth.valueOf();
21567         
21568         var fillMonths = false;
21569         
21570         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21571         
21572         while(prevMonth.valueOf() <= nextMonth) {
21573             var clsName = '';
21574             
21575             if (prevMonth.getUTCDay() === this.weekStart) {
21576                 if(fillMonths){
21577                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21578                 }
21579                     
21580                 fillMonths = {
21581                     tag: 'tr',
21582                     cn: []
21583                 };
21584                 
21585                 if(this.calendarWeeks){
21586                     // ISO 8601: First week contains first thursday.
21587                     // ISO also states week starts on Monday, but we can be more abstract here.
21588                     var
21589                     // Start of current week: based on weekstart/current date
21590                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21591                     // Thursday of this week
21592                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21593                     // First Thursday of year, year from thursday
21594                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21595                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21596                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21597                     
21598                     fillMonths.cn.push({
21599                         tag: 'td',
21600                         cls: 'cw',
21601                         html: calWeek
21602                     });
21603                 }
21604             }
21605             
21606             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21607                 clsName += ' old';
21608             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21609                 clsName += ' new';
21610             }
21611             if (this.todayHighlight &&
21612                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21613                 prevMonth.getUTCMonth() == today.getMonth() &&
21614                 prevMonth.getUTCDate() == today.getDate()) {
21615                 clsName += ' today';
21616             }
21617             
21618             if (currentDate && prevMonth.valueOf() === currentDate) {
21619                 clsName += ' active';
21620             }
21621             
21622             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21623                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21624                     clsName += ' disabled';
21625             }
21626             
21627             fillMonths.cn.push({
21628                 tag: 'td',
21629                 cls: 'day ' + clsName,
21630                 html: prevMonth.getDate()
21631             });
21632             
21633             prevMonth.setDate(prevMonth.getDate()+1);
21634         }
21635           
21636         var currentYear = this.date && this.date.getUTCFullYear();
21637         var currentMonth = this.date && this.date.getUTCMonth();
21638         
21639         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21640         
21641         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21642             v.removeClass('active');
21643             
21644             if(currentYear === year && k === currentMonth){
21645                 v.addClass('active');
21646             }
21647             
21648             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21649                 v.addClass('disabled');
21650             }
21651             
21652         });
21653         
21654         
21655         year = parseInt(year/10, 10) * 10;
21656         
21657         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21658         
21659         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21660         
21661         year -= 1;
21662         for (var i = -1; i < 11; i++) {
21663             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21664                 tag: 'span',
21665                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21666                 html: year
21667             });
21668             
21669             year += 1;
21670         }
21671     },
21672     
21673     showMode: function(dir) 
21674     {
21675         if (dir) {
21676             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21677         }
21678         
21679         Roo.each(this.picker().select('>div',true).elements, function(v){
21680             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21681             v.hide();
21682         });
21683         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21684     },
21685     
21686     place: function()
21687     {
21688         if(this.isInline) {
21689             return;
21690         }
21691         
21692         this.picker().removeClass(['bottom', 'top']);
21693         
21694         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21695             /*
21696              * place to the top of element!
21697              *
21698              */
21699             
21700             this.picker().addClass('top');
21701             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21702             
21703             return;
21704         }
21705         
21706         this.picker().addClass('bottom');
21707         
21708         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21709     },
21710     
21711     parseDate : function(value)
21712     {
21713         if(!value || value instanceof Date){
21714             return value;
21715         }
21716         var v = Date.parseDate(value, this.format);
21717         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21718             v = Date.parseDate(value, 'Y-m-d');
21719         }
21720         if(!v && this.altFormats){
21721             if(!this.altFormatsArray){
21722                 this.altFormatsArray = this.altFormats.split("|");
21723             }
21724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21725                 v = Date.parseDate(value, this.altFormatsArray[i]);
21726             }
21727         }
21728         return v;
21729     },
21730     
21731     formatDate : function(date, fmt)
21732     {   
21733         return (!date || !(date instanceof Date)) ?
21734         date : date.dateFormat(fmt || this.format);
21735     },
21736     
21737     onFocus : function()
21738     {
21739         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21740         this.showPopup();
21741     },
21742     
21743     onBlur : function()
21744     {
21745         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21746         
21747         var d = this.inputEl().getValue();
21748         
21749         this.setValue(d);
21750                 
21751         this.hidePopup();
21752     },
21753     
21754     showPopup : function()
21755     {
21756         this.picker().show();
21757         this.update();
21758         this.place();
21759         
21760         this.fireEvent('showpopup', this, this.date);
21761     },
21762     
21763     hidePopup : function()
21764     {
21765         if(this.isInline) {
21766             return;
21767         }
21768         this.picker().hide();
21769         this.viewMode = this.startViewMode;
21770         this.showMode();
21771         
21772         this.fireEvent('hidepopup', this, this.date);
21773         
21774     },
21775     
21776     onMousedown: function(e)
21777     {
21778         e.stopPropagation();
21779         e.preventDefault();
21780     },
21781     
21782     keyup: function(e)
21783     {
21784         Roo.bootstrap.DateField.superclass.keyup.call(this);
21785         this.update();
21786     },
21787
21788     setValue: function(v)
21789     {
21790         if(this.fireEvent('beforeselect', this, v) !== false){
21791             var d = new Date(this.parseDate(v) ).clearTime();
21792         
21793             if(isNaN(d.getTime())){
21794                 this.date = this.viewDate = '';
21795                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21796                 return;
21797             }
21798
21799             v = this.formatDate(d);
21800
21801             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21802
21803             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21804
21805             this.update();
21806
21807             this.fireEvent('select', this, this.date);
21808         }
21809     },
21810     
21811     getValue: function()
21812     {
21813         return this.formatDate(this.date);
21814     },
21815     
21816     fireKey: function(e)
21817     {
21818         if (!this.picker().isVisible()){
21819             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21820                 this.showPopup();
21821             }
21822             return;
21823         }
21824         
21825         var dateChanged = false,
21826         dir, day, month,
21827         newDate, newViewDate;
21828         
21829         switch(e.keyCode){
21830             case 27: // escape
21831                 this.hidePopup();
21832                 e.preventDefault();
21833                 break;
21834             case 37: // left
21835             case 39: // right
21836                 if (!this.keyboardNavigation) {
21837                     break;
21838                 }
21839                 dir = e.keyCode == 37 ? -1 : 1;
21840                 
21841                 if (e.ctrlKey){
21842                     newDate = this.moveYear(this.date, dir);
21843                     newViewDate = this.moveYear(this.viewDate, dir);
21844                 } else if (e.shiftKey){
21845                     newDate = this.moveMonth(this.date, dir);
21846                     newViewDate = this.moveMonth(this.viewDate, dir);
21847                 } else {
21848                     newDate = new Date(this.date);
21849                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21850                     newViewDate = new Date(this.viewDate);
21851                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21852                 }
21853                 if (this.dateWithinRange(newDate)){
21854                     this.date = newDate;
21855                     this.viewDate = newViewDate;
21856                     this.setValue(this.formatDate(this.date));
21857 //                    this.update();
21858                     e.preventDefault();
21859                     dateChanged = true;
21860                 }
21861                 break;
21862             case 38: // up
21863             case 40: // down
21864                 if (!this.keyboardNavigation) {
21865                     break;
21866                 }
21867                 dir = e.keyCode == 38 ? -1 : 1;
21868                 if (e.ctrlKey){
21869                     newDate = this.moveYear(this.date, dir);
21870                     newViewDate = this.moveYear(this.viewDate, dir);
21871                 } else if (e.shiftKey){
21872                     newDate = this.moveMonth(this.date, dir);
21873                     newViewDate = this.moveMonth(this.viewDate, dir);
21874                 } else {
21875                     newDate = new Date(this.date);
21876                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21877                     newViewDate = new Date(this.viewDate);
21878                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21879                 }
21880                 if (this.dateWithinRange(newDate)){
21881                     this.date = newDate;
21882                     this.viewDate = newViewDate;
21883                     this.setValue(this.formatDate(this.date));
21884 //                    this.update();
21885                     e.preventDefault();
21886                     dateChanged = true;
21887                 }
21888                 break;
21889             case 13: // enter
21890                 this.setValue(this.formatDate(this.date));
21891                 this.hidePopup();
21892                 e.preventDefault();
21893                 break;
21894             case 9: // tab
21895                 this.setValue(this.formatDate(this.date));
21896                 this.hidePopup();
21897                 break;
21898             case 16: // shift
21899             case 17: // ctrl
21900             case 18: // alt
21901                 break;
21902             default :
21903                 this.hidePopup();
21904                 
21905         }
21906     },
21907     
21908     
21909     onClick: function(e) 
21910     {
21911         e.stopPropagation();
21912         e.preventDefault();
21913         
21914         var target = e.getTarget();
21915         
21916         if(target.nodeName.toLowerCase() === 'i'){
21917             target = Roo.get(target).dom.parentNode;
21918         }
21919         
21920         var nodeName = target.nodeName;
21921         var className = target.className;
21922         var html = target.innerHTML;
21923         //Roo.log(nodeName);
21924         
21925         switch(nodeName.toLowerCase()) {
21926             case 'th':
21927                 switch(className) {
21928                     case 'switch':
21929                         this.showMode(1);
21930                         break;
21931                     case 'prev':
21932                     case 'next':
21933                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21934                         switch(this.viewMode){
21935                                 case 0:
21936                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21937                                         break;
21938                                 case 1:
21939                                 case 2:
21940                                         this.viewDate = this.moveYear(this.viewDate, dir);
21941                                         break;
21942                         }
21943                         this.fill();
21944                         break;
21945                     case 'today':
21946                         var date = new Date();
21947                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21948 //                        this.fill()
21949                         this.setValue(this.formatDate(this.date));
21950                         
21951                         this.hidePopup();
21952                         break;
21953                 }
21954                 break;
21955             case 'span':
21956                 if (className.indexOf('disabled') < 0) {
21957                     this.viewDate.setUTCDate(1);
21958                     if (className.indexOf('month') > -1) {
21959                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21960                     } else {
21961                         var year = parseInt(html, 10) || 0;
21962                         this.viewDate.setUTCFullYear(year);
21963                         
21964                     }
21965                     
21966                     if(this.singleMode){
21967                         this.setValue(this.formatDate(this.viewDate));
21968                         this.hidePopup();
21969                         return;
21970                     }
21971                     
21972                     this.showMode(-1);
21973                     this.fill();
21974                 }
21975                 break;
21976                 
21977             case 'td':
21978                 //Roo.log(className);
21979                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21980                     var day = parseInt(html, 10) || 1;
21981                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21982                         month = (this.viewDate || new Date()).getUTCMonth();
21983
21984                     if (className.indexOf('old') > -1) {
21985                         if(month === 0 ){
21986                             month = 11;
21987                             year -= 1;
21988                         }else{
21989                             month -= 1;
21990                         }
21991                     } else if (className.indexOf('new') > -1) {
21992                         if (month == 11) {
21993                             month = 0;
21994                             year += 1;
21995                         } else {
21996                             month += 1;
21997                         }
21998                     }
21999                     //Roo.log([year,month,day]);
22000                     this.date = this.UTCDate(year, month, day,0,0,0,0);
22001                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
22002 //                    this.fill();
22003                     //Roo.log(this.formatDate(this.date));
22004                     this.setValue(this.formatDate(this.date));
22005                     this.hidePopup();
22006                 }
22007                 break;
22008         }
22009     },
22010     
22011     setStartDate: function(startDate)
22012     {
22013         this.startDate = startDate || -Infinity;
22014         if (this.startDate !== -Infinity) {
22015             this.startDate = this.parseDate(this.startDate);
22016         }
22017         this.update();
22018         this.updateNavArrows();
22019     },
22020
22021     setEndDate: function(endDate)
22022     {
22023         this.endDate = endDate || Infinity;
22024         if (this.endDate !== Infinity) {
22025             this.endDate = this.parseDate(this.endDate);
22026         }
22027         this.update();
22028         this.updateNavArrows();
22029     },
22030     
22031     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
22032     {
22033         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
22034         if (typeof(this.daysOfWeekDisabled) !== 'object') {
22035             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
22036         }
22037         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
22038             return parseInt(d, 10);
22039         });
22040         this.update();
22041         this.updateNavArrows();
22042     },
22043     
22044     updateNavArrows: function() 
22045     {
22046         if(this.singleMode){
22047             return;
22048         }
22049         
22050         var d = new Date(this.viewDate),
22051         year = d.getUTCFullYear(),
22052         month = d.getUTCMonth();
22053         
22054         Roo.each(this.picker().select('.prev', true).elements, function(v){
22055             v.show();
22056             switch (this.viewMode) {
22057                 case 0:
22058
22059                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
22060                         v.hide();
22061                     }
22062                     break;
22063                 case 1:
22064                 case 2:
22065                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
22066                         v.hide();
22067                     }
22068                     break;
22069             }
22070         });
22071         
22072         Roo.each(this.picker().select('.next', true).elements, function(v){
22073             v.show();
22074             switch (this.viewMode) {
22075                 case 0:
22076
22077                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
22078                         v.hide();
22079                     }
22080                     break;
22081                 case 1:
22082                 case 2:
22083                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
22084                         v.hide();
22085                     }
22086                     break;
22087             }
22088         })
22089     },
22090     
22091     moveMonth: function(date, dir)
22092     {
22093         if (!dir) {
22094             return date;
22095         }
22096         var new_date = new Date(date.valueOf()),
22097         day = new_date.getUTCDate(),
22098         month = new_date.getUTCMonth(),
22099         mag = Math.abs(dir),
22100         new_month, test;
22101         dir = dir > 0 ? 1 : -1;
22102         if (mag == 1){
22103             test = dir == -1
22104             // If going back one month, make sure month is not current month
22105             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
22106             ? function(){
22107                 return new_date.getUTCMonth() == month;
22108             }
22109             // If going forward one month, make sure month is as expected
22110             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
22111             : function(){
22112                 return new_date.getUTCMonth() != new_month;
22113             };
22114             new_month = month + dir;
22115             new_date.setUTCMonth(new_month);
22116             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
22117             if (new_month < 0 || new_month > 11) {
22118                 new_month = (new_month + 12) % 12;
22119             }
22120         } else {
22121             // For magnitudes >1, move one month at a time...
22122             for (var i=0; i<mag; i++) {
22123                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
22124                 new_date = this.moveMonth(new_date, dir);
22125             }
22126             // ...then reset the day, keeping it in the new month
22127             new_month = new_date.getUTCMonth();
22128             new_date.setUTCDate(day);
22129             test = function(){
22130                 return new_month != new_date.getUTCMonth();
22131             };
22132         }
22133         // Common date-resetting loop -- if date is beyond end of month, make it
22134         // end of month
22135         while (test()){
22136             new_date.setUTCDate(--day);
22137             new_date.setUTCMonth(new_month);
22138         }
22139         return new_date;
22140     },
22141
22142     moveYear: function(date, dir)
22143     {
22144         return this.moveMonth(date, dir*12);
22145     },
22146
22147     dateWithinRange: function(date)
22148     {
22149         return date >= this.startDate && date <= this.endDate;
22150     },
22151
22152     
22153     remove: function() 
22154     {
22155         this.picker().remove();
22156     },
22157     
22158     validateValue : function(value)
22159     {
22160         if(this.getVisibilityEl().hasClass('hidden')){
22161             return true;
22162         }
22163         
22164         if(value.length < 1)  {
22165             if(this.allowBlank){
22166                 return true;
22167             }
22168             return false;
22169         }
22170         
22171         if(value.length < this.minLength){
22172             return false;
22173         }
22174         if(value.length > this.maxLength){
22175             return false;
22176         }
22177         if(this.vtype){
22178             var vt = Roo.form.VTypes;
22179             if(!vt[this.vtype](value, this)){
22180                 return false;
22181             }
22182         }
22183         if(typeof this.validator == "function"){
22184             var msg = this.validator(value);
22185             if(msg !== true){
22186                 return false;
22187             }
22188         }
22189         
22190         if(this.regex && !this.regex.test(value)){
22191             return false;
22192         }
22193         
22194         if(typeof(this.parseDate(value)) == 'undefined'){
22195             return false;
22196         }
22197         
22198         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
22199             return false;
22200         }      
22201         
22202         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
22203             return false;
22204         } 
22205         
22206         
22207         return true;
22208     },
22209     
22210     reset : function()
22211     {
22212         this.date = this.viewDate = '';
22213         
22214         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
22215     }
22216    
22217 });
22218
22219 Roo.apply(Roo.bootstrap.DateField,  {
22220     
22221     head : {
22222         tag: 'thead',
22223         cn: [
22224         {
22225             tag: 'tr',
22226             cn: [
22227             {
22228                 tag: 'th',
22229                 cls: 'prev',
22230                 html: '<i class="fa fa-arrow-left"/>'
22231             },
22232             {
22233                 tag: 'th',
22234                 cls: 'switch',
22235                 colspan: '5'
22236             },
22237             {
22238                 tag: 'th',
22239                 cls: 'next',
22240                 html: '<i class="fa fa-arrow-right"/>'
22241             }
22242
22243             ]
22244         }
22245         ]
22246     },
22247     
22248     content : {
22249         tag: 'tbody',
22250         cn: [
22251         {
22252             tag: 'tr',
22253             cn: [
22254             {
22255                 tag: 'td',
22256                 colspan: '7'
22257             }
22258             ]
22259         }
22260         ]
22261     },
22262     
22263     footer : {
22264         tag: 'tfoot',
22265         cn: [
22266         {
22267             tag: 'tr',
22268             cn: [
22269             {
22270                 tag: 'th',
22271                 colspan: '7',
22272                 cls: 'today'
22273             }
22274                     
22275             ]
22276         }
22277         ]
22278     },
22279     
22280     dates:{
22281         en: {
22282             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
22283             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
22284             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
22285             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22286             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
22287             today: "Today"
22288         }
22289     },
22290     
22291     modes: [
22292     {
22293         clsName: 'days',
22294         navFnc: 'Month',
22295         navStep: 1
22296     },
22297     {
22298         clsName: 'months',
22299         navFnc: 'FullYear',
22300         navStep: 1
22301     },
22302     {
22303         clsName: 'years',
22304         navFnc: 'FullYear',
22305         navStep: 10
22306     }]
22307 });
22308
22309 Roo.apply(Roo.bootstrap.DateField,  {
22310   
22311     template : {
22312         tag: 'div',
22313         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22314         cn: [
22315         {
22316             tag: 'div',
22317             cls: 'datepicker-days',
22318             cn: [
22319             {
22320                 tag: 'table',
22321                 cls: 'table-condensed',
22322                 cn:[
22323                 Roo.bootstrap.DateField.head,
22324                 {
22325                     tag: 'tbody'
22326                 },
22327                 Roo.bootstrap.DateField.footer
22328                 ]
22329             }
22330             ]
22331         },
22332         {
22333             tag: 'div',
22334             cls: 'datepicker-months',
22335             cn: [
22336             {
22337                 tag: 'table',
22338                 cls: 'table-condensed',
22339                 cn:[
22340                 Roo.bootstrap.DateField.head,
22341                 Roo.bootstrap.DateField.content,
22342                 Roo.bootstrap.DateField.footer
22343                 ]
22344             }
22345             ]
22346         },
22347         {
22348             tag: 'div',
22349             cls: 'datepicker-years',
22350             cn: [
22351             {
22352                 tag: 'table',
22353                 cls: 'table-condensed',
22354                 cn:[
22355                 Roo.bootstrap.DateField.head,
22356                 Roo.bootstrap.DateField.content,
22357                 Roo.bootstrap.DateField.footer
22358                 ]
22359             }
22360             ]
22361         }
22362         ]
22363     }
22364 });
22365
22366  
22367
22368  /*
22369  * - LGPL
22370  *
22371  * TimeField
22372  * 
22373  */
22374
22375 /**
22376  * @class Roo.bootstrap.TimeField
22377  * @extends Roo.bootstrap.Input
22378  * Bootstrap DateField class
22379  * 
22380  * 
22381  * @constructor
22382  * Create a new TimeField
22383  * @param {Object} config The config object
22384  */
22385
22386 Roo.bootstrap.TimeField = function(config){
22387     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22388     this.addEvents({
22389             /**
22390              * @event show
22391              * Fires when this field show.
22392              * @param {Roo.bootstrap.DateField} thisthis
22393              * @param {Mixed} date The date value
22394              */
22395             show : true,
22396             /**
22397              * @event show
22398              * Fires when this field hide.
22399              * @param {Roo.bootstrap.DateField} this
22400              * @param {Mixed} date The date value
22401              */
22402             hide : true,
22403             /**
22404              * @event select
22405              * Fires when select a date.
22406              * @param {Roo.bootstrap.DateField} this
22407              * @param {Mixed} date The date value
22408              */
22409             select : true
22410         });
22411 };
22412
22413 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22414     
22415     /**
22416      * @cfg {String} format
22417      * The default time format string which can be overriden for localization support.  The format must be
22418      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22419      */
22420     format : "H:i",
22421
22422     getAutoCreate : function()
22423     {
22424         this.after = '<i class="fa far fa-clock"></i>';
22425         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22426         
22427          
22428     },
22429     onRender: function(ct, position)
22430     {
22431         
22432         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22433                 
22434         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22435         
22436         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22437         
22438         this.pop = this.picker().select('>.datepicker-time',true).first();
22439         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22440         
22441         this.picker().on('mousedown', this.onMousedown, this);
22442         this.picker().on('click', this.onClick, this);
22443         
22444         this.picker().addClass('datepicker-dropdown');
22445     
22446         this.fillTime();
22447         this.update();
22448             
22449         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22450         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22451         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22452         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22453         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22454         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22455
22456     },
22457     
22458     fireKey: function(e){
22459         if (!this.picker().isVisible()){
22460             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22461                 this.show();
22462             }
22463             return;
22464         }
22465
22466         e.preventDefault();
22467         
22468         switch(e.keyCode){
22469             case 27: // escape
22470                 this.hide();
22471                 break;
22472             case 37: // left
22473             case 39: // right
22474                 this.onTogglePeriod();
22475                 break;
22476             case 38: // up
22477                 this.onIncrementMinutes();
22478                 break;
22479             case 40: // down
22480                 this.onDecrementMinutes();
22481                 break;
22482             case 13: // enter
22483             case 9: // tab
22484                 this.setTime();
22485                 break;
22486         }
22487     },
22488     
22489     onClick: function(e) {
22490         e.stopPropagation();
22491         e.preventDefault();
22492     },
22493     
22494     picker : function()
22495     {
22496         return this.pickerEl;
22497     },
22498     
22499     fillTime: function()
22500     {    
22501         var time = this.pop.select('tbody', true).first();
22502         
22503         time.dom.innerHTML = '';
22504         
22505         time.createChild({
22506             tag: 'tr',
22507             cn: [
22508                 {
22509                     tag: 'td',
22510                     cn: [
22511                         {
22512                             tag: 'a',
22513                             href: '#',
22514                             cls: 'btn',
22515                             cn: [
22516                                 {
22517                                     tag: 'i',
22518                                     cls: 'hours-up fa fas fa-chevron-up'
22519                                 }
22520                             ]
22521                         } 
22522                     ]
22523                 },
22524                 {
22525                     tag: 'td',
22526                     cls: 'separator'
22527                 },
22528                 {
22529                     tag: 'td',
22530                     cn: [
22531                         {
22532                             tag: 'a',
22533                             href: '#',
22534                             cls: 'btn',
22535                             cn: [
22536                                 {
22537                                     tag: 'i',
22538                                     cls: 'minutes-up fa fas fa-chevron-up'
22539                                 }
22540                             ]
22541                         }
22542                     ]
22543                 },
22544                 {
22545                     tag: 'td',
22546                     cls: 'separator'
22547                 }
22548             ]
22549         });
22550         
22551         time.createChild({
22552             tag: 'tr',
22553             cn: [
22554                 {
22555                     tag: 'td',
22556                     cn: [
22557                         {
22558                             tag: 'span',
22559                             cls: 'timepicker-hour',
22560                             html: '00'
22561                         }  
22562                     ]
22563                 },
22564                 {
22565                     tag: 'td',
22566                     cls: 'separator',
22567                     html: ':'
22568                 },
22569                 {
22570                     tag: 'td',
22571                     cn: [
22572                         {
22573                             tag: 'span',
22574                             cls: 'timepicker-minute',
22575                             html: '00'
22576                         }  
22577                     ]
22578                 },
22579                 {
22580                     tag: 'td',
22581                     cls: 'separator'
22582                 },
22583                 {
22584                     tag: 'td',
22585                     cn: [
22586                         {
22587                             tag: 'button',
22588                             type: 'button',
22589                             cls: 'btn btn-primary period',
22590                             html: 'AM'
22591                             
22592                         }
22593                     ]
22594                 }
22595             ]
22596         });
22597         
22598         time.createChild({
22599             tag: 'tr',
22600             cn: [
22601                 {
22602                     tag: 'td',
22603                     cn: [
22604                         {
22605                             tag: 'a',
22606                             href: '#',
22607                             cls: 'btn',
22608                             cn: [
22609                                 {
22610                                     tag: 'span',
22611                                     cls: 'hours-down fa fas fa-chevron-down'
22612                                 }
22613                             ]
22614                         }
22615                     ]
22616                 },
22617                 {
22618                     tag: 'td',
22619                     cls: 'separator'
22620                 },
22621                 {
22622                     tag: 'td',
22623                     cn: [
22624                         {
22625                             tag: 'a',
22626                             href: '#',
22627                             cls: 'btn',
22628                             cn: [
22629                                 {
22630                                     tag: 'span',
22631                                     cls: 'minutes-down fa fas fa-chevron-down'
22632                                 }
22633                             ]
22634                         }
22635                     ]
22636                 },
22637                 {
22638                     tag: 'td',
22639                     cls: 'separator'
22640                 }
22641             ]
22642         });
22643         
22644     },
22645     
22646     update: function()
22647     {
22648         
22649         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22650         
22651         this.fill();
22652     },
22653     
22654     fill: function() 
22655     {
22656         var hours = this.time.getHours();
22657         var minutes = this.time.getMinutes();
22658         var period = 'AM';
22659         
22660         if(hours > 11){
22661             period = 'PM';
22662         }
22663         
22664         if(hours == 0){
22665             hours = 12;
22666         }
22667         
22668         
22669         if(hours > 12){
22670             hours = hours - 12;
22671         }
22672         
22673         if(hours < 10){
22674             hours = '0' + hours;
22675         }
22676         
22677         if(minutes < 10){
22678             minutes = '0' + minutes;
22679         }
22680         
22681         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22682         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22683         this.pop.select('button', true).first().dom.innerHTML = period;
22684         
22685     },
22686     
22687     place: function()
22688     {   
22689         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22690         
22691         var cls = ['bottom'];
22692         
22693         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22694             cls.pop();
22695             cls.push('top');
22696         }
22697         
22698         cls.push('right');
22699         
22700         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22701             cls.pop();
22702             cls.push('left');
22703         }
22704         //this.picker().setXY(20000,20000);
22705         this.picker().addClass(cls.join('-'));
22706         
22707         var _this = this;
22708         
22709         Roo.each(cls, function(c){
22710             if(c == 'bottom'){
22711                 (function() {
22712                  //  
22713                 }).defer(200);
22714                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22715                 //_this.picker().setTop(_this.inputEl().getHeight());
22716                 return;
22717             }
22718             if(c == 'top'){
22719                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22720                 
22721                 //_this.picker().setTop(0 - _this.picker().getHeight());
22722                 return;
22723             }
22724             /*
22725             if(c == 'left'){
22726                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22727                 return;
22728             }
22729             if(c == 'right'){
22730                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22731                 return;
22732             }
22733             */
22734         });
22735         
22736     },
22737   
22738     onFocus : function()
22739     {
22740         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22741         this.show();
22742     },
22743     
22744     onBlur : function()
22745     {
22746         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22747         this.hide();
22748     },
22749     
22750     show : function()
22751     {
22752         this.picker().show();
22753         this.pop.show();
22754         this.update();
22755         this.place();
22756         
22757         this.fireEvent('show', this, this.date);
22758     },
22759     
22760     hide : function()
22761     {
22762         this.picker().hide();
22763         this.pop.hide();
22764         
22765         this.fireEvent('hide', this, this.date);
22766     },
22767     
22768     setTime : function()
22769     {
22770         this.hide();
22771         this.setValue(this.time.format(this.format));
22772         
22773         this.fireEvent('select', this, this.date);
22774         
22775         
22776     },
22777     
22778     onMousedown: function(e){
22779         e.stopPropagation();
22780         e.preventDefault();
22781     },
22782     
22783     onIncrementHours: function()
22784     {
22785         Roo.log('onIncrementHours');
22786         this.time = this.time.add(Date.HOUR, 1);
22787         this.update();
22788         
22789     },
22790     
22791     onDecrementHours: function()
22792     {
22793         Roo.log('onDecrementHours');
22794         this.time = this.time.add(Date.HOUR, -1);
22795         this.update();
22796     },
22797     
22798     onIncrementMinutes: function()
22799     {
22800         Roo.log('onIncrementMinutes');
22801         this.time = this.time.add(Date.MINUTE, 1);
22802         this.update();
22803     },
22804     
22805     onDecrementMinutes: function()
22806     {
22807         Roo.log('onDecrementMinutes');
22808         this.time = this.time.add(Date.MINUTE, -1);
22809         this.update();
22810     },
22811     
22812     onTogglePeriod: function()
22813     {
22814         Roo.log('onTogglePeriod');
22815         this.time = this.time.add(Date.HOUR, 12);
22816         this.update();
22817     }
22818     
22819    
22820 });
22821  
22822
22823 Roo.apply(Roo.bootstrap.TimeField,  {
22824   
22825     template : {
22826         tag: 'div',
22827         cls: 'datepicker dropdown-menu',
22828         cn: [
22829             {
22830                 tag: 'div',
22831                 cls: 'datepicker-time',
22832                 cn: [
22833                 {
22834                     tag: 'table',
22835                     cls: 'table-condensed',
22836                     cn:[
22837                         {
22838                             tag: 'tbody',
22839                             cn: [
22840                                 {
22841                                     tag: 'tr',
22842                                     cn: [
22843                                     {
22844                                         tag: 'td',
22845                                         colspan: '7'
22846                                     }
22847                                     ]
22848                                 }
22849                             ]
22850                         },
22851                         {
22852                             tag: 'tfoot',
22853                             cn: [
22854                                 {
22855                                     tag: 'tr',
22856                                     cn: [
22857                                     {
22858                                         tag: 'th',
22859                                         colspan: '7',
22860                                         cls: '',
22861                                         cn: [
22862                                             {
22863                                                 tag: 'button',
22864                                                 cls: 'btn btn-info ok',
22865                                                 html: 'OK'
22866                                             }
22867                                         ]
22868                                     }
22869                     
22870                                     ]
22871                                 }
22872                             ]
22873                         }
22874                     ]
22875                 }
22876                 ]
22877             }
22878         ]
22879     }
22880 });
22881
22882  
22883
22884  /*
22885  * - LGPL
22886  *
22887  * MonthField
22888  * 
22889  */
22890
22891 /**
22892  * @class Roo.bootstrap.MonthField
22893  * @extends Roo.bootstrap.Input
22894  * Bootstrap MonthField class
22895  * 
22896  * @cfg {String} language default en
22897  * 
22898  * @constructor
22899  * Create a new MonthField
22900  * @param {Object} config The config object
22901  */
22902
22903 Roo.bootstrap.MonthField = function(config){
22904     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22905     
22906     this.addEvents({
22907         /**
22908          * @event show
22909          * Fires when this field show.
22910          * @param {Roo.bootstrap.MonthField} this
22911          * @param {Mixed} date The date value
22912          */
22913         show : true,
22914         /**
22915          * @event show
22916          * Fires when this field hide.
22917          * @param {Roo.bootstrap.MonthField} this
22918          * @param {Mixed} date The date value
22919          */
22920         hide : true,
22921         /**
22922          * @event select
22923          * Fires when select a date.
22924          * @param {Roo.bootstrap.MonthField} this
22925          * @param {String} oldvalue The old value
22926          * @param {String} newvalue The new value
22927          */
22928         select : true
22929     });
22930 };
22931
22932 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22933     
22934     onRender: function(ct, position)
22935     {
22936         
22937         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22938         
22939         this.language = this.language || 'en';
22940         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22941         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22942         
22943         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22944         this.isInline = false;
22945         this.isInput = true;
22946         this.component = this.el.select('.add-on', true).first() || false;
22947         this.component = (this.component && this.component.length === 0) ? false : this.component;
22948         this.hasInput = this.component && this.inputEL().length;
22949         
22950         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22951         
22952         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22953         
22954         this.picker().on('mousedown', this.onMousedown, this);
22955         this.picker().on('click', this.onClick, this);
22956         
22957         this.picker().addClass('datepicker-dropdown');
22958         
22959         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22960             v.setStyle('width', '189px');
22961         });
22962         
22963         this.fillMonths();
22964         
22965         this.update();
22966         
22967         if(this.isInline) {
22968             this.show();
22969         }
22970         
22971     },
22972     
22973     setValue: function(v, suppressEvent)
22974     {   
22975         var o = this.getValue();
22976         
22977         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22978         
22979         this.update();
22980
22981         if(suppressEvent !== true){
22982             this.fireEvent('select', this, o, v);
22983         }
22984         
22985     },
22986     
22987     getValue: function()
22988     {
22989         return this.value;
22990     },
22991     
22992     onClick: function(e) 
22993     {
22994         e.stopPropagation();
22995         e.preventDefault();
22996         
22997         var target = e.getTarget();
22998         
22999         if(target.nodeName.toLowerCase() === 'i'){
23000             target = Roo.get(target).dom.parentNode;
23001         }
23002         
23003         var nodeName = target.nodeName;
23004         var className = target.className;
23005         var html = target.innerHTML;
23006         
23007         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
23008             return;
23009         }
23010         
23011         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
23012         
23013         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23014         
23015         this.hide();
23016                         
23017     },
23018     
23019     picker : function()
23020     {
23021         return this.pickerEl;
23022     },
23023     
23024     fillMonths: function()
23025     {    
23026         var i = 0;
23027         var months = this.picker().select('>.datepicker-months td', true).first();
23028         
23029         months.dom.innerHTML = '';
23030         
23031         while (i < 12) {
23032             var month = {
23033                 tag: 'span',
23034                 cls: 'month',
23035                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
23036             };
23037             
23038             months.createChild(month);
23039         }
23040         
23041     },
23042     
23043     update: function()
23044     {
23045         var _this = this;
23046         
23047         if(typeof(this.vIndex) == 'undefined' && this.value.length){
23048             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
23049         }
23050         
23051         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
23052             e.removeClass('active');
23053             
23054             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
23055                 e.addClass('active');
23056             }
23057         })
23058     },
23059     
23060     place: function()
23061     {
23062         if(this.isInline) {
23063             return;
23064         }
23065         
23066         this.picker().removeClass(['bottom', 'top']);
23067         
23068         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
23069             /*
23070              * place to the top of element!
23071              *
23072              */
23073             
23074             this.picker().addClass('top');
23075             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
23076             
23077             return;
23078         }
23079         
23080         this.picker().addClass('bottom');
23081         
23082         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
23083     },
23084     
23085     onFocus : function()
23086     {
23087         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
23088         this.show();
23089     },
23090     
23091     onBlur : function()
23092     {
23093         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
23094         
23095         var d = this.inputEl().getValue();
23096         
23097         this.setValue(d);
23098                 
23099         this.hide();
23100     },
23101     
23102     show : function()
23103     {
23104         this.picker().show();
23105         this.picker().select('>.datepicker-months', true).first().show();
23106         this.update();
23107         this.place();
23108         
23109         this.fireEvent('show', this, this.date);
23110     },
23111     
23112     hide : function()
23113     {
23114         if(this.isInline) {
23115             return;
23116         }
23117         this.picker().hide();
23118         this.fireEvent('hide', this, this.date);
23119         
23120     },
23121     
23122     onMousedown: function(e)
23123     {
23124         e.stopPropagation();
23125         e.preventDefault();
23126     },
23127     
23128     keyup: function(e)
23129     {
23130         Roo.bootstrap.MonthField.superclass.keyup.call(this);
23131         this.update();
23132     },
23133
23134     fireKey: function(e)
23135     {
23136         if (!this.picker().isVisible()){
23137             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
23138                 this.show();
23139             }
23140             return;
23141         }
23142         
23143         var dir;
23144         
23145         switch(e.keyCode){
23146             case 27: // escape
23147                 this.hide();
23148                 e.preventDefault();
23149                 break;
23150             case 37: // left
23151             case 39: // right
23152                 dir = e.keyCode == 37 ? -1 : 1;
23153                 
23154                 this.vIndex = this.vIndex + dir;
23155                 
23156                 if(this.vIndex < 0){
23157                     this.vIndex = 0;
23158                 }
23159                 
23160                 if(this.vIndex > 11){
23161                     this.vIndex = 11;
23162                 }
23163                 
23164                 if(isNaN(this.vIndex)){
23165                     this.vIndex = 0;
23166                 }
23167                 
23168                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23169                 
23170                 break;
23171             case 38: // up
23172             case 40: // down
23173                 
23174                 dir = e.keyCode == 38 ? -1 : 1;
23175                 
23176                 this.vIndex = this.vIndex + dir * 4;
23177                 
23178                 if(this.vIndex < 0){
23179                     this.vIndex = 0;
23180                 }
23181                 
23182                 if(this.vIndex > 11){
23183                     this.vIndex = 11;
23184                 }
23185                 
23186                 if(isNaN(this.vIndex)){
23187                     this.vIndex = 0;
23188                 }
23189                 
23190                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23191                 break;
23192                 
23193             case 13: // enter
23194                 
23195                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23196                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23197                 }
23198                 
23199                 this.hide();
23200                 e.preventDefault();
23201                 break;
23202             case 9: // tab
23203                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
23204                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
23205                 }
23206                 this.hide();
23207                 break;
23208             case 16: // shift
23209             case 17: // ctrl
23210             case 18: // alt
23211                 break;
23212             default :
23213                 this.hide();
23214                 
23215         }
23216     },
23217     
23218     remove: function() 
23219     {
23220         this.picker().remove();
23221     }
23222    
23223 });
23224
23225 Roo.apply(Roo.bootstrap.MonthField,  {
23226     
23227     content : {
23228         tag: 'tbody',
23229         cn: [
23230         {
23231             tag: 'tr',
23232             cn: [
23233             {
23234                 tag: 'td',
23235                 colspan: '7'
23236             }
23237             ]
23238         }
23239         ]
23240     },
23241     
23242     dates:{
23243         en: {
23244             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
23245             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
23246         }
23247     }
23248 });
23249
23250 Roo.apply(Roo.bootstrap.MonthField,  {
23251   
23252     template : {
23253         tag: 'div',
23254         cls: 'datepicker dropdown-menu roo-dynamic',
23255         cn: [
23256             {
23257                 tag: 'div',
23258                 cls: 'datepicker-months',
23259                 cn: [
23260                 {
23261                     tag: 'table',
23262                     cls: 'table-condensed',
23263                     cn:[
23264                         Roo.bootstrap.DateField.content
23265                     ]
23266                 }
23267                 ]
23268             }
23269         ]
23270     }
23271 });
23272
23273  
23274
23275  
23276  /*
23277  * - LGPL
23278  *
23279  * CheckBox
23280  * 
23281  */
23282
23283 /**
23284  * @class Roo.bootstrap.CheckBox
23285  * @extends Roo.bootstrap.Input
23286  * Bootstrap CheckBox class
23287  * 
23288  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
23289  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
23290  * @cfg {String} boxLabel The text that appears beside the checkbox
23291  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
23292  * @cfg {Boolean} checked initnal the element
23293  * @cfg {Boolean} inline inline the element (default false)
23294  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
23295  * @cfg {String} tooltip label tooltip
23296  * 
23297  * @constructor
23298  * Create a new CheckBox
23299  * @param {Object} config The config object
23300  */
23301
23302 Roo.bootstrap.CheckBox = function(config){
23303     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23304    
23305     this.addEvents({
23306         /**
23307         * @event check
23308         * Fires when the element is checked or unchecked.
23309         * @param {Roo.bootstrap.CheckBox} this This input
23310         * @param {Boolean} checked The new checked value
23311         */
23312        check : true,
23313        /**
23314         * @event click
23315         * Fires when the element is click.
23316         * @param {Roo.bootstrap.CheckBox} this This input
23317         */
23318        click : true
23319     });
23320     
23321 };
23322
23323 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23324   
23325     inputType: 'checkbox',
23326     inputValue: 1,
23327     valueOff: 0,
23328     boxLabel: false,
23329     checked: false,
23330     weight : false,
23331     inline: false,
23332     tooltip : '',
23333     
23334     // checkbox success does not make any sense really.. 
23335     invalidClass : "",
23336     validClass : "",
23337     
23338     
23339     getAutoCreate : function()
23340     {
23341         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23342         
23343         var id = Roo.id();
23344         
23345         var cfg = {};
23346         
23347         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23348         
23349         if(this.inline){
23350             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23351         }
23352         
23353         var input =  {
23354             tag: 'input',
23355             id : id,
23356             type : this.inputType,
23357             value : this.inputValue,
23358             cls : 'roo-' + this.inputType, //'form-box',
23359             placeholder : this.placeholder || ''
23360             
23361         };
23362         
23363         if(this.inputType != 'radio'){
23364             var hidden =  {
23365                 tag: 'input',
23366                 type : 'hidden',
23367                 cls : 'roo-hidden-value',
23368                 value : this.checked ? this.inputValue : this.valueOff
23369             };
23370         }
23371         
23372             
23373         if (this.weight) { // Validity check?
23374             cfg.cls += " " + this.inputType + "-" + this.weight;
23375         }
23376         
23377         if (this.disabled) {
23378             input.disabled=true;
23379         }
23380         
23381         if(this.checked){
23382             input.checked = this.checked;
23383         }
23384         
23385         if (this.name) {
23386             
23387             input.name = this.name;
23388             
23389             if(this.inputType != 'radio'){
23390                 hidden.name = this.name;
23391                 input.name = '_hidden_' + this.name;
23392             }
23393         }
23394         
23395         if (this.size) {
23396             input.cls += ' input-' + this.size;
23397         }
23398         
23399         var settings=this;
23400         
23401         ['xs','sm','md','lg'].map(function(size){
23402             if (settings[size]) {
23403                 cfg.cls += ' col-' + size + '-' + settings[size];
23404             }
23405         });
23406         
23407         var inputblock = input;
23408          
23409         if (this.before || this.after) {
23410             
23411             inputblock = {
23412                 cls : 'input-group',
23413                 cn :  [] 
23414             };
23415             
23416             if (this.before) {
23417                 inputblock.cn.push({
23418                     tag :'span',
23419                     cls : 'input-group-addon',
23420                     html : this.before
23421                 });
23422             }
23423             
23424             inputblock.cn.push(input);
23425             
23426             if(this.inputType != 'radio'){
23427                 inputblock.cn.push(hidden);
23428             }
23429             
23430             if (this.after) {
23431                 inputblock.cn.push({
23432                     tag :'span',
23433                     cls : 'input-group-addon',
23434                     html : this.after
23435                 });
23436             }
23437             
23438         }
23439         var boxLabelCfg = false;
23440         
23441         if(this.boxLabel){
23442            
23443             boxLabelCfg = {
23444                 tag: 'label',
23445                 //'for': id, // box label is handled by onclick - so no for...
23446                 cls: 'box-label',
23447                 html: this.boxLabel
23448             };
23449             if(this.tooltip){
23450                 boxLabelCfg.tooltip = this.tooltip;
23451             }
23452              
23453         }
23454         
23455         
23456         if (align ==='left' && this.fieldLabel.length) {
23457 //                Roo.log("left and has label");
23458             cfg.cn = [
23459                 {
23460                     tag: 'label',
23461                     'for' :  id,
23462                     cls : 'control-label',
23463                     html : this.fieldLabel
23464                 },
23465                 {
23466                     cls : "", 
23467                     cn: [
23468                         inputblock
23469                     ]
23470                 }
23471             ];
23472             
23473             if (boxLabelCfg) {
23474                 cfg.cn[1].cn.push(boxLabelCfg);
23475             }
23476             
23477             if(this.labelWidth > 12){
23478                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23479             }
23480             
23481             if(this.labelWidth < 13 && this.labelmd == 0){
23482                 this.labelmd = this.labelWidth;
23483             }
23484             
23485             if(this.labellg > 0){
23486                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23487                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23488             }
23489             
23490             if(this.labelmd > 0){
23491                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23492                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23493             }
23494             
23495             if(this.labelsm > 0){
23496                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23497                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23498             }
23499             
23500             if(this.labelxs > 0){
23501                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23502                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23503             }
23504             
23505         } else if ( this.fieldLabel.length) {
23506 //                Roo.log(" label");
23507                 cfg.cn = [
23508                    
23509                     {
23510                         tag: this.boxLabel ? 'span' : 'label',
23511                         'for': id,
23512                         cls: 'control-label box-input-label',
23513                         //cls : 'input-group-addon',
23514                         html : this.fieldLabel
23515                     },
23516                     
23517                     inputblock
23518                     
23519                 ];
23520                 if (boxLabelCfg) {
23521                     cfg.cn.push(boxLabelCfg);
23522                 }
23523
23524         } else {
23525             
23526 //                Roo.log(" no label && no align");
23527                 cfg.cn = [  inputblock ] ;
23528                 if (boxLabelCfg) {
23529                     cfg.cn.push(boxLabelCfg);
23530                 }
23531
23532                 
23533         }
23534         
23535        
23536         
23537         if(this.inputType != 'radio'){
23538             cfg.cn.push(hidden);
23539         }
23540         
23541         return cfg;
23542         
23543     },
23544     
23545     /**
23546      * return the real input element.
23547      */
23548     inputEl: function ()
23549     {
23550         return this.el.select('input.roo-' + this.inputType,true).first();
23551     },
23552     hiddenEl: function ()
23553     {
23554         return this.el.select('input.roo-hidden-value',true).first();
23555     },
23556     
23557     labelEl: function()
23558     {
23559         return this.el.select('label.control-label',true).first();
23560     },
23561     /* depricated... */
23562     
23563     label: function()
23564     {
23565         return this.labelEl();
23566     },
23567     
23568     boxLabelEl: function()
23569     {
23570         return this.el.select('label.box-label',true).first();
23571     },
23572     
23573     initEvents : function()
23574     {
23575 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23576         
23577         this.inputEl().on('click', this.onClick,  this);
23578         
23579         if (this.boxLabel) { 
23580             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23581         }
23582         
23583         this.startValue = this.getValue();
23584         
23585         if(this.groupId){
23586             Roo.bootstrap.CheckBox.register(this);
23587         }
23588     },
23589     
23590     onClick : function(e)
23591     {   
23592         if(this.fireEvent('click', this, e) !== false){
23593             this.setChecked(!this.checked);
23594         }
23595         
23596     },
23597     
23598     setChecked : function(state,suppressEvent)
23599     {
23600         this.startValue = this.getValue();
23601
23602         if(this.inputType == 'radio'){
23603             
23604             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23605                 e.dom.checked = false;
23606             });
23607             
23608             this.inputEl().dom.checked = true;
23609             
23610             this.inputEl().dom.value = this.inputValue;
23611             
23612             if(suppressEvent !== true){
23613                 this.fireEvent('check', this, true);
23614             }
23615             
23616             this.validate();
23617             
23618             return;
23619         }
23620         
23621         this.checked = state;
23622         
23623         this.inputEl().dom.checked = state;
23624         
23625         
23626         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23627         
23628         if(suppressEvent !== true){
23629             this.fireEvent('check', this, state);
23630         }
23631         
23632         this.validate();
23633     },
23634     
23635     getValue : function()
23636     {
23637         if(this.inputType == 'radio'){
23638             return this.getGroupValue();
23639         }
23640         
23641         return this.hiddenEl().dom.value;
23642         
23643     },
23644     
23645     getGroupValue : function()
23646     {
23647         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23648             return '';
23649         }
23650         
23651         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23652     },
23653     
23654     setValue : function(v,suppressEvent)
23655     {
23656         if(this.inputType == 'radio'){
23657             this.setGroupValue(v, suppressEvent);
23658             return;
23659         }
23660         
23661         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23662         
23663         this.validate();
23664     },
23665     
23666     setGroupValue : function(v, suppressEvent)
23667     {
23668         this.startValue = this.getValue();
23669         
23670         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23671             e.dom.checked = false;
23672             
23673             if(e.dom.value == v){
23674                 e.dom.checked = true;
23675             }
23676         });
23677         
23678         if(suppressEvent !== true){
23679             this.fireEvent('check', this, true);
23680         }
23681
23682         this.validate();
23683         
23684         return;
23685     },
23686     
23687     validate : function()
23688     {
23689         if(this.getVisibilityEl().hasClass('hidden')){
23690             return true;
23691         }
23692         
23693         if(
23694                 this.disabled || 
23695                 (this.inputType == 'radio' && this.validateRadio()) ||
23696                 (this.inputType == 'checkbox' && this.validateCheckbox())
23697         ){
23698             this.markValid();
23699             return true;
23700         }
23701         
23702         this.markInvalid();
23703         return false;
23704     },
23705     
23706     validateRadio : function()
23707     {
23708         if(this.getVisibilityEl().hasClass('hidden')){
23709             return true;
23710         }
23711         
23712         if(this.allowBlank){
23713             return true;
23714         }
23715         
23716         var valid = false;
23717         
23718         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23719             if(!e.dom.checked){
23720                 return;
23721             }
23722             
23723             valid = true;
23724             
23725             return false;
23726         });
23727         
23728         return valid;
23729     },
23730     
23731     validateCheckbox : function()
23732     {
23733         if(!this.groupId){
23734             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23735             //return (this.getValue() == this.inputValue) ? true : false;
23736         }
23737         
23738         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23739         
23740         if(!group){
23741             return false;
23742         }
23743         
23744         var r = false;
23745         
23746         for(var i in group){
23747             if(group[i].el.isVisible(true)){
23748                 r = false;
23749                 break;
23750             }
23751             
23752             r = true;
23753         }
23754         
23755         for(var i in group){
23756             if(r){
23757                 break;
23758             }
23759             
23760             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23761         }
23762         
23763         return r;
23764     },
23765     
23766     /**
23767      * Mark this field as valid
23768      */
23769     markValid : function()
23770     {
23771         var _this = this;
23772         
23773         this.fireEvent('valid', this);
23774         
23775         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23776         
23777         if(this.groupId){
23778             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23779         }
23780         
23781         if(label){
23782             label.markValid();
23783         }
23784
23785         if(this.inputType == 'radio'){
23786             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23787                 var fg = e.findParent('.form-group', false, true);
23788                 if (Roo.bootstrap.version == 3) {
23789                     fg.removeClass([_this.invalidClass, _this.validClass]);
23790                     fg.addClass(_this.validClass);
23791                 } else {
23792                     fg.removeClass(['is-valid', 'is-invalid']);
23793                     fg.addClass('is-valid');
23794                 }
23795             });
23796             
23797             return;
23798         }
23799
23800         if(!this.groupId){
23801             var fg = this.el.findParent('.form-group', false, true);
23802             if (Roo.bootstrap.version == 3) {
23803                 fg.removeClass([this.invalidClass, this.validClass]);
23804                 fg.addClass(this.validClass);
23805             } else {
23806                 fg.removeClass(['is-valid', 'is-invalid']);
23807                 fg.addClass('is-valid');
23808             }
23809             return;
23810         }
23811         
23812         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23813         
23814         if(!group){
23815             return;
23816         }
23817         
23818         for(var i in group){
23819             var fg = group[i].el.findParent('.form-group', false, true);
23820             if (Roo.bootstrap.version == 3) {
23821                 fg.removeClass([this.invalidClass, this.validClass]);
23822                 fg.addClass(this.validClass);
23823             } else {
23824                 fg.removeClass(['is-valid', 'is-invalid']);
23825                 fg.addClass('is-valid');
23826             }
23827         }
23828     },
23829     
23830      /**
23831      * Mark this field as invalid
23832      * @param {String} msg The validation message
23833      */
23834     markInvalid : function(msg)
23835     {
23836         if(this.allowBlank){
23837             return;
23838         }
23839         
23840         var _this = this;
23841         
23842         this.fireEvent('invalid', this, msg);
23843         
23844         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23845         
23846         if(this.groupId){
23847             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23848         }
23849         
23850         if(label){
23851             label.markInvalid();
23852         }
23853             
23854         if(this.inputType == 'radio'){
23855             
23856             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23857                 var fg = e.findParent('.form-group', false, true);
23858                 if (Roo.bootstrap.version == 3) {
23859                     fg.removeClass([_this.invalidClass, _this.validClass]);
23860                     fg.addClass(_this.invalidClass);
23861                 } else {
23862                     fg.removeClass(['is-invalid', 'is-valid']);
23863                     fg.addClass('is-invalid');
23864                 }
23865             });
23866             
23867             return;
23868         }
23869         
23870         if(!this.groupId){
23871             var fg = this.el.findParent('.form-group', false, true);
23872             if (Roo.bootstrap.version == 3) {
23873                 fg.removeClass([_this.invalidClass, _this.validClass]);
23874                 fg.addClass(_this.invalidClass);
23875             } else {
23876                 fg.removeClass(['is-invalid', 'is-valid']);
23877                 fg.addClass('is-invalid');
23878             }
23879             return;
23880         }
23881         
23882         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23883         
23884         if(!group){
23885             return;
23886         }
23887         
23888         for(var i in group){
23889             var fg = group[i].el.findParent('.form-group', false, true);
23890             if (Roo.bootstrap.version == 3) {
23891                 fg.removeClass([_this.invalidClass, _this.validClass]);
23892                 fg.addClass(_this.invalidClass);
23893             } else {
23894                 fg.removeClass(['is-invalid', 'is-valid']);
23895                 fg.addClass('is-invalid');
23896             }
23897         }
23898         
23899     },
23900     
23901     clearInvalid : function()
23902     {
23903         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23904         
23905         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23906         
23907         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23908         
23909         if (label && label.iconEl) {
23910             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23911             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23912         }
23913     },
23914     
23915     disable : function()
23916     {
23917         if(this.inputType != 'radio'){
23918             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23919             return;
23920         }
23921         
23922         var _this = this;
23923         
23924         if(this.rendered){
23925             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23926                 _this.getActionEl().addClass(this.disabledClass);
23927                 e.dom.disabled = true;
23928             });
23929         }
23930         
23931         this.disabled = true;
23932         this.fireEvent("disable", this);
23933         return this;
23934     },
23935
23936     enable : function()
23937     {
23938         if(this.inputType != 'radio'){
23939             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23940             return;
23941         }
23942         
23943         var _this = this;
23944         
23945         if(this.rendered){
23946             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23947                 _this.getActionEl().removeClass(this.disabledClass);
23948                 e.dom.disabled = false;
23949             });
23950         }
23951         
23952         this.disabled = false;
23953         this.fireEvent("enable", this);
23954         return this;
23955     },
23956     
23957     setBoxLabel : function(v)
23958     {
23959         this.boxLabel = v;
23960         
23961         if(this.rendered){
23962             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23963         }
23964     }
23965
23966 });
23967
23968 Roo.apply(Roo.bootstrap.CheckBox, {
23969     
23970     groups: {},
23971     
23972      /**
23973     * register a CheckBox Group
23974     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23975     */
23976     register : function(checkbox)
23977     {
23978         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23979             this.groups[checkbox.groupId] = {};
23980         }
23981         
23982         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23983             return;
23984         }
23985         
23986         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23987         
23988     },
23989     /**
23990     * fetch a CheckBox Group based on the group ID
23991     * @param {string} the group ID
23992     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23993     */
23994     get: function(groupId) {
23995         if (typeof(this.groups[groupId]) == 'undefined') {
23996             return false;
23997         }
23998         
23999         return this.groups[groupId] ;
24000     }
24001     
24002     
24003 });
24004 /*
24005  * - LGPL
24006  *
24007  * RadioItem
24008  * 
24009  */
24010
24011 /**
24012  * @class Roo.bootstrap.Radio
24013  * @extends Roo.bootstrap.Component
24014  * Bootstrap Radio class
24015  * @cfg {String} boxLabel - the label associated
24016  * @cfg {String} value - the value of radio
24017  * 
24018  * @constructor
24019  * Create a new Radio
24020  * @param {Object} config The config object
24021  */
24022 Roo.bootstrap.Radio = function(config){
24023     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
24024     
24025 };
24026
24027 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
24028     
24029     boxLabel : '',
24030     
24031     value : '',
24032     
24033     getAutoCreate : function()
24034     {
24035         var cfg = {
24036             tag : 'div',
24037             cls : 'form-group radio',
24038             cn : [
24039                 {
24040                     tag : 'label',
24041                     cls : 'box-label',
24042                     html : this.boxLabel
24043                 }
24044             ]
24045         };
24046         
24047         return cfg;
24048     },
24049     
24050     initEvents : function() 
24051     {
24052         this.parent().register(this);
24053         
24054         this.el.on('click', this.onClick, this);
24055         
24056     },
24057     
24058     onClick : function(e)
24059     {
24060         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
24061             this.setChecked(true);
24062         }
24063     },
24064     
24065     setChecked : function(state, suppressEvent)
24066     {
24067         this.parent().setValue(this.value, suppressEvent);
24068         
24069     },
24070     
24071     setBoxLabel : function(v)
24072     {
24073         this.boxLabel = v;
24074         
24075         if(this.rendered){
24076             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
24077         }
24078     }
24079     
24080 });
24081  
24082
24083  /*
24084  * - LGPL
24085  *
24086  * Input
24087  * 
24088  */
24089
24090 /**
24091  * @class Roo.bootstrap.SecurePass
24092  * @extends Roo.bootstrap.Input
24093  * Bootstrap SecurePass class
24094  *
24095  * 
24096  * @constructor
24097  * Create a new SecurePass
24098  * @param {Object} config The config object
24099  */
24100  
24101 Roo.bootstrap.SecurePass = function (config) {
24102     // these go here, so the translation tool can replace them..
24103     this.errors = {
24104         PwdEmpty: "Please type a password, and then retype it to confirm.",
24105         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24106         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24107         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24108         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24109         FNInPwd: "Your password can't contain your first name. Please type a different password.",
24110         LNInPwd: "Your password can't contain your last name. Please type a different password.",
24111         TooWeak: "Your password is Too Weak."
24112     },
24113     this.meterLabel = "Password strength:";
24114     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
24115     this.meterClass = [
24116         "roo-password-meter-tooweak", 
24117         "roo-password-meter-weak", 
24118         "roo-password-meter-medium", 
24119         "roo-password-meter-strong", 
24120         "roo-password-meter-grey"
24121     ];
24122     
24123     this.errors = {};
24124     
24125     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
24126 }
24127
24128 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
24129     /**
24130      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
24131      * {
24132      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
24133      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
24134      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
24135      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
24136      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
24137      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
24138      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
24139      * })
24140      */
24141     // private
24142     
24143     meterWidth: 300,
24144     errorMsg :'',    
24145     errors: false,
24146     imageRoot: '/',
24147     /**
24148      * @cfg {String/Object} Label for the strength meter (defaults to
24149      * 'Password strength:')
24150      */
24151     // private
24152     meterLabel: '',
24153     /**
24154      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
24155      * ['Weak', 'Medium', 'Strong'])
24156      */
24157     // private    
24158     pwdStrengths: false,    
24159     // private
24160     strength: 0,
24161     // private
24162     _lastPwd: null,
24163     // private
24164     kCapitalLetter: 0,
24165     kSmallLetter: 1,
24166     kDigit: 2,
24167     kPunctuation: 3,
24168     
24169     insecure: false,
24170     // private
24171     initEvents: function ()
24172     {
24173         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
24174
24175         if (this.el.is('input[type=password]') && Roo.isSafari) {
24176             this.el.on('keydown', this.SafariOnKeyDown, this);
24177         }
24178
24179         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
24180     },
24181     // private
24182     onRender: function (ct, position)
24183     {
24184         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
24185         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
24186         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
24187
24188         this.trigger.createChild({
24189                    cn: [
24190                     {
24191                     //id: 'PwdMeter',
24192                     tag: 'div',
24193                     cls: 'roo-password-meter-grey col-xs-12',
24194                     style: {
24195                         //width: 0,
24196                         //width: this.meterWidth + 'px'                                                
24197                         }
24198                     },
24199                     {                            
24200                          cls: 'roo-password-meter-text'                          
24201                     }
24202                 ]            
24203         });
24204
24205          
24206         if (this.hideTrigger) {
24207             this.trigger.setDisplayed(false);
24208         }
24209         this.setSize(this.width || '', this.height || '');
24210     },
24211     // private
24212     onDestroy: function ()
24213     {
24214         if (this.trigger) {
24215             this.trigger.removeAllListeners();
24216             this.trigger.remove();
24217         }
24218         if (this.wrap) {
24219             this.wrap.remove();
24220         }
24221         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
24222     },
24223     // private
24224     checkStrength: function ()
24225     {
24226         var pwd = this.inputEl().getValue();
24227         if (pwd == this._lastPwd) {
24228             return;
24229         }
24230
24231         var strength;
24232         if (this.ClientSideStrongPassword(pwd)) {
24233             strength = 3;
24234         } else if (this.ClientSideMediumPassword(pwd)) {
24235             strength = 2;
24236         } else if (this.ClientSideWeakPassword(pwd)) {
24237             strength = 1;
24238         } else {
24239             strength = 0;
24240         }
24241         
24242         Roo.log('strength1: ' + strength);
24243         
24244         //var pm = this.trigger.child('div/div/div').dom;
24245         var pm = this.trigger.child('div/div');
24246         pm.removeClass(this.meterClass);
24247         pm.addClass(this.meterClass[strength]);
24248                 
24249         
24250         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24251                 
24252         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24253         
24254         this._lastPwd = pwd;
24255     },
24256     reset: function ()
24257     {
24258         Roo.bootstrap.SecurePass.superclass.reset.call(this);
24259         
24260         this._lastPwd = '';
24261         
24262         var pm = this.trigger.child('div/div');
24263         pm.removeClass(this.meterClass);
24264         pm.addClass('roo-password-meter-grey');        
24265         
24266         
24267         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24268         
24269         pt.innerHTML = '';
24270         this.inputEl().dom.type='password';
24271     },
24272     // private
24273     validateValue: function (value)
24274     {
24275         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
24276             return false;
24277         }
24278         if (value.length == 0) {
24279             if (this.allowBlank) {
24280                 this.clearInvalid();
24281                 return true;
24282             }
24283
24284             this.markInvalid(this.errors.PwdEmpty);
24285             this.errorMsg = this.errors.PwdEmpty;
24286             return false;
24287         }
24288         
24289         if(this.insecure){
24290             return true;
24291         }
24292         
24293         if (!value.match(/[\x21-\x7e]+/)) {
24294             this.markInvalid(this.errors.PwdBadChar);
24295             this.errorMsg = this.errors.PwdBadChar;
24296             return false;
24297         }
24298         if (value.length < 6) {
24299             this.markInvalid(this.errors.PwdShort);
24300             this.errorMsg = this.errors.PwdShort;
24301             return false;
24302         }
24303         if (value.length > 16) {
24304             this.markInvalid(this.errors.PwdLong);
24305             this.errorMsg = this.errors.PwdLong;
24306             return false;
24307         }
24308         var strength;
24309         if (this.ClientSideStrongPassword(value)) {
24310             strength = 3;
24311         } else if (this.ClientSideMediumPassword(value)) {
24312             strength = 2;
24313         } else if (this.ClientSideWeakPassword(value)) {
24314             strength = 1;
24315         } else {
24316             strength = 0;
24317         }
24318
24319         
24320         if (strength < 2) {
24321             //this.markInvalid(this.errors.TooWeak);
24322             this.errorMsg = this.errors.TooWeak;
24323             //return false;
24324         }
24325         
24326         
24327         console.log('strength2: ' + strength);
24328         
24329         //var pm = this.trigger.child('div/div/div').dom;
24330         
24331         var pm = this.trigger.child('div/div');
24332         pm.removeClass(this.meterClass);
24333         pm.addClass(this.meterClass[strength]);
24334                 
24335         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24336                 
24337         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24338         
24339         this.errorMsg = ''; 
24340         return true;
24341     },
24342     // private
24343     CharacterSetChecks: function (type)
24344     {
24345         this.type = type;
24346         this.fResult = false;
24347     },
24348     // private
24349     isctype: function (character, type)
24350     {
24351         switch (type) {  
24352             case this.kCapitalLetter:
24353                 if (character >= 'A' && character <= 'Z') {
24354                     return true;
24355                 }
24356                 break;
24357             
24358             case this.kSmallLetter:
24359                 if (character >= 'a' && character <= 'z') {
24360                     return true;
24361                 }
24362                 break;
24363             
24364             case this.kDigit:
24365                 if (character >= '0' && character <= '9') {
24366                     return true;
24367                 }
24368                 break;
24369             
24370             case this.kPunctuation:
24371                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24372                     return true;
24373                 }
24374                 break;
24375             
24376             default:
24377                 return false;
24378         }
24379
24380     },
24381     // private
24382     IsLongEnough: function (pwd, size)
24383     {
24384         return !(pwd == null || isNaN(size) || pwd.length < size);
24385     },
24386     // private
24387     SpansEnoughCharacterSets: function (word, nb)
24388     {
24389         if (!this.IsLongEnough(word, nb))
24390         {
24391             return false;
24392         }
24393
24394         var characterSetChecks = new Array(
24395             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24396             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24397         );
24398         
24399         for (var index = 0; index < word.length; ++index) {
24400             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24401                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24402                     characterSetChecks[nCharSet].fResult = true;
24403                     break;
24404                 }
24405             }
24406         }
24407
24408         var nCharSets = 0;
24409         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24410             if (characterSetChecks[nCharSet].fResult) {
24411                 ++nCharSets;
24412             }
24413         }
24414
24415         if (nCharSets < nb) {
24416             return false;
24417         }
24418         return true;
24419     },
24420     // private
24421     ClientSideStrongPassword: function (pwd)
24422     {
24423         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24424     },
24425     // private
24426     ClientSideMediumPassword: function (pwd)
24427     {
24428         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24429     },
24430     // private
24431     ClientSideWeakPassword: function (pwd)
24432     {
24433         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24434     }
24435           
24436 })//<script type="text/javascript">
24437
24438 /*
24439  * Based  Ext JS Library 1.1.1
24440  * Copyright(c) 2006-2007, Ext JS, LLC.
24441  * LGPL
24442  *
24443  */
24444  
24445 /**
24446  * @class Roo.HtmlEditorCore
24447  * @extends Roo.Component
24448  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24449  *
24450  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24451  */
24452
24453 Roo.HtmlEditorCore = function(config){
24454     
24455     
24456     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24457     
24458     
24459     this.addEvents({
24460         /**
24461          * @event initialize
24462          * Fires when the editor is fully initialized (including the iframe)
24463          * @param {Roo.HtmlEditorCore} this
24464          */
24465         initialize: true,
24466         /**
24467          * @event activate
24468          * Fires when the editor is first receives the focus. Any insertion must wait
24469          * until after this event.
24470          * @param {Roo.HtmlEditorCore} this
24471          */
24472         activate: true,
24473          /**
24474          * @event beforesync
24475          * Fires before the textarea is updated with content from the editor iframe. Return false
24476          * to cancel the sync.
24477          * @param {Roo.HtmlEditorCore} this
24478          * @param {String} html
24479          */
24480         beforesync: true,
24481          /**
24482          * @event beforepush
24483          * Fires before the iframe editor is updated with content from the textarea. Return false
24484          * to cancel the push.
24485          * @param {Roo.HtmlEditorCore} this
24486          * @param {String} html
24487          */
24488         beforepush: true,
24489          /**
24490          * @event sync
24491          * Fires when the textarea is updated with content from the editor iframe.
24492          * @param {Roo.HtmlEditorCore} this
24493          * @param {String} html
24494          */
24495         sync: true,
24496          /**
24497          * @event push
24498          * Fires when the iframe editor is updated with content from the textarea.
24499          * @param {Roo.HtmlEditorCore} this
24500          * @param {String} html
24501          */
24502         push: true,
24503         
24504         /**
24505          * @event editorevent
24506          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24507          * @param {Roo.HtmlEditorCore} this
24508          */
24509         editorevent: true
24510         
24511     });
24512     
24513     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24514     
24515     // defaults : white / black...
24516     this.applyBlacklists();
24517     
24518     
24519     
24520 };
24521
24522
24523 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24524
24525
24526      /**
24527      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24528      */
24529     
24530     owner : false,
24531     
24532      /**
24533      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24534      *                        Roo.resizable.
24535      */
24536     resizable : false,
24537      /**
24538      * @cfg {Number} height (in pixels)
24539      */   
24540     height: 300,
24541    /**
24542      * @cfg {Number} width (in pixels)
24543      */   
24544     width: 500,
24545     
24546     /**
24547      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24548      * 
24549      */
24550     stylesheets: false,
24551     
24552     // id of frame..
24553     frameId: false,
24554     
24555     // private properties
24556     validationEvent : false,
24557     deferHeight: true,
24558     initialized : false,
24559     activated : false,
24560     sourceEditMode : false,
24561     onFocus : Roo.emptyFn,
24562     iframePad:3,
24563     hideMode:'offsets',
24564     
24565     clearUp: true,
24566     
24567     // blacklist + whitelisted elements..
24568     black: false,
24569     white: false,
24570      
24571     bodyCls : '',
24572
24573     /**
24574      * Protected method that will not generally be called directly. It
24575      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24576      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24577      */
24578     getDocMarkup : function(){
24579         // body styles..
24580         var st = '';
24581         
24582         // inherit styels from page...?? 
24583         if (this.stylesheets === false) {
24584             
24585             Roo.get(document.head).select('style').each(function(node) {
24586                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24587             });
24588             
24589             Roo.get(document.head).select('link').each(function(node) { 
24590                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24591             });
24592             
24593         } else if (!this.stylesheets.length) {
24594                 // simple..
24595                 st = '<style type="text/css">' +
24596                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24597                    '</style>';
24598         } else {
24599             for (var i in this.stylesheets) { 
24600                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24601             }
24602             
24603         }
24604         
24605         st +=  '<style type="text/css">' +
24606             'IMG { cursor: pointer } ' +
24607         '</style>';
24608
24609         var cls = 'roo-htmleditor-body';
24610         
24611         if(this.bodyCls.length){
24612             cls += ' ' + this.bodyCls;
24613         }
24614         
24615         return '<html><head>' + st  +
24616             //<style type="text/css">' +
24617             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24618             //'</style>' +
24619             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24620     },
24621
24622     // private
24623     onRender : function(ct, position)
24624     {
24625         var _t = this;
24626         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24627         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24628         
24629         
24630         this.el.dom.style.border = '0 none';
24631         this.el.dom.setAttribute('tabIndex', -1);
24632         this.el.addClass('x-hidden hide');
24633         
24634         
24635         
24636         if(Roo.isIE){ // fix IE 1px bogus margin
24637             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24638         }
24639        
24640         
24641         this.frameId = Roo.id();
24642         
24643          
24644         
24645         var iframe = this.owner.wrap.createChild({
24646             tag: 'iframe',
24647             cls: 'form-control', // bootstrap..
24648             id: this.frameId,
24649             name: this.frameId,
24650             frameBorder : 'no',
24651             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24652         }, this.el
24653         );
24654         
24655         
24656         this.iframe = iframe.dom;
24657
24658          this.assignDocWin();
24659         
24660         this.doc.designMode = 'on';
24661        
24662         this.doc.open();
24663         this.doc.write(this.getDocMarkup());
24664         this.doc.close();
24665
24666         
24667         var task = { // must defer to wait for browser to be ready
24668             run : function(){
24669                 //console.log("run task?" + this.doc.readyState);
24670                 this.assignDocWin();
24671                 if(this.doc.body || this.doc.readyState == 'complete'){
24672                     try {
24673                         this.doc.designMode="on";
24674                     } catch (e) {
24675                         return;
24676                     }
24677                     Roo.TaskMgr.stop(task);
24678                     this.initEditor.defer(10, this);
24679                 }
24680             },
24681             interval : 10,
24682             duration: 10000,
24683             scope: this
24684         };
24685         Roo.TaskMgr.start(task);
24686
24687     },
24688
24689     // private
24690     onResize : function(w, h)
24691     {
24692          Roo.log('resize: ' +w + ',' + h );
24693         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24694         if(!this.iframe){
24695             return;
24696         }
24697         if(typeof w == 'number'){
24698             
24699             this.iframe.style.width = w + 'px';
24700         }
24701         if(typeof h == 'number'){
24702             
24703             this.iframe.style.height = h + 'px';
24704             if(this.doc){
24705                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24706             }
24707         }
24708         
24709     },
24710
24711     /**
24712      * Toggles the editor between standard and source edit mode.
24713      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24714      */
24715     toggleSourceEdit : function(sourceEditMode){
24716         
24717         this.sourceEditMode = sourceEditMode === true;
24718         
24719         if(this.sourceEditMode){
24720  
24721             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24722             
24723         }else{
24724             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24725             //this.iframe.className = '';
24726             this.deferFocus();
24727         }
24728         //this.setSize(this.owner.wrap.getSize());
24729         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24730     },
24731
24732     
24733   
24734
24735     /**
24736      * Protected method that will not generally be called directly. If you need/want
24737      * custom HTML cleanup, this is the method you should override.
24738      * @param {String} html The HTML to be cleaned
24739      * return {String} The cleaned HTML
24740      */
24741     cleanHtml : function(html){
24742         html = String(html);
24743         if(html.length > 5){
24744             if(Roo.isSafari){ // strip safari nonsense
24745                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24746             }
24747         }
24748         if(html == '&nbsp;'){
24749             html = '';
24750         }
24751         return html;
24752     },
24753
24754     /**
24755      * HTML Editor -> Textarea
24756      * Protected method that will not generally be called directly. Syncs the contents
24757      * of the editor iframe with the textarea.
24758      */
24759     syncValue : function(){
24760         if(this.initialized){
24761             var bd = (this.doc.body || this.doc.documentElement);
24762             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24763             var html = bd.innerHTML;
24764             if(Roo.isSafari){
24765                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24766                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24767                 if(m && m[1]){
24768                     html = '<div style="'+m[0]+'">' + html + '</div>';
24769                 }
24770             }
24771             html = this.cleanHtml(html);
24772             // fix up the special chars.. normaly like back quotes in word...
24773             // however we do not want to do this with chinese..
24774             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24775                 
24776                 var cc = match.charCodeAt();
24777
24778                 // Get the character value, handling surrogate pairs
24779                 if (match.length == 2) {
24780                     // It's a surrogate pair, calculate the Unicode code point
24781                     var high = match.charCodeAt(0) - 0xD800;
24782                     var low  = match.charCodeAt(1) - 0xDC00;
24783                     cc = (high * 0x400) + low + 0x10000;
24784                 }  else if (
24785                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24786                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24787                     (cc >= 0xf900 && cc < 0xfb00 )
24788                 ) {
24789                         return match;
24790                 }  
24791          
24792                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24793                 return "&#" + cc + ";";
24794                 
24795                 
24796             });
24797             
24798             
24799              
24800             if(this.owner.fireEvent('beforesync', this, html) !== false){
24801                 this.el.dom.value = html;
24802                 this.owner.fireEvent('sync', this, html);
24803             }
24804         }
24805     },
24806
24807     /**
24808      * Protected method that will not generally be called directly. Pushes the value of the textarea
24809      * into the iframe editor.
24810      */
24811     pushValue : function(){
24812         if(this.initialized){
24813             var v = this.el.dom.value.trim();
24814             
24815 //            if(v.length < 1){
24816 //                v = '&#160;';
24817 //            }
24818             
24819             if(this.owner.fireEvent('beforepush', this, v) !== false){
24820                 var d = (this.doc.body || this.doc.documentElement);
24821                 d.innerHTML = v;
24822                 this.cleanUpPaste();
24823                 this.el.dom.value = d.innerHTML;
24824                 this.owner.fireEvent('push', this, v);
24825             }
24826         }
24827     },
24828
24829     // private
24830     deferFocus : function(){
24831         this.focus.defer(10, this);
24832     },
24833
24834     // doc'ed in Field
24835     focus : function(){
24836         if(this.win && !this.sourceEditMode){
24837             this.win.focus();
24838         }else{
24839             this.el.focus();
24840         }
24841     },
24842     
24843     assignDocWin: function()
24844     {
24845         var iframe = this.iframe;
24846         
24847          if(Roo.isIE){
24848             this.doc = iframe.contentWindow.document;
24849             this.win = iframe.contentWindow;
24850         } else {
24851 //            if (!Roo.get(this.frameId)) {
24852 //                return;
24853 //            }
24854 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24855 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24856             
24857             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24858                 return;
24859             }
24860             
24861             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24862             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24863         }
24864     },
24865     
24866     // private
24867     initEditor : function(){
24868         //console.log("INIT EDITOR");
24869         this.assignDocWin();
24870         
24871         
24872         
24873         this.doc.designMode="on";
24874         this.doc.open();
24875         this.doc.write(this.getDocMarkup());
24876         this.doc.close();
24877         
24878         var dbody = (this.doc.body || this.doc.documentElement);
24879         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24880         // this copies styles from the containing element into thsi one..
24881         // not sure why we need all of this..
24882         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24883         
24884         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24885         //ss['background-attachment'] = 'fixed'; // w3c
24886         dbody.bgProperties = 'fixed'; // ie
24887         //Roo.DomHelper.applyStyles(dbody, ss);
24888         Roo.EventManager.on(this.doc, {
24889             //'mousedown': this.onEditorEvent,
24890             'mouseup': this.onEditorEvent,
24891             'dblclick': this.onEditorEvent,
24892             'click': this.onEditorEvent,
24893             'keyup': this.onEditorEvent,
24894             buffer:100,
24895             scope: this
24896         });
24897         if(Roo.isGecko){
24898             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24899         }
24900         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24901             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24902         }
24903         this.initialized = true;
24904
24905         this.owner.fireEvent('initialize', this);
24906         this.pushValue();
24907     },
24908
24909     // private
24910     onDestroy : function(){
24911         
24912         
24913         
24914         if(this.rendered){
24915             
24916             //for (var i =0; i < this.toolbars.length;i++) {
24917             //    // fixme - ask toolbars for heights?
24918             //    this.toolbars[i].onDestroy();
24919            // }
24920             
24921             //this.wrap.dom.innerHTML = '';
24922             //this.wrap.remove();
24923         }
24924     },
24925
24926     // private
24927     onFirstFocus : function(){
24928         
24929         this.assignDocWin();
24930         
24931         
24932         this.activated = true;
24933          
24934     
24935         if(Roo.isGecko){ // prevent silly gecko errors
24936             this.win.focus();
24937             var s = this.win.getSelection();
24938             if(!s.focusNode || s.focusNode.nodeType != 3){
24939                 var r = s.getRangeAt(0);
24940                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24941                 r.collapse(true);
24942                 this.deferFocus();
24943             }
24944             try{
24945                 this.execCmd('useCSS', true);
24946                 this.execCmd('styleWithCSS', false);
24947             }catch(e){}
24948         }
24949         this.owner.fireEvent('activate', this);
24950     },
24951
24952     // private
24953     adjustFont: function(btn){
24954         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24955         //if(Roo.isSafari){ // safari
24956         //    adjust *= 2;
24957        // }
24958         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24959         if(Roo.isSafari){ // safari
24960             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24961             v =  (v < 10) ? 10 : v;
24962             v =  (v > 48) ? 48 : v;
24963             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24964             
24965         }
24966         
24967         
24968         v = Math.max(1, v+adjust);
24969         
24970         this.execCmd('FontSize', v  );
24971     },
24972
24973     onEditorEvent : function(e)
24974     {
24975         this.owner.fireEvent('editorevent', this, e);
24976       //  this.updateToolbar();
24977         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24978     },
24979
24980     insertTag : function(tg)
24981     {
24982         // could be a bit smarter... -> wrap the current selected tRoo..
24983         if (tg.toLowerCase() == 'span' ||
24984             tg.toLowerCase() == 'code' ||
24985             tg.toLowerCase() == 'sup' ||
24986             tg.toLowerCase() == 'sub' 
24987             ) {
24988             
24989             range = this.createRange(this.getSelection());
24990             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24991             wrappingNode.appendChild(range.extractContents());
24992             range.insertNode(wrappingNode);
24993
24994             return;
24995             
24996             
24997             
24998         }
24999         this.execCmd("formatblock",   tg);
25000         
25001     },
25002     
25003     insertText : function(txt)
25004     {
25005         
25006         
25007         var range = this.createRange();
25008         range.deleteContents();
25009                //alert(Sender.getAttribute('label'));
25010                
25011         range.insertNode(this.doc.createTextNode(txt));
25012     } ,
25013     
25014      
25015
25016     /**
25017      * Executes a Midas editor command on the editor document and performs necessary focus and
25018      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
25019      * @param {String} cmd The Midas command
25020      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25021      */
25022     relayCmd : function(cmd, value){
25023         this.win.focus();
25024         this.execCmd(cmd, value);
25025         this.owner.fireEvent('editorevent', this);
25026         //this.updateToolbar();
25027         this.owner.deferFocus();
25028     },
25029
25030     /**
25031      * Executes a Midas editor command directly on the editor document.
25032      * For visual commands, you should use {@link #relayCmd} instead.
25033      * <b>This should only be called after the editor is initialized.</b>
25034      * @param {String} cmd The Midas command
25035      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
25036      */
25037     execCmd : function(cmd, value){
25038         this.doc.execCommand(cmd, false, value === undefined ? null : value);
25039         this.syncValue();
25040     },
25041  
25042  
25043    
25044     /**
25045      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
25046      * to insert tRoo.
25047      * @param {String} text | dom node.. 
25048      */
25049     insertAtCursor : function(text)
25050     {
25051         
25052         if(!this.activated){
25053             return;
25054         }
25055         /*
25056         if(Roo.isIE){
25057             this.win.focus();
25058             var r = this.doc.selection.createRange();
25059             if(r){
25060                 r.collapse(true);
25061                 r.pasteHTML(text);
25062                 this.syncValue();
25063                 this.deferFocus();
25064             
25065             }
25066             return;
25067         }
25068         */
25069         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
25070             this.win.focus();
25071             
25072             
25073             // from jquery ui (MIT licenced)
25074             var range, node;
25075             var win = this.win;
25076             
25077             if (win.getSelection && win.getSelection().getRangeAt) {
25078                 range = win.getSelection().getRangeAt(0);
25079                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
25080                 range.insertNode(node);
25081             } else if (win.document.selection && win.document.selection.createRange) {
25082                 // no firefox support
25083                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25084                 win.document.selection.createRange().pasteHTML(txt);
25085             } else {
25086                 // no firefox support
25087                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
25088                 this.execCmd('InsertHTML', txt);
25089             } 
25090             
25091             this.syncValue();
25092             
25093             this.deferFocus();
25094         }
25095     },
25096  // private
25097     mozKeyPress : function(e){
25098         if(e.ctrlKey){
25099             var c = e.getCharCode(), cmd;
25100           
25101             if(c > 0){
25102                 c = String.fromCharCode(c).toLowerCase();
25103                 switch(c){
25104                     case 'b':
25105                         cmd = 'bold';
25106                         break;
25107                     case 'i':
25108                         cmd = 'italic';
25109                         break;
25110                     
25111                     case 'u':
25112                         cmd = 'underline';
25113                         break;
25114                     
25115                     case 'v':
25116                         this.cleanUpPaste.defer(100, this);
25117                         return;
25118                         
25119                 }
25120                 if(cmd){
25121                     this.win.focus();
25122                     this.execCmd(cmd);
25123                     this.deferFocus();
25124                     e.preventDefault();
25125                 }
25126                 
25127             }
25128         }
25129     },
25130
25131     // private
25132     fixKeys : function(){ // load time branching for fastest keydown performance
25133         if(Roo.isIE){
25134             return function(e){
25135                 var k = e.getKey(), r;
25136                 if(k == e.TAB){
25137                     e.stopEvent();
25138                     r = this.doc.selection.createRange();
25139                     if(r){
25140                         r.collapse(true);
25141                         r.pasteHTML('&#160;&#160;&#160;&#160;');
25142                         this.deferFocus();
25143                     }
25144                     return;
25145                 }
25146                 
25147                 if(k == e.ENTER){
25148                     r = this.doc.selection.createRange();
25149                     if(r){
25150                         var target = r.parentElement();
25151                         if(!target || target.tagName.toLowerCase() != 'li'){
25152                             e.stopEvent();
25153                             r.pasteHTML('<br />');
25154                             r.collapse(false);
25155                             r.select();
25156                         }
25157                     }
25158                 }
25159                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25160                     this.cleanUpPaste.defer(100, this);
25161                     return;
25162                 }
25163                 
25164                 
25165             };
25166         }else if(Roo.isOpera){
25167             return function(e){
25168                 var k = e.getKey();
25169                 if(k == e.TAB){
25170                     e.stopEvent();
25171                     this.win.focus();
25172                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
25173                     this.deferFocus();
25174                 }
25175                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25176                     this.cleanUpPaste.defer(100, this);
25177                     return;
25178                 }
25179                 
25180             };
25181         }else if(Roo.isSafari){
25182             return function(e){
25183                 var k = e.getKey();
25184                 
25185                 if(k == e.TAB){
25186                     e.stopEvent();
25187                     this.execCmd('InsertText','\t');
25188                     this.deferFocus();
25189                     return;
25190                 }
25191                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
25192                     this.cleanUpPaste.defer(100, this);
25193                     return;
25194                 }
25195                 
25196              };
25197         }
25198     }(),
25199     
25200     getAllAncestors: function()
25201     {
25202         var p = this.getSelectedNode();
25203         var a = [];
25204         if (!p) {
25205             a.push(p); // push blank onto stack..
25206             p = this.getParentElement();
25207         }
25208         
25209         
25210         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
25211             a.push(p);
25212             p = p.parentNode;
25213         }
25214         a.push(this.doc.body);
25215         return a;
25216     },
25217     lastSel : false,
25218     lastSelNode : false,
25219     
25220     
25221     getSelection : function() 
25222     {
25223         this.assignDocWin();
25224         return Roo.isIE ? this.doc.selection : this.win.getSelection();
25225     },
25226     
25227     getSelectedNode: function() 
25228     {
25229         // this may only work on Gecko!!!
25230         
25231         // should we cache this!!!!
25232         
25233         
25234         
25235          
25236         var range = this.createRange(this.getSelection()).cloneRange();
25237         
25238         if (Roo.isIE) {
25239             var parent = range.parentElement();
25240             while (true) {
25241                 var testRange = range.duplicate();
25242                 testRange.moveToElementText(parent);
25243                 if (testRange.inRange(range)) {
25244                     break;
25245                 }
25246                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
25247                     break;
25248                 }
25249                 parent = parent.parentElement;
25250             }
25251             return parent;
25252         }
25253         
25254         // is ancestor a text element.
25255         var ac =  range.commonAncestorContainer;
25256         if (ac.nodeType == 3) {
25257             ac = ac.parentNode;
25258         }
25259         
25260         var ar = ac.childNodes;
25261          
25262         var nodes = [];
25263         var other_nodes = [];
25264         var has_other_nodes = false;
25265         for (var i=0;i<ar.length;i++) {
25266             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
25267                 continue;
25268             }
25269             // fullly contained node.
25270             
25271             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
25272                 nodes.push(ar[i]);
25273                 continue;
25274             }
25275             
25276             // probably selected..
25277             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
25278                 other_nodes.push(ar[i]);
25279                 continue;
25280             }
25281             // outer..
25282             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
25283                 continue;
25284             }
25285             
25286             
25287             has_other_nodes = true;
25288         }
25289         if (!nodes.length && other_nodes.length) {
25290             nodes= other_nodes;
25291         }
25292         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
25293             return false;
25294         }
25295         
25296         return nodes[0];
25297     },
25298     createRange: function(sel)
25299     {
25300         // this has strange effects when using with 
25301         // top toolbar - not sure if it's a great idea.
25302         //this.editor.contentWindow.focus();
25303         if (typeof sel != "undefined") {
25304             try {
25305                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25306             } catch(e) {
25307                 return this.doc.createRange();
25308             }
25309         } else {
25310             return this.doc.createRange();
25311         }
25312     },
25313     getParentElement: function()
25314     {
25315         
25316         this.assignDocWin();
25317         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25318         
25319         var range = this.createRange(sel);
25320          
25321         try {
25322             var p = range.commonAncestorContainer;
25323             while (p.nodeType == 3) { // text node
25324                 p = p.parentNode;
25325             }
25326             return p;
25327         } catch (e) {
25328             return null;
25329         }
25330     
25331     },
25332     /***
25333      *
25334      * Range intersection.. the hard stuff...
25335      *  '-1' = before
25336      *  '0' = hits..
25337      *  '1' = after.
25338      *         [ -- selected range --- ]
25339      *   [fail]                        [fail]
25340      *
25341      *    basically..
25342      *      if end is before start or  hits it. fail.
25343      *      if start is after end or hits it fail.
25344      *
25345      *   if either hits (but other is outside. - then it's not 
25346      *   
25347      *    
25348      **/
25349     
25350     
25351     // @see http://www.thismuchiknow.co.uk/?p=64.
25352     rangeIntersectsNode : function(range, node)
25353     {
25354         var nodeRange = node.ownerDocument.createRange();
25355         try {
25356             nodeRange.selectNode(node);
25357         } catch (e) {
25358             nodeRange.selectNodeContents(node);
25359         }
25360     
25361         var rangeStartRange = range.cloneRange();
25362         rangeStartRange.collapse(true);
25363     
25364         var rangeEndRange = range.cloneRange();
25365         rangeEndRange.collapse(false);
25366     
25367         var nodeStartRange = nodeRange.cloneRange();
25368         nodeStartRange.collapse(true);
25369     
25370         var nodeEndRange = nodeRange.cloneRange();
25371         nodeEndRange.collapse(false);
25372     
25373         return rangeStartRange.compareBoundaryPoints(
25374                  Range.START_TO_START, nodeEndRange) == -1 &&
25375                rangeEndRange.compareBoundaryPoints(
25376                  Range.START_TO_START, nodeStartRange) == 1;
25377         
25378          
25379     },
25380     rangeCompareNode : function(range, node)
25381     {
25382         var nodeRange = node.ownerDocument.createRange();
25383         try {
25384             nodeRange.selectNode(node);
25385         } catch (e) {
25386             nodeRange.selectNodeContents(node);
25387         }
25388         
25389         
25390         range.collapse(true);
25391     
25392         nodeRange.collapse(true);
25393      
25394         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25395         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25396          
25397         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25398         
25399         var nodeIsBefore   =  ss == 1;
25400         var nodeIsAfter    = ee == -1;
25401         
25402         if (nodeIsBefore && nodeIsAfter) {
25403             return 0; // outer
25404         }
25405         if (!nodeIsBefore && nodeIsAfter) {
25406             return 1; //right trailed.
25407         }
25408         
25409         if (nodeIsBefore && !nodeIsAfter) {
25410             return 2;  // left trailed.
25411         }
25412         // fully contined.
25413         return 3;
25414     },
25415
25416     // private? - in a new class?
25417     cleanUpPaste :  function()
25418     {
25419         // cleans up the whole document..
25420         Roo.log('cleanuppaste');
25421         
25422         this.cleanUpChildren(this.doc.body);
25423         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25424         if (clean != this.doc.body.innerHTML) {
25425             this.doc.body.innerHTML = clean;
25426         }
25427         
25428     },
25429     
25430     cleanWordChars : function(input) {// change the chars to hex code
25431         var he = Roo.HtmlEditorCore;
25432         
25433         var output = input;
25434         Roo.each(he.swapCodes, function(sw) { 
25435             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25436             
25437             output = output.replace(swapper, sw[1]);
25438         });
25439         
25440         return output;
25441     },
25442     
25443     
25444     cleanUpChildren : function (n)
25445     {
25446         if (!n.childNodes.length) {
25447             return;
25448         }
25449         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25450            this.cleanUpChild(n.childNodes[i]);
25451         }
25452     },
25453     
25454     
25455         
25456     
25457     cleanUpChild : function (node)
25458     {
25459         var ed = this;
25460         //console.log(node);
25461         if (node.nodeName == "#text") {
25462             // clean up silly Windows -- stuff?
25463             return; 
25464         }
25465         if (node.nodeName == "#comment") {
25466             node.parentNode.removeChild(node);
25467             // clean up silly Windows -- stuff?
25468             return; 
25469         }
25470         var lcname = node.tagName.toLowerCase();
25471         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25472         // whitelist of tags..
25473         
25474         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25475             // remove node.
25476             node.parentNode.removeChild(node);
25477             return;
25478             
25479         }
25480         
25481         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25482         
25483         // spans with no attributes - just remove them..
25484         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25485             remove_keep_children = true;
25486         }
25487         
25488         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25489         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25490         
25491         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25492         //    remove_keep_children = true;
25493         //}
25494         
25495         if (remove_keep_children) {
25496             this.cleanUpChildren(node);
25497             // inserts everything just before this node...
25498             while (node.childNodes.length) {
25499                 var cn = node.childNodes[0];
25500                 node.removeChild(cn);
25501                 node.parentNode.insertBefore(cn, node);
25502             }
25503             node.parentNode.removeChild(node);
25504             return;
25505         }
25506         
25507         if (!node.attributes || !node.attributes.length) {
25508             
25509           
25510             
25511             
25512             this.cleanUpChildren(node);
25513             return;
25514         }
25515         
25516         function cleanAttr(n,v)
25517         {
25518             
25519             if (v.match(/^\./) || v.match(/^\//)) {
25520                 return;
25521             }
25522             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25523                 return;
25524             }
25525             if (v.match(/^#/)) {
25526                 return;
25527             }
25528             if (v.match(/^\{/)) { // allow template editing.
25529                 return;
25530             }
25531 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25532             node.removeAttribute(n);
25533             
25534         }
25535         
25536         var cwhite = this.cwhite;
25537         var cblack = this.cblack;
25538             
25539         function cleanStyle(n,v)
25540         {
25541             if (v.match(/expression/)) { //XSS?? should we even bother..
25542                 node.removeAttribute(n);
25543                 return;
25544             }
25545             
25546             var parts = v.split(/;/);
25547             var clean = [];
25548             
25549             Roo.each(parts, function(p) {
25550                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25551                 if (!p.length) {
25552                     return true;
25553                 }
25554                 var l = p.split(':').shift().replace(/\s+/g,'');
25555                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25556                 
25557                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25558 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25559                     //node.removeAttribute(n);
25560                     return true;
25561                 }
25562                 //Roo.log()
25563                 // only allow 'c whitelisted system attributes'
25564                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25565 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25566                     //node.removeAttribute(n);
25567                     return true;
25568                 }
25569                 
25570                 
25571                  
25572                 
25573                 clean.push(p);
25574                 return true;
25575             });
25576             if (clean.length) { 
25577                 node.setAttribute(n, clean.join(';'));
25578             } else {
25579                 node.removeAttribute(n);
25580             }
25581             
25582         }
25583         
25584         
25585         for (var i = node.attributes.length-1; i > -1 ; i--) {
25586             var a = node.attributes[i];
25587             //console.log(a);
25588             
25589             if (a.name.toLowerCase().substr(0,2)=='on')  {
25590                 node.removeAttribute(a.name);
25591                 continue;
25592             }
25593             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25594                 node.removeAttribute(a.name);
25595                 continue;
25596             }
25597             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25598                 cleanAttr(a.name,a.value); // fixme..
25599                 continue;
25600             }
25601             if (a.name == 'style') {
25602                 cleanStyle(a.name,a.value);
25603                 continue;
25604             }
25605             /// clean up MS crap..
25606             // tecnically this should be a list of valid class'es..
25607             
25608             
25609             if (a.name == 'class') {
25610                 if (a.value.match(/^Mso/)) {
25611                     node.removeAttribute('class');
25612                 }
25613                 
25614                 if (a.value.match(/^body$/)) {
25615                     node.removeAttribute('class');
25616                 }
25617                 continue;
25618             }
25619             
25620             // style cleanup!?
25621             // class cleanup?
25622             
25623         }
25624         
25625         
25626         this.cleanUpChildren(node);
25627         
25628         
25629     },
25630     
25631     /**
25632      * Clean up MS wordisms...
25633      */
25634     cleanWord : function(node)
25635     {
25636         if (!node) {
25637             this.cleanWord(this.doc.body);
25638             return;
25639         }
25640         
25641         if(
25642                 node.nodeName == 'SPAN' &&
25643                 !node.hasAttributes() &&
25644                 node.childNodes.length == 1 &&
25645                 node.firstChild.nodeName == "#text"  
25646         ) {
25647             var textNode = node.firstChild;
25648             node.removeChild(textNode);
25649             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25650                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25651             }
25652             node.parentNode.insertBefore(textNode, node);
25653             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25654                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25655             }
25656             node.parentNode.removeChild(node);
25657         }
25658         
25659         if (node.nodeName == "#text") {
25660             // clean up silly Windows -- stuff?
25661             return; 
25662         }
25663         if (node.nodeName == "#comment") {
25664             node.parentNode.removeChild(node);
25665             // clean up silly Windows -- stuff?
25666             return; 
25667         }
25668         
25669         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25670             node.parentNode.removeChild(node);
25671             return;
25672         }
25673         //Roo.log(node.tagName);
25674         // remove - but keep children..
25675         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25676             //Roo.log('-- removed');
25677             while (node.childNodes.length) {
25678                 var cn = node.childNodes[0];
25679                 node.removeChild(cn);
25680                 node.parentNode.insertBefore(cn, node);
25681                 // move node to parent - and clean it..
25682                 this.cleanWord(cn);
25683             }
25684             node.parentNode.removeChild(node);
25685             /// no need to iterate chidlren = it's got none..
25686             //this.iterateChildren(node, this.cleanWord);
25687             return;
25688         }
25689         // clean styles
25690         if (node.className.length) {
25691             
25692             var cn = node.className.split(/\W+/);
25693             var cna = [];
25694             Roo.each(cn, function(cls) {
25695                 if (cls.match(/Mso[a-zA-Z]+/)) {
25696                     return;
25697                 }
25698                 cna.push(cls);
25699             });
25700             node.className = cna.length ? cna.join(' ') : '';
25701             if (!cna.length) {
25702                 node.removeAttribute("class");
25703             }
25704         }
25705         
25706         if (node.hasAttribute("lang")) {
25707             node.removeAttribute("lang");
25708         }
25709         
25710         if (node.hasAttribute("style")) {
25711             
25712             var styles = node.getAttribute("style").split(";");
25713             var nstyle = [];
25714             Roo.each(styles, function(s) {
25715                 if (!s.match(/:/)) {
25716                     return;
25717                 }
25718                 var kv = s.split(":");
25719                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25720                     return;
25721                 }
25722                 // what ever is left... we allow.
25723                 nstyle.push(s);
25724             });
25725             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25726             if (!nstyle.length) {
25727                 node.removeAttribute('style');
25728             }
25729         }
25730         this.iterateChildren(node, this.cleanWord);
25731         
25732         
25733         
25734     },
25735     /**
25736      * iterateChildren of a Node, calling fn each time, using this as the scole..
25737      * @param {DomNode} node node to iterate children of.
25738      * @param {Function} fn method of this class to call on each item.
25739      */
25740     iterateChildren : function(node, fn)
25741     {
25742         if (!node.childNodes.length) {
25743                 return;
25744         }
25745         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25746            fn.call(this, node.childNodes[i])
25747         }
25748     },
25749     
25750     
25751     /**
25752      * cleanTableWidths.
25753      *
25754      * Quite often pasting from word etc.. results in tables with column and widths.
25755      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25756      *
25757      */
25758     cleanTableWidths : function(node)
25759     {
25760          
25761          
25762         if (!node) {
25763             this.cleanTableWidths(this.doc.body);
25764             return;
25765         }
25766         
25767         // ignore list...
25768         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25769             return; 
25770         }
25771         Roo.log(node.tagName);
25772         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25773             this.iterateChildren(node, this.cleanTableWidths);
25774             return;
25775         }
25776         if (node.hasAttribute('width')) {
25777             node.removeAttribute('width');
25778         }
25779         
25780          
25781         if (node.hasAttribute("style")) {
25782             // pretty basic...
25783             
25784             var styles = node.getAttribute("style").split(";");
25785             var nstyle = [];
25786             Roo.each(styles, function(s) {
25787                 if (!s.match(/:/)) {
25788                     return;
25789                 }
25790                 var kv = s.split(":");
25791                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25792                     return;
25793                 }
25794                 // what ever is left... we allow.
25795                 nstyle.push(s);
25796             });
25797             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25798             if (!nstyle.length) {
25799                 node.removeAttribute('style');
25800             }
25801         }
25802         
25803         this.iterateChildren(node, this.cleanTableWidths);
25804         
25805         
25806     },
25807     
25808     
25809     
25810     
25811     domToHTML : function(currentElement, depth, nopadtext) {
25812         
25813         depth = depth || 0;
25814         nopadtext = nopadtext || false;
25815     
25816         if (!currentElement) {
25817             return this.domToHTML(this.doc.body);
25818         }
25819         
25820         //Roo.log(currentElement);
25821         var j;
25822         var allText = false;
25823         var nodeName = currentElement.nodeName;
25824         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25825         
25826         if  (nodeName == '#text') {
25827             
25828             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25829         }
25830         
25831         
25832         var ret = '';
25833         if (nodeName != 'BODY') {
25834              
25835             var i = 0;
25836             // Prints the node tagName, such as <A>, <IMG>, etc
25837             if (tagName) {
25838                 var attr = [];
25839                 for(i = 0; i < currentElement.attributes.length;i++) {
25840                     // quoting?
25841                     var aname = currentElement.attributes.item(i).name;
25842                     if (!currentElement.attributes.item(i).value.length) {
25843                         continue;
25844                     }
25845                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25846                 }
25847                 
25848                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25849             } 
25850             else {
25851                 
25852                 // eack
25853             }
25854         } else {
25855             tagName = false;
25856         }
25857         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25858             return ret;
25859         }
25860         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25861             nopadtext = true;
25862         }
25863         
25864         
25865         // Traverse the tree
25866         i = 0;
25867         var currentElementChild = currentElement.childNodes.item(i);
25868         var allText = true;
25869         var innerHTML  = '';
25870         lastnode = '';
25871         while (currentElementChild) {
25872             // Formatting code (indent the tree so it looks nice on the screen)
25873             var nopad = nopadtext;
25874             if (lastnode == 'SPAN') {
25875                 nopad  = true;
25876             }
25877             // text
25878             if  (currentElementChild.nodeName == '#text') {
25879                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25880                 toadd = nopadtext ? toadd : toadd.trim();
25881                 if (!nopad && toadd.length > 80) {
25882                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25883                 }
25884                 innerHTML  += toadd;
25885                 
25886                 i++;
25887                 currentElementChild = currentElement.childNodes.item(i);
25888                 lastNode = '';
25889                 continue;
25890             }
25891             allText = false;
25892             
25893             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25894                 
25895             // Recursively traverse the tree structure of the child node
25896             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25897             lastnode = currentElementChild.nodeName;
25898             i++;
25899             currentElementChild=currentElement.childNodes.item(i);
25900         }
25901         
25902         ret += innerHTML;
25903         
25904         if (!allText) {
25905                 // The remaining code is mostly for formatting the tree
25906             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25907         }
25908         
25909         
25910         if (tagName) {
25911             ret+= "</"+tagName+">";
25912         }
25913         return ret;
25914         
25915     },
25916         
25917     applyBlacklists : function()
25918     {
25919         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25920         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25921         
25922         this.white = [];
25923         this.black = [];
25924         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25925             if (b.indexOf(tag) > -1) {
25926                 return;
25927             }
25928             this.white.push(tag);
25929             
25930         }, this);
25931         
25932         Roo.each(w, function(tag) {
25933             if (b.indexOf(tag) > -1) {
25934                 return;
25935             }
25936             if (this.white.indexOf(tag) > -1) {
25937                 return;
25938             }
25939             this.white.push(tag);
25940             
25941         }, this);
25942         
25943         
25944         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25945             if (w.indexOf(tag) > -1) {
25946                 return;
25947             }
25948             this.black.push(tag);
25949             
25950         }, this);
25951         
25952         Roo.each(b, function(tag) {
25953             if (w.indexOf(tag) > -1) {
25954                 return;
25955             }
25956             if (this.black.indexOf(tag) > -1) {
25957                 return;
25958             }
25959             this.black.push(tag);
25960             
25961         }, this);
25962         
25963         
25964         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25965         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25966         
25967         this.cwhite = [];
25968         this.cblack = [];
25969         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25970             if (b.indexOf(tag) > -1) {
25971                 return;
25972             }
25973             this.cwhite.push(tag);
25974             
25975         }, this);
25976         
25977         Roo.each(w, function(tag) {
25978             if (b.indexOf(tag) > -1) {
25979                 return;
25980             }
25981             if (this.cwhite.indexOf(tag) > -1) {
25982                 return;
25983             }
25984             this.cwhite.push(tag);
25985             
25986         }, this);
25987         
25988         
25989         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25990             if (w.indexOf(tag) > -1) {
25991                 return;
25992             }
25993             this.cblack.push(tag);
25994             
25995         }, this);
25996         
25997         Roo.each(b, function(tag) {
25998             if (w.indexOf(tag) > -1) {
25999                 return;
26000             }
26001             if (this.cblack.indexOf(tag) > -1) {
26002                 return;
26003             }
26004             this.cblack.push(tag);
26005             
26006         }, this);
26007     },
26008     
26009     setStylesheets : function(stylesheets)
26010     {
26011         if(typeof(stylesheets) == 'string'){
26012             Roo.get(this.iframe.contentDocument.head).createChild({
26013                 tag : 'link',
26014                 rel : 'stylesheet',
26015                 type : 'text/css',
26016                 href : stylesheets
26017             });
26018             
26019             return;
26020         }
26021         var _this = this;
26022      
26023         Roo.each(stylesheets, function(s) {
26024             if(!s.length){
26025                 return;
26026             }
26027             
26028             Roo.get(_this.iframe.contentDocument.head).createChild({
26029                 tag : 'link',
26030                 rel : 'stylesheet',
26031                 type : 'text/css',
26032                 href : s
26033             });
26034         });
26035
26036         
26037     },
26038     
26039     removeStylesheets : function()
26040     {
26041         var _this = this;
26042         
26043         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
26044             s.remove();
26045         });
26046     },
26047     
26048     setStyle : function(style)
26049     {
26050         Roo.get(this.iframe.contentDocument.head).createChild({
26051             tag : 'style',
26052             type : 'text/css',
26053             html : style
26054         });
26055
26056         return;
26057     }
26058     
26059     // hide stuff that is not compatible
26060     /**
26061      * @event blur
26062      * @hide
26063      */
26064     /**
26065      * @event change
26066      * @hide
26067      */
26068     /**
26069      * @event focus
26070      * @hide
26071      */
26072     /**
26073      * @event specialkey
26074      * @hide
26075      */
26076     /**
26077      * @cfg {String} fieldClass @hide
26078      */
26079     /**
26080      * @cfg {String} focusClass @hide
26081      */
26082     /**
26083      * @cfg {String} autoCreate @hide
26084      */
26085     /**
26086      * @cfg {String} inputType @hide
26087      */
26088     /**
26089      * @cfg {String} invalidClass @hide
26090      */
26091     /**
26092      * @cfg {String} invalidText @hide
26093      */
26094     /**
26095      * @cfg {String} msgFx @hide
26096      */
26097     /**
26098      * @cfg {String} validateOnBlur @hide
26099      */
26100 });
26101
26102 Roo.HtmlEditorCore.white = [
26103         'area', 'br', 'img', 'input', 'hr', 'wbr',
26104         
26105        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
26106        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
26107        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
26108        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
26109        'table',   'ul',         'xmp', 
26110        
26111        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
26112       'thead',   'tr', 
26113      
26114       'dir', 'menu', 'ol', 'ul', 'dl',
26115        
26116       'embed',  'object'
26117 ];
26118
26119
26120 Roo.HtmlEditorCore.black = [
26121     //    'embed',  'object', // enable - backend responsiblity to clean thiese
26122         'applet', // 
26123         'base',   'basefont', 'bgsound', 'blink',  'body', 
26124         'frame',  'frameset', 'head',    'html',   'ilayer', 
26125         'iframe', 'layer',  'link',     'meta',    'object',   
26126         'script', 'style' ,'title',  'xml' // clean later..
26127 ];
26128 Roo.HtmlEditorCore.clean = [
26129     'script', 'style', 'title', 'xml'
26130 ];
26131 Roo.HtmlEditorCore.remove = [
26132     'font'
26133 ];
26134 // attributes..
26135
26136 Roo.HtmlEditorCore.ablack = [
26137     'on'
26138 ];
26139     
26140 Roo.HtmlEditorCore.aclean = [ 
26141     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
26142 ];
26143
26144 // protocols..
26145 Roo.HtmlEditorCore.pwhite= [
26146         'http',  'https',  'mailto'
26147 ];
26148
26149 // white listed style attributes.
26150 Roo.HtmlEditorCore.cwhite= [
26151       //  'text-align', /// default is to allow most things..
26152       
26153          
26154 //        'font-size'//??
26155 ];
26156
26157 // black listed style attributes.
26158 Roo.HtmlEditorCore.cblack= [
26159       //  'font-size' -- this can be set by the project 
26160 ];
26161
26162
26163 Roo.HtmlEditorCore.swapCodes   =[ 
26164     [    8211, "&#8211;" ], 
26165     [    8212, "&#8212;" ], 
26166     [    8216,  "'" ],  
26167     [    8217, "'" ],  
26168     [    8220, '"' ],  
26169     [    8221, '"' ],  
26170     [    8226, "*" ],  
26171     [    8230, "..." ]
26172 ]; 
26173
26174     /*
26175  * - LGPL
26176  *
26177  * HtmlEditor
26178  * 
26179  */
26180
26181 /**
26182  * @class Roo.bootstrap.HtmlEditor
26183  * @extends Roo.bootstrap.TextArea
26184  * Bootstrap HtmlEditor class
26185
26186  * @constructor
26187  * Create a new HtmlEditor
26188  * @param {Object} config The config object
26189  */
26190
26191 Roo.bootstrap.HtmlEditor = function(config){
26192     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
26193     if (!this.toolbars) {
26194         this.toolbars = [];
26195     }
26196     
26197     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
26198     this.addEvents({
26199             /**
26200              * @event initialize
26201              * Fires when the editor is fully initialized (including the iframe)
26202              * @param {HtmlEditor} this
26203              */
26204             initialize: true,
26205             /**
26206              * @event activate
26207              * Fires when the editor is first receives the focus. Any insertion must wait
26208              * until after this event.
26209              * @param {HtmlEditor} this
26210              */
26211             activate: true,
26212              /**
26213              * @event beforesync
26214              * Fires before the textarea is updated with content from the editor iframe. Return false
26215              * to cancel the sync.
26216              * @param {HtmlEditor} this
26217              * @param {String} html
26218              */
26219             beforesync: true,
26220              /**
26221              * @event beforepush
26222              * Fires before the iframe editor is updated with content from the textarea. Return false
26223              * to cancel the push.
26224              * @param {HtmlEditor} this
26225              * @param {String} html
26226              */
26227             beforepush: true,
26228              /**
26229              * @event sync
26230              * Fires when the textarea is updated with content from the editor iframe.
26231              * @param {HtmlEditor} this
26232              * @param {String} html
26233              */
26234             sync: true,
26235              /**
26236              * @event push
26237              * Fires when the iframe editor is updated with content from the textarea.
26238              * @param {HtmlEditor} this
26239              * @param {String} html
26240              */
26241             push: true,
26242              /**
26243              * @event editmodechange
26244              * Fires when the editor switches edit modes
26245              * @param {HtmlEditor} this
26246              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
26247              */
26248             editmodechange: true,
26249             /**
26250              * @event editorevent
26251              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
26252              * @param {HtmlEditor} this
26253              */
26254             editorevent: true,
26255             /**
26256              * @event firstfocus
26257              * Fires when on first focus - needed by toolbars..
26258              * @param {HtmlEditor} this
26259              */
26260             firstfocus: true,
26261             /**
26262              * @event autosave
26263              * Auto save the htmlEditor value as a file into Events
26264              * @param {HtmlEditor} this
26265              */
26266             autosave: true,
26267             /**
26268              * @event savedpreview
26269              * preview the saved version of htmlEditor
26270              * @param {HtmlEditor} this
26271              */
26272             savedpreview: true
26273         });
26274 };
26275
26276
26277 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
26278     
26279     
26280       /**
26281      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
26282      */
26283     toolbars : false,
26284     
26285      /**
26286     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
26287     */
26288     btns : [],
26289    
26290      /**
26291      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
26292      *                        Roo.resizable.
26293      */
26294     resizable : false,
26295      /**
26296      * @cfg {Number} height (in pixels)
26297      */   
26298     height: 300,
26299    /**
26300      * @cfg {Number} width (in pixels)
26301      */   
26302     width: false,
26303     
26304     /**
26305      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26306      * 
26307      */
26308     stylesheets: false,
26309     
26310     // id of frame..
26311     frameId: false,
26312     
26313     // private properties
26314     validationEvent : false,
26315     deferHeight: true,
26316     initialized : false,
26317     activated : false,
26318     
26319     onFocus : Roo.emptyFn,
26320     iframePad:3,
26321     hideMode:'offsets',
26322     
26323     tbContainer : false,
26324     
26325     bodyCls : '',
26326     
26327     toolbarContainer :function() {
26328         return this.wrap.select('.x-html-editor-tb',true).first();
26329     },
26330
26331     /**
26332      * Protected method that will not generally be called directly. It
26333      * is called when the editor creates its toolbar. Override this method if you need to
26334      * add custom toolbar buttons.
26335      * @param {HtmlEditor} editor
26336      */
26337     createToolbar : function(){
26338         Roo.log('renewing');
26339         Roo.log("create toolbars");
26340         
26341         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26342         this.toolbars[0].render(this.toolbarContainer());
26343         
26344         return;
26345         
26346 //        if (!editor.toolbars || !editor.toolbars.length) {
26347 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26348 //        }
26349 //        
26350 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26351 //            editor.toolbars[i] = Roo.factory(
26352 //                    typeof(editor.toolbars[i]) == 'string' ?
26353 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26354 //                Roo.bootstrap.HtmlEditor);
26355 //            editor.toolbars[i].init(editor);
26356 //        }
26357     },
26358
26359      
26360     // private
26361     onRender : function(ct, position)
26362     {
26363        // Roo.log("Call onRender: " + this.xtype);
26364         var _t = this;
26365         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26366       
26367         this.wrap = this.inputEl().wrap({
26368             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26369         });
26370         
26371         this.editorcore.onRender(ct, position);
26372          
26373         if (this.resizable) {
26374             this.resizeEl = new Roo.Resizable(this.wrap, {
26375                 pinned : true,
26376                 wrap: true,
26377                 dynamic : true,
26378                 minHeight : this.height,
26379                 height: this.height,
26380                 handles : this.resizable,
26381                 width: this.width,
26382                 listeners : {
26383                     resize : function(r, w, h) {
26384                         _t.onResize(w,h); // -something
26385                     }
26386                 }
26387             });
26388             
26389         }
26390         this.createToolbar(this);
26391        
26392         
26393         if(!this.width && this.resizable){
26394             this.setSize(this.wrap.getSize());
26395         }
26396         if (this.resizeEl) {
26397             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26398             // should trigger onReize..
26399         }
26400         
26401     },
26402
26403     // private
26404     onResize : function(w, h)
26405     {
26406         Roo.log('resize: ' +w + ',' + h );
26407         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26408         var ew = false;
26409         var eh = false;
26410         
26411         if(this.inputEl() ){
26412             if(typeof w == 'number'){
26413                 var aw = w - this.wrap.getFrameWidth('lr');
26414                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26415                 ew = aw;
26416             }
26417             if(typeof h == 'number'){
26418                  var tbh = -11;  // fixme it needs to tool bar size!
26419                 for (var i =0; i < this.toolbars.length;i++) {
26420                     // fixme - ask toolbars for heights?
26421                     tbh += this.toolbars[i].el.getHeight();
26422                     //if (this.toolbars[i].footer) {
26423                     //    tbh += this.toolbars[i].footer.el.getHeight();
26424                     //}
26425                 }
26426               
26427                 
26428                 
26429                 
26430                 
26431                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26432                 ah -= 5; // knock a few pixes off for look..
26433                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26434                 var eh = ah;
26435             }
26436         }
26437         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26438         this.editorcore.onResize(ew,eh);
26439         
26440     },
26441
26442     /**
26443      * Toggles the editor between standard and source edit mode.
26444      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26445      */
26446     toggleSourceEdit : function(sourceEditMode)
26447     {
26448         this.editorcore.toggleSourceEdit(sourceEditMode);
26449         
26450         if(this.editorcore.sourceEditMode){
26451             Roo.log('editor - showing textarea');
26452             
26453 //            Roo.log('in');
26454 //            Roo.log(this.syncValue());
26455             this.syncValue();
26456             this.inputEl().removeClass(['hide', 'x-hidden']);
26457             this.inputEl().dom.removeAttribute('tabIndex');
26458             this.inputEl().focus();
26459         }else{
26460             Roo.log('editor - hiding textarea');
26461 //            Roo.log('out')
26462 //            Roo.log(this.pushValue()); 
26463             this.pushValue();
26464             
26465             this.inputEl().addClass(['hide', 'x-hidden']);
26466             this.inputEl().dom.setAttribute('tabIndex', -1);
26467             //this.deferFocus();
26468         }
26469          
26470         if(this.resizable){
26471             this.setSize(this.wrap.getSize());
26472         }
26473         
26474         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26475     },
26476  
26477     // private (for BoxComponent)
26478     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26479
26480     // private (for BoxComponent)
26481     getResizeEl : function(){
26482         return this.wrap;
26483     },
26484
26485     // private (for BoxComponent)
26486     getPositionEl : function(){
26487         return this.wrap;
26488     },
26489
26490     // private
26491     initEvents : function(){
26492         this.originalValue = this.getValue();
26493     },
26494
26495 //    /**
26496 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26497 //     * @method
26498 //     */
26499 //    markInvalid : Roo.emptyFn,
26500 //    /**
26501 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26502 //     * @method
26503 //     */
26504 //    clearInvalid : Roo.emptyFn,
26505
26506     setValue : function(v){
26507         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26508         this.editorcore.pushValue();
26509     },
26510
26511      
26512     // private
26513     deferFocus : function(){
26514         this.focus.defer(10, this);
26515     },
26516
26517     // doc'ed in Field
26518     focus : function(){
26519         this.editorcore.focus();
26520         
26521     },
26522       
26523
26524     // private
26525     onDestroy : function(){
26526         
26527         
26528         
26529         if(this.rendered){
26530             
26531             for (var i =0; i < this.toolbars.length;i++) {
26532                 // fixme - ask toolbars for heights?
26533                 this.toolbars[i].onDestroy();
26534             }
26535             
26536             this.wrap.dom.innerHTML = '';
26537             this.wrap.remove();
26538         }
26539     },
26540
26541     // private
26542     onFirstFocus : function(){
26543         //Roo.log("onFirstFocus");
26544         this.editorcore.onFirstFocus();
26545          for (var i =0; i < this.toolbars.length;i++) {
26546             this.toolbars[i].onFirstFocus();
26547         }
26548         
26549     },
26550     
26551     // private
26552     syncValue : function()
26553     {   
26554         this.editorcore.syncValue();
26555     },
26556     
26557     pushValue : function()
26558     {   
26559         this.editorcore.pushValue();
26560     }
26561      
26562     
26563     // hide stuff that is not compatible
26564     /**
26565      * @event blur
26566      * @hide
26567      */
26568     /**
26569      * @event change
26570      * @hide
26571      */
26572     /**
26573      * @event focus
26574      * @hide
26575      */
26576     /**
26577      * @event specialkey
26578      * @hide
26579      */
26580     /**
26581      * @cfg {String} fieldClass @hide
26582      */
26583     /**
26584      * @cfg {String} focusClass @hide
26585      */
26586     /**
26587      * @cfg {String} autoCreate @hide
26588      */
26589     /**
26590      * @cfg {String} inputType @hide
26591      */
26592      
26593     /**
26594      * @cfg {String} invalidText @hide
26595      */
26596     /**
26597      * @cfg {String} msgFx @hide
26598      */
26599     /**
26600      * @cfg {String} validateOnBlur @hide
26601      */
26602 });
26603  
26604     
26605    
26606    
26607    
26608       
26609 Roo.namespace('Roo.bootstrap.htmleditor');
26610 /**
26611  * @class Roo.bootstrap.HtmlEditorToolbar1
26612  * Basic Toolbar
26613  * 
26614  * @example
26615  * Usage:
26616  *
26617  new Roo.bootstrap.HtmlEditor({
26618     ....
26619     toolbars : [
26620         new Roo.bootstrap.HtmlEditorToolbar1({
26621             disable : { fonts: 1 , format: 1, ..., ... , ...],
26622             btns : [ .... ]
26623         })
26624     }
26625      
26626  * 
26627  * @cfg {Object} disable List of elements to disable..
26628  * @cfg {Array} btns List of additional buttons.
26629  * 
26630  * 
26631  * NEEDS Extra CSS? 
26632  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26633  */
26634  
26635 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26636 {
26637     
26638     Roo.apply(this, config);
26639     
26640     // default disabled, based on 'good practice'..
26641     this.disable = this.disable || {};
26642     Roo.applyIf(this.disable, {
26643         fontSize : true,
26644         colors : true,
26645         specialElements : true
26646     });
26647     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26648     
26649     this.editor = config.editor;
26650     this.editorcore = config.editor.editorcore;
26651     
26652     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26653     
26654     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26655     // dont call parent... till later.
26656 }
26657 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26658      
26659     bar : true,
26660     
26661     editor : false,
26662     editorcore : false,
26663     
26664     
26665     formats : [
26666         "p" ,  
26667         "h1","h2","h3","h4","h5","h6", 
26668         "pre", "code", 
26669         "abbr", "acronym", "address", "cite", "samp", "var",
26670         'div','span'
26671     ],
26672     
26673     onRender : function(ct, position)
26674     {
26675        // Roo.log("Call onRender: " + this.xtype);
26676         
26677        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26678        Roo.log(this.el);
26679        this.el.dom.style.marginBottom = '0';
26680        var _this = this;
26681        var editorcore = this.editorcore;
26682        var editor= this.editor;
26683        
26684        var children = [];
26685        var btn = function(id,cmd , toggle, handler, html){
26686        
26687             var  event = toggle ? 'toggle' : 'click';
26688        
26689             var a = {
26690                 size : 'sm',
26691                 xtype: 'Button',
26692                 xns: Roo.bootstrap,
26693                 //glyphicon : id,
26694                 fa: id,
26695                 cmd : id || cmd,
26696                 enableToggle:toggle !== false,
26697                 html : html || '',
26698                 pressed : toggle ? false : null,
26699                 listeners : {}
26700             };
26701             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26702                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26703             };
26704             children.push(a);
26705             return a;
26706        }
26707        
26708     //    var cb_box = function...
26709         
26710         var style = {
26711                 xtype: 'Button',
26712                 size : 'sm',
26713                 xns: Roo.bootstrap,
26714                 fa : 'font',
26715                 //html : 'submit'
26716                 menu : {
26717                     xtype: 'Menu',
26718                     xns: Roo.bootstrap,
26719                     items:  []
26720                 }
26721         };
26722         Roo.each(this.formats, function(f) {
26723             style.menu.items.push({
26724                 xtype :'MenuItem',
26725                 xns: Roo.bootstrap,
26726                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26727                 tagname : f,
26728                 listeners : {
26729                     click : function()
26730                     {
26731                         editorcore.insertTag(this.tagname);
26732                         editor.focus();
26733                     }
26734                 }
26735                 
26736             });
26737         });
26738         children.push(style);   
26739         
26740         btn('bold',false,true);
26741         btn('italic',false,true);
26742         btn('align-left', 'justifyleft',true);
26743         btn('align-center', 'justifycenter',true);
26744         btn('align-right' , 'justifyright',true);
26745         btn('link', false, false, function(btn) {
26746             //Roo.log("create link?");
26747             var url = prompt(this.createLinkText, this.defaultLinkValue);
26748             if(url && url != 'http:/'+'/'){
26749                 this.editorcore.relayCmd('createlink', url);
26750             }
26751         }),
26752         btn('list','insertunorderedlist',true);
26753         btn('pencil', false,true, function(btn){
26754                 Roo.log(this);
26755                 this.toggleSourceEdit(btn.pressed);
26756         });
26757         
26758         if (this.editor.btns.length > 0) {
26759             for (var i = 0; i<this.editor.btns.length; i++) {
26760                 children.push(this.editor.btns[i]);
26761             }
26762         }
26763         
26764         /*
26765         var cog = {
26766                 xtype: 'Button',
26767                 size : 'sm',
26768                 xns: Roo.bootstrap,
26769                 glyphicon : 'cog',
26770                 //html : 'submit'
26771                 menu : {
26772                     xtype: 'Menu',
26773                     xns: Roo.bootstrap,
26774                     items:  []
26775                 }
26776         };
26777         
26778         cog.menu.items.push({
26779             xtype :'MenuItem',
26780             xns: Roo.bootstrap,
26781             html : Clean styles,
26782             tagname : f,
26783             listeners : {
26784                 click : function()
26785                 {
26786                     editorcore.insertTag(this.tagname);
26787                     editor.focus();
26788                 }
26789             }
26790             
26791         });
26792        */
26793         
26794          
26795        this.xtype = 'NavSimplebar';
26796         
26797         for(var i=0;i< children.length;i++) {
26798             
26799             this.buttons.add(this.addxtypeChild(children[i]));
26800             
26801         }
26802         
26803         editor.on('editorevent', this.updateToolbar, this);
26804     },
26805     onBtnClick : function(id)
26806     {
26807        this.editorcore.relayCmd(id);
26808        this.editorcore.focus();
26809     },
26810     
26811     /**
26812      * Protected method that will not generally be called directly. It triggers
26813      * a toolbar update by reading the markup state of the current selection in the editor.
26814      */
26815     updateToolbar: function(){
26816
26817         if(!this.editorcore.activated){
26818             this.editor.onFirstFocus(); // is this neeed?
26819             return;
26820         }
26821
26822         var btns = this.buttons; 
26823         var doc = this.editorcore.doc;
26824         btns.get('bold').setActive(doc.queryCommandState('bold'));
26825         btns.get('italic').setActive(doc.queryCommandState('italic'));
26826         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26827         
26828         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26829         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26830         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26831         
26832         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26833         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26834          /*
26835         
26836         var ans = this.editorcore.getAllAncestors();
26837         if (this.formatCombo) {
26838             
26839             
26840             var store = this.formatCombo.store;
26841             this.formatCombo.setValue("");
26842             for (var i =0; i < ans.length;i++) {
26843                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26844                     // select it..
26845                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26846                     break;
26847                 }
26848             }
26849         }
26850         
26851         
26852         
26853         // hides menus... - so this cant be on a menu...
26854         Roo.bootstrap.MenuMgr.hideAll();
26855         */
26856         Roo.bootstrap.MenuMgr.hideAll();
26857         //this.editorsyncValue();
26858     },
26859     onFirstFocus: function() {
26860         this.buttons.each(function(item){
26861            item.enable();
26862         });
26863     },
26864     toggleSourceEdit : function(sourceEditMode){
26865         
26866           
26867         if(sourceEditMode){
26868             Roo.log("disabling buttons");
26869            this.buttons.each( function(item){
26870                 if(item.cmd != 'pencil'){
26871                     item.disable();
26872                 }
26873             });
26874           
26875         }else{
26876             Roo.log("enabling buttons");
26877             if(this.editorcore.initialized){
26878                 this.buttons.each( function(item){
26879                     item.enable();
26880                 });
26881             }
26882             
26883         }
26884         Roo.log("calling toggole on editor");
26885         // tell the editor that it's been pressed..
26886         this.editor.toggleSourceEdit(sourceEditMode);
26887        
26888     }
26889 });
26890
26891
26892
26893
26894  
26895 /*
26896  * - LGPL
26897  */
26898
26899 /**
26900  * @class Roo.bootstrap.Markdown
26901  * @extends Roo.bootstrap.TextArea
26902  * Bootstrap Showdown editable area
26903  * @cfg {string} content
26904  * 
26905  * @constructor
26906  * Create a new Showdown
26907  */
26908
26909 Roo.bootstrap.Markdown = function(config){
26910     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26911    
26912 };
26913
26914 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26915     
26916     editing :false,
26917     
26918     initEvents : function()
26919     {
26920         
26921         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26922         this.markdownEl = this.el.createChild({
26923             cls : 'roo-markdown-area'
26924         });
26925         this.inputEl().addClass('d-none');
26926         if (this.getValue() == '') {
26927             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26928             
26929         } else {
26930             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26931         }
26932         this.markdownEl.on('click', this.toggleTextEdit, this);
26933         this.on('blur', this.toggleTextEdit, this);
26934         this.on('specialkey', this.resizeTextArea, this);
26935     },
26936     
26937     toggleTextEdit : function()
26938     {
26939         var sh = this.markdownEl.getHeight();
26940         this.inputEl().addClass('d-none');
26941         this.markdownEl.addClass('d-none');
26942         if (!this.editing) {
26943             // show editor?
26944             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26945             this.inputEl().removeClass('d-none');
26946             this.inputEl().focus();
26947             this.editing = true;
26948             return;
26949         }
26950         // show showdown...
26951         this.updateMarkdown();
26952         this.markdownEl.removeClass('d-none');
26953         this.editing = false;
26954         return;
26955     },
26956     updateMarkdown : function()
26957     {
26958         if (this.getValue() == '') {
26959             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26960             return;
26961         }
26962  
26963         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26964     },
26965     
26966     resizeTextArea: function () {
26967         
26968         var sh = 100;
26969         Roo.log([sh, this.getValue().split("\n").length * 30]);
26970         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26971     },
26972     setValue : function(val)
26973     {
26974         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26975         if (!this.editing) {
26976             this.updateMarkdown();
26977         }
26978         
26979     },
26980     focus : function()
26981     {
26982         if (!this.editing) {
26983             this.toggleTextEdit();
26984         }
26985         
26986     }
26987
26988
26989 });
26990 /**
26991  * @class Roo.bootstrap.Table.AbstractSelectionModel
26992  * @extends Roo.util.Observable
26993  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26994  * implemented by descendant classes.  This class should not be directly instantiated.
26995  * @constructor
26996  */
26997 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26998     this.locked = false;
26999     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
27000 };
27001
27002
27003 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
27004     /** @ignore Called by the grid automatically. Do not call directly. */
27005     init : function(grid){
27006         this.grid = grid;
27007         this.initEvents();
27008     },
27009
27010     /**
27011      * Locks the selections.
27012      */
27013     lock : function(){
27014         this.locked = true;
27015     },
27016
27017     /**
27018      * Unlocks the selections.
27019      */
27020     unlock : function(){
27021         this.locked = false;
27022     },
27023
27024     /**
27025      * Returns true if the selections are locked.
27026      * @return {Boolean}
27027      */
27028     isLocked : function(){
27029         return this.locked;
27030     },
27031     
27032     
27033     initEvents : function ()
27034     {
27035         
27036     }
27037 });
27038 /**
27039  * @extends Roo.bootstrap.Table.AbstractSelectionModel
27040  * @class Roo.bootstrap.Table.RowSelectionModel
27041  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
27042  * It supports multiple selections and keyboard selection/navigation. 
27043  * @constructor
27044  * @param {Object} config
27045  */
27046
27047 Roo.bootstrap.Table.RowSelectionModel = function(config){
27048     Roo.apply(this, config);
27049     this.selections = new Roo.util.MixedCollection(false, function(o){
27050         return o.id;
27051     });
27052
27053     this.last = false;
27054     this.lastActive = false;
27055
27056     this.addEvents({
27057         /**
27058              * @event selectionchange
27059              * Fires when the selection changes
27060              * @param {SelectionModel} this
27061              */
27062             "selectionchange" : true,
27063         /**
27064              * @event afterselectionchange
27065              * Fires after the selection changes (eg. by key press or clicking)
27066              * @param {SelectionModel} this
27067              */
27068             "afterselectionchange" : true,
27069         /**
27070              * @event beforerowselect
27071              * Fires when a row is selected being selected, return false to cancel.
27072              * @param {SelectionModel} this
27073              * @param {Number} rowIndex The selected index
27074              * @param {Boolean} keepExisting False if other selections will be cleared
27075              */
27076             "beforerowselect" : true,
27077         /**
27078              * @event rowselect
27079              * Fires when a row is selected.
27080              * @param {SelectionModel} this
27081              * @param {Number} rowIndex The selected index
27082              * @param {Roo.data.Record} r The record
27083              */
27084             "rowselect" : true,
27085         /**
27086              * @event rowdeselect
27087              * Fires when a row is deselected.
27088              * @param {SelectionModel} this
27089              * @param {Number} rowIndex The selected index
27090              */
27091         "rowdeselect" : true
27092     });
27093     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
27094     this.locked = false;
27095  };
27096
27097 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
27098     /**
27099      * @cfg {Boolean} singleSelect
27100      * True to allow selection of only one row at a time (defaults to false)
27101      */
27102     singleSelect : false,
27103
27104     // private
27105     initEvents : function()
27106     {
27107
27108         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
27109         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
27110         //}else{ // allow click to work like normal
27111          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
27112         //}
27113         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
27114         this.grid.on("rowclick", this.handleMouseDown, this);
27115         
27116         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
27117             "up" : function(e){
27118                 if(!e.shiftKey){
27119                     this.selectPrevious(e.shiftKey);
27120                 }else if(this.last !== false && this.lastActive !== false){
27121                     var last = this.last;
27122                     this.selectRange(this.last,  this.lastActive-1);
27123                     this.grid.getView().focusRow(this.lastActive);
27124                     if(last !== false){
27125                         this.last = last;
27126                     }
27127                 }else{
27128                     this.selectFirstRow();
27129                 }
27130                 this.fireEvent("afterselectionchange", this);
27131             },
27132             "down" : function(e){
27133                 if(!e.shiftKey){
27134                     this.selectNext(e.shiftKey);
27135                 }else if(this.last !== false && this.lastActive !== false){
27136                     var last = this.last;
27137                     this.selectRange(this.last,  this.lastActive+1);
27138                     this.grid.getView().focusRow(this.lastActive);
27139                     if(last !== false){
27140                         this.last = last;
27141                     }
27142                 }else{
27143                     this.selectFirstRow();
27144                 }
27145                 this.fireEvent("afterselectionchange", this);
27146             },
27147             scope: this
27148         });
27149         this.grid.store.on('load', function(){
27150             this.selections.clear();
27151         },this);
27152         /*
27153         var view = this.grid.view;
27154         view.on("refresh", this.onRefresh, this);
27155         view.on("rowupdated", this.onRowUpdated, this);
27156         view.on("rowremoved", this.onRemove, this);
27157         */
27158     },
27159
27160     // private
27161     onRefresh : function()
27162     {
27163         var ds = this.grid.store, i, v = this.grid.view;
27164         var s = this.selections;
27165         s.each(function(r){
27166             if((i = ds.indexOfId(r.id)) != -1){
27167                 v.onRowSelect(i);
27168             }else{
27169                 s.remove(r);
27170             }
27171         });
27172     },
27173
27174     // private
27175     onRemove : function(v, index, r){
27176         this.selections.remove(r);
27177     },
27178
27179     // private
27180     onRowUpdated : function(v, index, r){
27181         if(this.isSelected(r)){
27182             v.onRowSelect(index);
27183         }
27184     },
27185
27186     /**
27187      * Select records.
27188      * @param {Array} records The records to select
27189      * @param {Boolean} keepExisting (optional) True to keep existing selections
27190      */
27191     selectRecords : function(records, keepExisting)
27192     {
27193         if(!keepExisting){
27194             this.clearSelections();
27195         }
27196             var ds = this.grid.store;
27197         for(var i = 0, len = records.length; i < len; i++){
27198             this.selectRow(ds.indexOf(records[i]), true);
27199         }
27200     },
27201
27202     /**
27203      * Gets the number of selected rows.
27204      * @return {Number}
27205      */
27206     getCount : function(){
27207         return this.selections.length;
27208     },
27209
27210     /**
27211      * Selects the first row in the grid.
27212      */
27213     selectFirstRow : function(){
27214         this.selectRow(0);
27215     },
27216
27217     /**
27218      * Select the last row.
27219      * @param {Boolean} keepExisting (optional) True to keep existing selections
27220      */
27221     selectLastRow : function(keepExisting){
27222         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
27223         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
27224     },
27225
27226     /**
27227      * Selects the row immediately following the last selected row.
27228      * @param {Boolean} keepExisting (optional) True to keep existing selections
27229      */
27230     selectNext : function(keepExisting)
27231     {
27232             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
27233             this.selectRow(this.last+1, keepExisting);
27234             this.grid.getView().focusRow(this.last);
27235         }
27236     },
27237
27238     /**
27239      * Selects the row that precedes the last selected row.
27240      * @param {Boolean} keepExisting (optional) True to keep existing selections
27241      */
27242     selectPrevious : function(keepExisting){
27243         if(this.last){
27244             this.selectRow(this.last-1, keepExisting);
27245             this.grid.getView().focusRow(this.last);
27246         }
27247     },
27248
27249     /**
27250      * Returns the selected records
27251      * @return {Array} Array of selected records
27252      */
27253     getSelections : function(){
27254         return [].concat(this.selections.items);
27255     },
27256
27257     /**
27258      * Returns the first selected record.
27259      * @return {Record}
27260      */
27261     getSelected : function(){
27262         return this.selections.itemAt(0);
27263     },
27264
27265
27266     /**
27267      * Clears all selections.
27268      */
27269     clearSelections : function(fast)
27270     {
27271         if(this.locked) {
27272             return;
27273         }
27274         if(fast !== true){
27275                 var ds = this.grid.store;
27276             var s = this.selections;
27277             s.each(function(r){
27278                 this.deselectRow(ds.indexOfId(r.id));
27279             }, this);
27280             s.clear();
27281         }else{
27282             this.selections.clear();
27283         }
27284         this.last = false;
27285     },
27286
27287
27288     /**
27289      * Selects all rows.
27290      */
27291     selectAll : function(){
27292         if(this.locked) {
27293             return;
27294         }
27295         this.selections.clear();
27296         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
27297             this.selectRow(i, true);
27298         }
27299     },
27300
27301     /**
27302      * Returns True if there is a selection.
27303      * @return {Boolean}
27304      */
27305     hasSelection : function(){
27306         return this.selections.length > 0;
27307     },
27308
27309     /**
27310      * Returns True if the specified row is selected.
27311      * @param {Number/Record} record The record or index of the record to check
27312      * @return {Boolean}
27313      */
27314     isSelected : function(index){
27315             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27316         return (r && this.selections.key(r.id) ? true : false);
27317     },
27318
27319     /**
27320      * Returns True if the specified record id is selected.
27321      * @param {String} id The id of record to check
27322      * @return {Boolean}
27323      */
27324     isIdSelected : function(id){
27325         return (this.selections.key(id) ? true : false);
27326     },
27327
27328
27329     // private
27330     handleMouseDBClick : function(e, t){
27331         
27332     },
27333     // private
27334     handleMouseDown : function(e, t)
27335     {
27336             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27337         if(this.isLocked() || rowIndex < 0 ){
27338             return;
27339         };
27340         if(e.shiftKey && this.last !== false){
27341             var last = this.last;
27342             this.selectRange(last, rowIndex, e.ctrlKey);
27343             this.last = last; // reset the last
27344             t.focus();
27345     
27346         }else{
27347             var isSelected = this.isSelected(rowIndex);
27348             //Roo.log("select row:" + rowIndex);
27349             if(isSelected){
27350                 this.deselectRow(rowIndex);
27351             } else {
27352                         this.selectRow(rowIndex, true);
27353             }
27354     
27355             /*
27356                 if(e.button !== 0 && isSelected){
27357                 alert('rowIndex 2: ' + rowIndex);
27358                     view.focusRow(rowIndex);
27359                 }else if(e.ctrlKey && isSelected){
27360                     this.deselectRow(rowIndex);
27361                 }else if(!isSelected){
27362                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27363                     view.focusRow(rowIndex);
27364                 }
27365             */
27366         }
27367         this.fireEvent("afterselectionchange", this);
27368     },
27369     // private
27370     handleDragableRowClick :  function(grid, rowIndex, e) 
27371     {
27372         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27373             this.selectRow(rowIndex, false);
27374             grid.view.focusRow(rowIndex);
27375              this.fireEvent("afterselectionchange", this);
27376         }
27377     },
27378     
27379     /**
27380      * Selects multiple rows.
27381      * @param {Array} rows Array of the indexes of the row to select
27382      * @param {Boolean} keepExisting (optional) True to keep existing selections
27383      */
27384     selectRows : function(rows, keepExisting){
27385         if(!keepExisting){
27386             this.clearSelections();
27387         }
27388         for(var i = 0, len = rows.length; i < len; i++){
27389             this.selectRow(rows[i], true);
27390         }
27391     },
27392
27393     /**
27394      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27395      * @param {Number} startRow The index of the first row in the range
27396      * @param {Number} endRow The index of the last row in the range
27397      * @param {Boolean} keepExisting (optional) True to retain existing selections
27398      */
27399     selectRange : function(startRow, endRow, keepExisting){
27400         if(this.locked) {
27401             return;
27402         }
27403         if(!keepExisting){
27404             this.clearSelections();
27405         }
27406         if(startRow <= endRow){
27407             for(var i = startRow; i <= endRow; i++){
27408                 this.selectRow(i, true);
27409             }
27410         }else{
27411             for(var i = startRow; i >= endRow; i--){
27412                 this.selectRow(i, true);
27413             }
27414         }
27415     },
27416
27417     /**
27418      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27419      * @param {Number} startRow The index of the first row in the range
27420      * @param {Number} endRow The index of the last row in the range
27421      */
27422     deselectRange : function(startRow, endRow, preventViewNotify){
27423         if(this.locked) {
27424             return;
27425         }
27426         for(var i = startRow; i <= endRow; i++){
27427             this.deselectRow(i, preventViewNotify);
27428         }
27429     },
27430
27431     /**
27432      * Selects a row.
27433      * @param {Number} row The index of the row to select
27434      * @param {Boolean} keepExisting (optional) True to keep existing selections
27435      */
27436     selectRow : function(index, keepExisting, preventViewNotify)
27437     {
27438             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27439             return;
27440         }
27441         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27442             if(!keepExisting || this.singleSelect){
27443                 this.clearSelections();
27444             }
27445             
27446             var r = this.grid.store.getAt(index);
27447             //console.log('selectRow - record id :' + r.id);
27448             
27449             this.selections.add(r);
27450             this.last = this.lastActive = index;
27451             if(!preventViewNotify){
27452                 var proxy = new Roo.Element(
27453                                 this.grid.getRowDom(index)
27454                 );
27455                 proxy.addClass('bg-info info');
27456             }
27457             this.fireEvent("rowselect", this, index, r);
27458             this.fireEvent("selectionchange", this);
27459         }
27460     },
27461
27462     /**
27463      * Deselects a row.
27464      * @param {Number} row The index of the row to deselect
27465      */
27466     deselectRow : function(index, preventViewNotify)
27467     {
27468         if(this.locked) {
27469             return;
27470         }
27471         if(this.last == index){
27472             this.last = false;
27473         }
27474         if(this.lastActive == index){
27475             this.lastActive = false;
27476         }
27477         
27478         var r = this.grid.store.getAt(index);
27479         if (!r) {
27480             return;
27481         }
27482         
27483         this.selections.remove(r);
27484         //.console.log('deselectRow - record id :' + r.id);
27485         if(!preventViewNotify){
27486         
27487             var proxy = new Roo.Element(
27488                 this.grid.getRowDom(index)
27489             );
27490             proxy.removeClass('bg-info info');
27491         }
27492         this.fireEvent("rowdeselect", this, index);
27493         this.fireEvent("selectionchange", this);
27494     },
27495
27496     // private
27497     restoreLast : function(){
27498         if(this._last){
27499             this.last = this._last;
27500         }
27501     },
27502
27503     // private
27504     acceptsNav : function(row, col, cm){
27505         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27506     },
27507
27508     // private
27509     onEditorKey : function(field, e){
27510         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27511         if(k == e.TAB){
27512             e.stopEvent();
27513             ed.completeEdit();
27514             if(e.shiftKey){
27515                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27516             }else{
27517                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27518             }
27519         }else if(k == e.ENTER && !e.ctrlKey){
27520             e.stopEvent();
27521             ed.completeEdit();
27522             if(e.shiftKey){
27523                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27524             }else{
27525                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27526             }
27527         }else if(k == e.ESC){
27528             ed.cancelEdit();
27529         }
27530         if(newCell){
27531             g.startEditing(newCell[0], newCell[1]);
27532         }
27533     }
27534 });
27535 /*
27536  * Based on:
27537  * Ext JS Library 1.1.1
27538  * Copyright(c) 2006-2007, Ext JS, LLC.
27539  *
27540  * Originally Released Under LGPL - original licence link has changed is not relivant.
27541  *
27542  * Fork - LGPL
27543  * <script type="text/javascript">
27544  */
27545  
27546 /**
27547  * @class Roo.bootstrap.PagingToolbar
27548  * @extends Roo.bootstrap.NavSimplebar
27549  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27550  * @constructor
27551  * Create a new PagingToolbar
27552  * @param {Object} config The config object
27553  * @param {Roo.data.Store} store
27554  */
27555 Roo.bootstrap.PagingToolbar = function(config)
27556 {
27557     // old args format still supported... - xtype is prefered..
27558         // created from xtype...
27559     
27560     this.ds = config.dataSource;
27561     
27562     if (config.store && !this.ds) {
27563         this.store= Roo.factory(config.store, Roo.data);
27564         this.ds = this.store;
27565         this.ds.xmodule = this.xmodule || false;
27566     }
27567     
27568     this.toolbarItems = [];
27569     if (config.items) {
27570         this.toolbarItems = config.items;
27571     }
27572     
27573     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27574     
27575     this.cursor = 0;
27576     
27577     if (this.ds) { 
27578         this.bind(this.ds);
27579     }
27580     
27581     if (Roo.bootstrap.version == 4) {
27582         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27583     } else {
27584         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27585     }
27586     
27587 };
27588
27589 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27590     /**
27591      * @cfg {Roo.data.Store} dataSource
27592      * The underlying data store providing the paged data
27593      */
27594     /**
27595      * @cfg {String/HTMLElement/Element} container
27596      * container The id or element that will contain the toolbar
27597      */
27598     /**
27599      * @cfg {Boolean} displayInfo
27600      * True to display the displayMsg (defaults to false)
27601      */
27602     /**
27603      * @cfg {Number} pageSize
27604      * The number of records to display per page (defaults to 20)
27605      */
27606     pageSize: 20,
27607     /**
27608      * @cfg {String} displayMsg
27609      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27610      */
27611     displayMsg : 'Displaying {0} - {1} of {2}',
27612     /**
27613      * @cfg {String} emptyMsg
27614      * The message to display when no records are found (defaults to "No data to display")
27615      */
27616     emptyMsg : 'No data to display',
27617     /**
27618      * Customizable piece of the default paging text (defaults to "Page")
27619      * @type String
27620      */
27621     beforePageText : "Page",
27622     /**
27623      * Customizable piece of the default paging text (defaults to "of %0")
27624      * @type String
27625      */
27626     afterPageText : "of {0}",
27627     /**
27628      * Customizable piece of the default paging text (defaults to "First Page")
27629      * @type String
27630      */
27631     firstText : "First Page",
27632     /**
27633      * Customizable piece of the default paging text (defaults to "Previous Page")
27634      * @type String
27635      */
27636     prevText : "Previous Page",
27637     /**
27638      * Customizable piece of the default paging text (defaults to "Next Page")
27639      * @type String
27640      */
27641     nextText : "Next Page",
27642     /**
27643      * Customizable piece of the default paging text (defaults to "Last Page")
27644      * @type String
27645      */
27646     lastText : "Last Page",
27647     /**
27648      * Customizable piece of the default paging text (defaults to "Refresh")
27649      * @type String
27650      */
27651     refreshText : "Refresh",
27652
27653     buttons : false,
27654     // private
27655     onRender : function(ct, position) 
27656     {
27657         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27658         this.navgroup.parentId = this.id;
27659         this.navgroup.onRender(this.el, null);
27660         // add the buttons to the navgroup
27661         
27662         if(this.displayInfo){
27663             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27664             this.displayEl = this.el.select('.x-paging-info', true).first();
27665 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27666 //            this.displayEl = navel.el.select('span',true).first();
27667         }
27668         
27669         var _this = this;
27670         
27671         if(this.buttons){
27672             Roo.each(_this.buttons, function(e){ // this might need to use render????
27673                Roo.factory(e).render(_this.el);
27674             });
27675         }
27676             
27677         Roo.each(_this.toolbarItems, function(e) {
27678             _this.navgroup.addItem(e);
27679         });
27680         
27681         
27682         this.first = this.navgroup.addItem({
27683             tooltip: this.firstText,
27684             cls: "prev btn-outline-secondary",
27685             html : ' <i class="fa fa-step-backward"></i>',
27686             disabled: true,
27687             preventDefault: true,
27688             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27689         });
27690         
27691         this.prev =  this.navgroup.addItem({
27692             tooltip: this.prevText,
27693             cls: "prev btn-outline-secondary",
27694             html : ' <i class="fa fa-backward"></i>',
27695             disabled: true,
27696             preventDefault: true,
27697             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27698         });
27699     //this.addSeparator();
27700         
27701         
27702         var field = this.navgroup.addItem( {
27703             tagtype : 'span',
27704             cls : 'x-paging-position  btn-outline-secondary',
27705              disabled: true,
27706             html : this.beforePageText  +
27707                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27708                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27709          } ); //?? escaped?
27710         
27711         this.field = field.el.select('input', true).first();
27712         this.field.on("keydown", this.onPagingKeydown, this);
27713         this.field.on("focus", function(){this.dom.select();});
27714     
27715     
27716         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27717         //this.field.setHeight(18);
27718         //this.addSeparator();
27719         this.next = this.navgroup.addItem({
27720             tooltip: this.nextText,
27721             cls: "next btn-outline-secondary",
27722             html : ' <i class="fa fa-forward"></i>',
27723             disabled: true,
27724             preventDefault: true,
27725             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27726         });
27727         this.last = this.navgroup.addItem({
27728             tooltip: this.lastText,
27729             html : ' <i class="fa fa-step-forward"></i>',
27730             cls: "next btn-outline-secondary",
27731             disabled: true,
27732             preventDefault: true,
27733             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27734         });
27735     //this.addSeparator();
27736         this.loading = this.navgroup.addItem({
27737             tooltip: this.refreshText,
27738             cls: "btn-outline-secondary",
27739             html : ' <i class="fa fa-refresh"></i>',
27740             preventDefault: true,
27741             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27742         });
27743         
27744     },
27745
27746     // private
27747     updateInfo : function(){
27748         if(this.displayEl){
27749             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27750             var msg = count == 0 ?
27751                 this.emptyMsg :
27752                 String.format(
27753                     this.displayMsg,
27754                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27755                 );
27756             this.displayEl.update(msg);
27757         }
27758     },
27759
27760     // private
27761     onLoad : function(ds, r, o)
27762     {
27763         this.cursor = o.params && o.params.start ? o.params.start : 0;
27764         
27765         var d = this.getPageData(),
27766             ap = d.activePage,
27767             ps = d.pages;
27768         
27769         
27770         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27771         this.field.dom.value = ap;
27772         this.first.setDisabled(ap == 1);
27773         this.prev.setDisabled(ap == 1);
27774         this.next.setDisabled(ap == ps);
27775         this.last.setDisabled(ap == ps);
27776         this.loading.enable();
27777         this.updateInfo();
27778     },
27779
27780     // private
27781     getPageData : function(){
27782         var total = this.ds.getTotalCount();
27783         return {
27784             total : total,
27785             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27786             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27787         };
27788     },
27789
27790     // private
27791     onLoadError : function(){
27792         this.loading.enable();
27793     },
27794
27795     // private
27796     onPagingKeydown : function(e){
27797         var k = e.getKey();
27798         var d = this.getPageData();
27799         if(k == e.RETURN){
27800             var v = this.field.dom.value, pageNum;
27801             if(!v || isNaN(pageNum = parseInt(v, 10))){
27802                 this.field.dom.value = d.activePage;
27803                 return;
27804             }
27805             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27806             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27807             e.stopEvent();
27808         }
27809         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))
27810         {
27811           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27812           this.field.dom.value = pageNum;
27813           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27814           e.stopEvent();
27815         }
27816         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27817         {
27818           var v = this.field.dom.value, pageNum; 
27819           var increment = (e.shiftKey) ? 10 : 1;
27820           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27821                 increment *= -1;
27822           }
27823           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27824             this.field.dom.value = d.activePage;
27825             return;
27826           }
27827           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27828           {
27829             this.field.dom.value = parseInt(v, 10) + increment;
27830             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27831             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27832           }
27833           e.stopEvent();
27834         }
27835     },
27836
27837     // private
27838     beforeLoad : function(){
27839         if(this.loading){
27840             this.loading.disable();
27841         }
27842     },
27843
27844     // private
27845     onClick : function(which){
27846         
27847         var ds = this.ds;
27848         if (!ds) {
27849             return;
27850         }
27851         
27852         switch(which){
27853             case "first":
27854                 ds.load({params:{start: 0, limit: this.pageSize}});
27855             break;
27856             case "prev":
27857                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27858             break;
27859             case "next":
27860                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27861             break;
27862             case "last":
27863                 var total = ds.getTotalCount();
27864                 var extra = total % this.pageSize;
27865                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27866                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27867             break;
27868             case "refresh":
27869                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27870             break;
27871         }
27872     },
27873
27874     /**
27875      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27876      * @param {Roo.data.Store} store The data store to unbind
27877      */
27878     unbind : function(ds){
27879         ds.un("beforeload", this.beforeLoad, this);
27880         ds.un("load", this.onLoad, this);
27881         ds.un("loadexception", this.onLoadError, this);
27882         ds.un("remove", this.updateInfo, this);
27883         ds.un("add", this.updateInfo, this);
27884         this.ds = undefined;
27885     },
27886
27887     /**
27888      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27889      * @param {Roo.data.Store} store The data store to bind
27890      */
27891     bind : function(ds){
27892         ds.on("beforeload", this.beforeLoad, this);
27893         ds.on("load", this.onLoad, this);
27894         ds.on("loadexception", this.onLoadError, this);
27895         ds.on("remove", this.updateInfo, this);
27896         ds.on("add", this.updateInfo, this);
27897         this.ds = ds;
27898     }
27899 });/*
27900  * - LGPL
27901  *
27902  * element
27903  * 
27904  */
27905
27906 /**
27907  * @class Roo.bootstrap.MessageBar
27908  * @extends Roo.bootstrap.Component
27909  * Bootstrap MessageBar class
27910  * @cfg {String} html contents of the MessageBar
27911  * @cfg {String} weight (info | success | warning | danger) default info
27912  * @cfg {String} beforeClass insert the bar before the given class
27913  * @cfg {Boolean} closable (true | false) default false
27914  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27915  * 
27916  * @constructor
27917  * Create a new Element
27918  * @param {Object} config The config object
27919  */
27920
27921 Roo.bootstrap.MessageBar = function(config){
27922     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27923 };
27924
27925 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27926     
27927     html: '',
27928     weight: 'info',
27929     closable: false,
27930     fixed: false,
27931     beforeClass: 'bootstrap-sticky-wrap',
27932     
27933     getAutoCreate : function(){
27934         
27935         var cfg = {
27936             tag: 'div',
27937             cls: 'alert alert-dismissable alert-' + this.weight,
27938             cn: [
27939                 {
27940                     tag: 'span',
27941                     cls: 'message',
27942                     html: this.html || ''
27943                 }
27944             ]
27945         };
27946         
27947         if(this.fixed){
27948             cfg.cls += ' alert-messages-fixed';
27949         }
27950         
27951         if(this.closable){
27952             cfg.cn.push({
27953                 tag: 'button',
27954                 cls: 'close',
27955                 html: 'x'
27956             });
27957         }
27958         
27959         return cfg;
27960     },
27961     
27962     onRender : function(ct, position)
27963     {
27964         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27965         
27966         if(!this.el){
27967             var cfg = Roo.apply({},  this.getAutoCreate());
27968             cfg.id = Roo.id();
27969             
27970             if (this.cls) {
27971                 cfg.cls += ' ' + this.cls;
27972             }
27973             if (this.style) {
27974                 cfg.style = this.style;
27975             }
27976             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27977             
27978             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27979         }
27980         
27981         this.el.select('>button.close').on('click', this.hide, this);
27982         
27983     },
27984     
27985     show : function()
27986     {
27987         if (!this.rendered) {
27988             this.render();
27989         }
27990         
27991         this.el.show();
27992         
27993         this.fireEvent('show', this);
27994         
27995     },
27996     
27997     hide : function()
27998     {
27999         if (!this.rendered) {
28000             this.render();
28001         }
28002         
28003         this.el.hide();
28004         
28005         this.fireEvent('hide', this);
28006     },
28007     
28008     update : function()
28009     {
28010 //        var e = this.el.dom.firstChild;
28011 //        
28012 //        if(this.closable){
28013 //            e = e.nextSibling;
28014 //        }
28015 //        
28016 //        e.data = this.html || '';
28017
28018         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
28019     }
28020    
28021 });
28022
28023  
28024
28025      /*
28026  * - LGPL
28027  *
28028  * Graph
28029  * 
28030  */
28031
28032
28033 /**
28034  * @class Roo.bootstrap.Graph
28035  * @extends Roo.bootstrap.Component
28036  * Bootstrap Graph class
28037 > Prameters
28038  -sm {number} sm 4
28039  -md {number} md 5
28040  @cfg {String} graphtype  bar | vbar | pie
28041  @cfg {number} g_x coodinator | centre x (pie)
28042  @cfg {number} g_y coodinator | centre y (pie)
28043  @cfg {number} g_r radius (pie)
28044  @cfg {number} g_height height of the chart (respected by all elements in the set)
28045  @cfg {number} g_width width of the chart (respected by all elements in the set)
28046  @cfg {Object} title The title of the chart
28047     
28048  -{Array}  values
28049  -opts (object) options for the chart 
28050      o {
28051      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
28052      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
28053      o vgutter (number)
28054      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.
28055      o stacked (boolean) whether or not to tread values as in a stacked bar chart
28056      o to
28057      o stretch (boolean)
28058      o }
28059  -opts (object) options for the pie
28060      o{
28061      o cut
28062      o startAngle (number)
28063      o endAngle (number)
28064      } 
28065  *
28066  * @constructor
28067  * Create a new Input
28068  * @param {Object} config The config object
28069  */
28070
28071 Roo.bootstrap.Graph = function(config){
28072     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
28073     
28074     this.addEvents({
28075         // img events
28076         /**
28077          * @event click
28078          * The img click event for the img.
28079          * @param {Roo.EventObject} e
28080          */
28081         "click" : true
28082     });
28083 };
28084
28085 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
28086     
28087     sm: 4,
28088     md: 5,
28089     graphtype: 'bar',
28090     g_height: 250,
28091     g_width: 400,
28092     g_x: 50,
28093     g_y: 50,
28094     g_r: 30,
28095     opts:{
28096         //g_colors: this.colors,
28097         g_type: 'soft',
28098         g_gutter: '20%'
28099
28100     },
28101     title : false,
28102
28103     getAutoCreate : function(){
28104         
28105         var cfg = {
28106             tag: 'div',
28107             html : null
28108         };
28109         
28110         
28111         return  cfg;
28112     },
28113
28114     onRender : function(ct,position){
28115         
28116         
28117         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
28118         
28119         if (typeof(Raphael) == 'undefined') {
28120             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
28121             return;
28122         }
28123         
28124         this.raphael = Raphael(this.el.dom);
28125         
28126                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28127                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28128                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
28129                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
28130                 /*
28131                 r.text(160, 10, "Single Series Chart").attr(txtattr);
28132                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
28133                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
28134                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
28135                 
28136                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
28137                 r.barchart(330, 10, 300, 220, data1);
28138                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
28139                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
28140                 */
28141                 
28142                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28143                 // r.barchart(30, 30, 560, 250,  xdata, {
28144                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
28145                 //     axis : "0 0 1 1",
28146                 //     axisxlabels :  xdata
28147                 //     //yvalues : cols,
28148                    
28149                 // });
28150 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
28151 //        
28152 //        this.load(null,xdata,{
28153 //                axis : "0 0 1 1",
28154 //                axisxlabels :  xdata
28155 //                });
28156
28157     },
28158
28159     load : function(graphtype,xdata,opts)
28160     {
28161         this.raphael.clear();
28162         if(!graphtype) {
28163             graphtype = this.graphtype;
28164         }
28165         if(!opts){
28166             opts = this.opts;
28167         }
28168         var r = this.raphael,
28169             fin = function () {
28170                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
28171             },
28172             fout = function () {
28173                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
28174             },
28175             pfin = function() {
28176                 this.sector.stop();
28177                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
28178
28179                 if (this.label) {
28180                     this.label[0].stop();
28181                     this.label[0].attr({ r: 7.5 });
28182                     this.label[1].attr({ "font-weight": 800 });
28183                 }
28184             },
28185             pfout = function() {
28186                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
28187
28188                 if (this.label) {
28189                     this.label[0].animate({ r: 5 }, 500, "bounce");
28190                     this.label[1].attr({ "font-weight": 400 });
28191                 }
28192             };
28193
28194         switch(graphtype){
28195             case 'bar':
28196                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28197                 break;
28198             case 'hbar':
28199                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
28200                 break;
28201             case 'pie':
28202 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
28203 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
28204 //            
28205                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
28206                 
28207                 break;
28208
28209         }
28210         
28211         if(this.title){
28212             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
28213         }
28214         
28215     },
28216     
28217     setTitle: function(o)
28218     {
28219         this.title = o;
28220     },
28221     
28222     initEvents: function() {
28223         
28224         if(!this.href){
28225             this.el.on('click', this.onClick, this);
28226         }
28227     },
28228     
28229     onClick : function(e)
28230     {
28231         Roo.log('img onclick');
28232         this.fireEvent('click', this, e);
28233     }
28234    
28235 });
28236
28237  
28238 /*
28239  * - LGPL
28240  *
28241  * numberBox
28242  * 
28243  */
28244 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28245
28246 /**
28247  * @class Roo.bootstrap.dash.NumberBox
28248  * @extends Roo.bootstrap.Component
28249  * Bootstrap NumberBox class
28250  * @cfg {String} headline Box headline
28251  * @cfg {String} content Box content
28252  * @cfg {String} icon Box icon
28253  * @cfg {String} footer Footer text
28254  * @cfg {String} fhref Footer href
28255  * 
28256  * @constructor
28257  * Create a new NumberBox
28258  * @param {Object} config The config object
28259  */
28260
28261
28262 Roo.bootstrap.dash.NumberBox = function(config){
28263     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
28264     
28265 };
28266
28267 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
28268     
28269     headline : '',
28270     content : '',
28271     icon : '',
28272     footer : '',
28273     fhref : '',
28274     ficon : '',
28275     
28276     getAutoCreate : function(){
28277         
28278         var cfg = {
28279             tag : 'div',
28280             cls : 'small-box ',
28281             cn : [
28282                 {
28283                     tag : 'div',
28284                     cls : 'inner',
28285                     cn :[
28286                         {
28287                             tag : 'h3',
28288                             cls : 'roo-headline',
28289                             html : this.headline
28290                         },
28291                         {
28292                             tag : 'p',
28293                             cls : 'roo-content',
28294                             html : this.content
28295                         }
28296                     ]
28297                 }
28298             ]
28299         };
28300         
28301         if(this.icon){
28302             cfg.cn.push({
28303                 tag : 'div',
28304                 cls : 'icon',
28305                 cn :[
28306                     {
28307                         tag : 'i',
28308                         cls : 'ion ' + this.icon
28309                     }
28310                 ]
28311             });
28312         }
28313         
28314         if(this.footer){
28315             var footer = {
28316                 tag : 'a',
28317                 cls : 'small-box-footer',
28318                 href : this.fhref || '#',
28319                 html : this.footer
28320             };
28321             
28322             cfg.cn.push(footer);
28323             
28324         }
28325         
28326         return  cfg;
28327     },
28328
28329     onRender : function(ct,position){
28330         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28331
28332
28333        
28334                 
28335     },
28336
28337     setHeadline: function (value)
28338     {
28339         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28340     },
28341     
28342     setFooter: function (value, href)
28343     {
28344         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28345         
28346         if(href){
28347             this.el.select('a.small-box-footer',true).first().attr('href', href);
28348         }
28349         
28350     },
28351
28352     setContent: function (value)
28353     {
28354         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28355     },
28356
28357     initEvents: function() 
28358     {   
28359         
28360     }
28361     
28362 });
28363
28364  
28365 /*
28366  * - LGPL
28367  *
28368  * TabBox
28369  * 
28370  */
28371 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28372
28373 /**
28374  * @class Roo.bootstrap.dash.TabBox
28375  * @extends Roo.bootstrap.Component
28376  * Bootstrap TabBox class
28377  * @cfg {String} title Title of the TabBox
28378  * @cfg {String} icon Icon of the TabBox
28379  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28380  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28381  * 
28382  * @constructor
28383  * Create a new TabBox
28384  * @param {Object} config The config object
28385  */
28386
28387
28388 Roo.bootstrap.dash.TabBox = function(config){
28389     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28390     this.addEvents({
28391         // raw events
28392         /**
28393          * @event addpane
28394          * When a pane is added
28395          * @param {Roo.bootstrap.dash.TabPane} pane
28396          */
28397         "addpane" : true,
28398         /**
28399          * @event activatepane
28400          * When a pane is activated
28401          * @param {Roo.bootstrap.dash.TabPane} pane
28402          */
28403         "activatepane" : true
28404         
28405          
28406     });
28407     
28408     this.panes = [];
28409 };
28410
28411 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28412
28413     title : '',
28414     icon : false,
28415     showtabs : true,
28416     tabScrollable : false,
28417     
28418     getChildContainer : function()
28419     {
28420         return this.el.select('.tab-content', true).first();
28421     },
28422     
28423     getAutoCreate : function(){
28424         
28425         var header = {
28426             tag: 'li',
28427             cls: 'pull-left header',
28428             html: this.title,
28429             cn : []
28430         };
28431         
28432         if(this.icon){
28433             header.cn.push({
28434                 tag: 'i',
28435                 cls: 'fa ' + this.icon
28436             });
28437         }
28438         
28439         var h = {
28440             tag: 'ul',
28441             cls: 'nav nav-tabs pull-right',
28442             cn: [
28443                 header
28444             ]
28445         };
28446         
28447         if(this.tabScrollable){
28448             h = {
28449                 tag: 'div',
28450                 cls: 'tab-header',
28451                 cn: [
28452                     {
28453                         tag: 'ul',
28454                         cls: 'nav nav-tabs pull-right',
28455                         cn: [
28456                             header
28457                         ]
28458                     }
28459                 ]
28460             };
28461         }
28462         
28463         var cfg = {
28464             tag: 'div',
28465             cls: 'nav-tabs-custom',
28466             cn: [
28467                 h,
28468                 {
28469                     tag: 'div',
28470                     cls: 'tab-content no-padding',
28471                     cn: []
28472                 }
28473             ]
28474         };
28475
28476         return  cfg;
28477     },
28478     initEvents : function()
28479     {
28480         //Roo.log('add add pane handler');
28481         this.on('addpane', this.onAddPane, this);
28482     },
28483      /**
28484      * Updates the box title
28485      * @param {String} html to set the title to.
28486      */
28487     setTitle : function(value)
28488     {
28489         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28490     },
28491     onAddPane : function(pane)
28492     {
28493         this.panes.push(pane);
28494         //Roo.log('addpane');
28495         //Roo.log(pane);
28496         // tabs are rendere left to right..
28497         if(!this.showtabs){
28498             return;
28499         }
28500         
28501         var ctr = this.el.select('.nav-tabs', true).first();
28502          
28503          
28504         var existing = ctr.select('.nav-tab',true);
28505         var qty = existing.getCount();;
28506         
28507         
28508         var tab = ctr.createChild({
28509             tag : 'li',
28510             cls : 'nav-tab' + (qty ? '' : ' active'),
28511             cn : [
28512                 {
28513                     tag : 'a',
28514                     href:'#',
28515                     html : pane.title
28516                 }
28517             ]
28518         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28519         pane.tab = tab;
28520         
28521         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28522         if (!qty) {
28523             pane.el.addClass('active');
28524         }
28525         
28526                 
28527     },
28528     onTabClick : function(ev,un,ob,pane)
28529     {
28530         //Roo.log('tab - prev default');
28531         ev.preventDefault();
28532         
28533         
28534         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28535         pane.tab.addClass('active');
28536         //Roo.log(pane.title);
28537         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28538         // technically we should have a deactivate event.. but maybe add later.
28539         // and it should not de-activate the selected tab...
28540         this.fireEvent('activatepane', pane);
28541         pane.el.addClass('active');
28542         pane.fireEvent('activate');
28543         
28544         
28545     },
28546     
28547     getActivePane : function()
28548     {
28549         var r = false;
28550         Roo.each(this.panes, function(p) {
28551             if(p.el.hasClass('active')){
28552                 r = p;
28553                 return false;
28554             }
28555             
28556             return;
28557         });
28558         
28559         return r;
28560     }
28561     
28562     
28563 });
28564
28565  
28566 /*
28567  * - LGPL
28568  *
28569  * Tab pane
28570  * 
28571  */
28572 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28573 /**
28574  * @class Roo.bootstrap.TabPane
28575  * @extends Roo.bootstrap.Component
28576  * Bootstrap TabPane class
28577  * @cfg {Boolean} active (false | true) Default false
28578  * @cfg {String} title title of panel
28579
28580  * 
28581  * @constructor
28582  * Create a new TabPane
28583  * @param {Object} config The config object
28584  */
28585
28586 Roo.bootstrap.dash.TabPane = function(config){
28587     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28588     
28589     this.addEvents({
28590         // raw events
28591         /**
28592          * @event activate
28593          * When a pane is activated
28594          * @param {Roo.bootstrap.dash.TabPane} pane
28595          */
28596         "activate" : true
28597          
28598     });
28599 };
28600
28601 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28602     
28603     active : false,
28604     title : '',
28605     
28606     // the tabBox that this is attached to.
28607     tab : false,
28608      
28609     getAutoCreate : function() 
28610     {
28611         var cfg = {
28612             tag: 'div',
28613             cls: 'tab-pane'
28614         };
28615         
28616         if(this.active){
28617             cfg.cls += ' active';
28618         }
28619         
28620         return cfg;
28621     },
28622     initEvents  : function()
28623     {
28624         //Roo.log('trigger add pane handler');
28625         this.parent().fireEvent('addpane', this)
28626     },
28627     
28628      /**
28629      * Updates the tab title 
28630      * @param {String} html to set the title to.
28631      */
28632     setTitle: function(str)
28633     {
28634         if (!this.tab) {
28635             return;
28636         }
28637         this.title = str;
28638         this.tab.select('a', true).first().dom.innerHTML = str;
28639         
28640     }
28641     
28642     
28643     
28644 });
28645
28646  
28647
28648
28649  /*
28650  * - LGPL
28651  *
28652  * menu
28653  * 
28654  */
28655 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28656
28657 /**
28658  * @class Roo.bootstrap.menu.Menu
28659  * @extends Roo.bootstrap.Component
28660  * Bootstrap Menu class - container for Menu
28661  * @cfg {String} html Text of the menu
28662  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28663  * @cfg {String} icon Font awesome icon
28664  * @cfg {String} pos Menu align to (top | bottom) default bottom
28665  * 
28666  * 
28667  * @constructor
28668  * Create a new Menu
28669  * @param {Object} config The config object
28670  */
28671
28672
28673 Roo.bootstrap.menu.Menu = function(config){
28674     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28675     
28676     this.addEvents({
28677         /**
28678          * @event beforeshow
28679          * Fires before this menu is displayed
28680          * @param {Roo.bootstrap.menu.Menu} this
28681          */
28682         beforeshow : true,
28683         /**
28684          * @event beforehide
28685          * Fires before this menu is hidden
28686          * @param {Roo.bootstrap.menu.Menu} this
28687          */
28688         beforehide : true,
28689         /**
28690          * @event show
28691          * Fires after this menu is displayed
28692          * @param {Roo.bootstrap.menu.Menu} this
28693          */
28694         show : true,
28695         /**
28696          * @event hide
28697          * Fires after this menu is hidden
28698          * @param {Roo.bootstrap.menu.Menu} this
28699          */
28700         hide : true,
28701         /**
28702          * @event click
28703          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28704          * @param {Roo.bootstrap.menu.Menu} this
28705          * @param {Roo.EventObject} e
28706          */
28707         click : true
28708     });
28709     
28710 };
28711
28712 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28713     
28714     submenu : false,
28715     html : '',
28716     weight : 'default',
28717     icon : false,
28718     pos : 'bottom',
28719     
28720     
28721     getChildContainer : function() {
28722         if(this.isSubMenu){
28723             return this.el;
28724         }
28725         
28726         return this.el.select('ul.dropdown-menu', true).first();  
28727     },
28728     
28729     getAutoCreate : function()
28730     {
28731         var text = [
28732             {
28733                 tag : 'span',
28734                 cls : 'roo-menu-text',
28735                 html : this.html
28736             }
28737         ];
28738         
28739         if(this.icon){
28740             text.unshift({
28741                 tag : 'i',
28742                 cls : 'fa ' + this.icon
28743             })
28744         }
28745         
28746         
28747         var cfg = {
28748             tag : 'div',
28749             cls : 'btn-group',
28750             cn : [
28751                 {
28752                     tag : 'button',
28753                     cls : 'dropdown-button btn btn-' + this.weight,
28754                     cn : text
28755                 },
28756                 {
28757                     tag : 'button',
28758                     cls : 'dropdown-toggle btn btn-' + this.weight,
28759                     cn : [
28760                         {
28761                             tag : 'span',
28762                             cls : 'caret'
28763                         }
28764                     ]
28765                 },
28766                 {
28767                     tag : 'ul',
28768                     cls : 'dropdown-menu'
28769                 }
28770             ]
28771             
28772         };
28773         
28774         if(this.pos == 'top'){
28775             cfg.cls += ' dropup';
28776         }
28777         
28778         if(this.isSubMenu){
28779             cfg = {
28780                 tag : 'ul',
28781                 cls : 'dropdown-menu'
28782             }
28783         }
28784         
28785         return cfg;
28786     },
28787     
28788     onRender : function(ct, position)
28789     {
28790         this.isSubMenu = ct.hasClass('dropdown-submenu');
28791         
28792         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28793     },
28794     
28795     initEvents : function() 
28796     {
28797         if(this.isSubMenu){
28798             return;
28799         }
28800         
28801         this.hidden = true;
28802         
28803         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28804         this.triggerEl.on('click', this.onTriggerPress, this);
28805         
28806         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28807         this.buttonEl.on('click', this.onClick, this);
28808         
28809     },
28810     
28811     list : function()
28812     {
28813         if(this.isSubMenu){
28814             return this.el;
28815         }
28816         
28817         return this.el.select('ul.dropdown-menu', true).first();
28818     },
28819     
28820     onClick : function(e)
28821     {
28822         this.fireEvent("click", this, e);
28823     },
28824     
28825     onTriggerPress  : function(e)
28826     {   
28827         if (this.isVisible()) {
28828             this.hide();
28829         } else {
28830             this.show();
28831         }
28832     },
28833     
28834     isVisible : function(){
28835         return !this.hidden;
28836     },
28837     
28838     show : function()
28839     {
28840         this.fireEvent("beforeshow", this);
28841         
28842         this.hidden = false;
28843         this.el.addClass('open');
28844         
28845         Roo.get(document).on("mouseup", this.onMouseUp, this);
28846         
28847         this.fireEvent("show", this);
28848         
28849         
28850     },
28851     
28852     hide : function()
28853     {
28854         this.fireEvent("beforehide", this);
28855         
28856         this.hidden = true;
28857         this.el.removeClass('open');
28858         
28859         Roo.get(document).un("mouseup", this.onMouseUp);
28860         
28861         this.fireEvent("hide", this);
28862     },
28863     
28864     onMouseUp : function()
28865     {
28866         this.hide();
28867     }
28868     
28869 });
28870
28871  
28872  /*
28873  * - LGPL
28874  *
28875  * menu item
28876  * 
28877  */
28878 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28879
28880 /**
28881  * @class Roo.bootstrap.menu.Item
28882  * @extends Roo.bootstrap.Component
28883  * Bootstrap MenuItem class
28884  * @cfg {Boolean} submenu (true | false) default false
28885  * @cfg {String} html text of the item
28886  * @cfg {String} href the link
28887  * @cfg {Boolean} disable (true | false) default false
28888  * @cfg {Boolean} preventDefault (true | false) default true
28889  * @cfg {String} icon Font awesome icon
28890  * @cfg {String} pos Submenu align to (left | right) default right 
28891  * 
28892  * 
28893  * @constructor
28894  * Create a new Item
28895  * @param {Object} config The config object
28896  */
28897
28898
28899 Roo.bootstrap.menu.Item = function(config){
28900     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28901     this.addEvents({
28902         /**
28903          * @event mouseover
28904          * Fires when the mouse is hovering over this menu
28905          * @param {Roo.bootstrap.menu.Item} this
28906          * @param {Roo.EventObject} e
28907          */
28908         mouseover : true,
28909         /**
28910          * @event mouseout
28911          * Fires when the mouse exits this menu
28912          * @param {Roo.bootstrap.menu.Item} this
28913          * @param {Roo.EventObject} e
28914          */
28915         mouseout : true,
28916         // raw events
28917         /**
28918          * @event click
28919          * The raw click event for the entire grid.
28920          * @param {Roo.EventObject} e
28921          */
28922         click : true
28923     });
28924 };
28925
28926 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28927     
28928     submenu : false,
28929     href : '',
28930     html : '',
28931     preventDefault: true,
28932     disable : false,
28933     icon : false,
28934     pos : 'right',
28935     
28936     getAutoCreate : function()
28937     {
28938         var text = [
28939             {
28940                 tag : 'span',
28941                 cls : 'roo-menu-item-text',
28942                 html : this.html
28943             }
28944         ];
28945         
28946         if(this.icon){
28947             text.unshift({
28948                 tag : 'i',
28949                 cls : 'fa ' + this.icon
28950             })
28951         }
28952         
28953         var cfg = {
28954             tag : 'li',
28955             cn : [
28956                 {
28957                     tag : 'a',
28958                     href : this.href || '#',
28959                     cn : text
28960                 }
28961             ]
28962         };
28963         
28964         if(this.disable){
28965             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28966         }
28967         
28968         if(this.submenu){
28969             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28970             
28971             if(this.pos == 'left'){
28972                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28973             }
28974         }
28975         
28976         return cfg;
28977     },
28978     
28979     initEvents : function() 
28980     {
28981         this.el.on('mouseover', this.onMouseOver, this);
28982         this.el.on('mouseout', this.onMouseOut, this);
28983         
28984         this.el.select('a', true).first().on('click', this.onClick, this);
28985         
28986     },
28987     
28988     onClick : function(e)
28989     {
28990         if(this.preventDefault){
28991             e.preventDefault();
28992         }
28993         
28994         this.fireEvent("click", this, e);
28995     },
28996     
28997     onMouseOver : function(e)
28998     {
28999         if(this.submenu && this.pos == 'left'){
29000             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
29001         }
29002         
29003         this.fireEvent("mouseover", this, e);
29004     },
29005     
29006     onMouseOut : function(e)
29007     {
29008         this.fireEvent("mouseout", this, e);
29009     }
29010 });
29011
29012  
29013
29014  /*
29015  * - LGPL
29016  *
29017  * menu separator
29018  * 
29019  */
29020 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
29021
29022 /**
29023  * @class Roo.bootstrap.menu.Separator
29024  * @extends Roo.bootstrap.Component
29025  * Bootstrap Separator class
29026  * 
29027  * @constructor
29028  * Create a new Separator
29029  * @param {Object} config The config object
29030  */
29031
29032
29033 Roo.bootstrap.menu.Separator = function(config){
29034     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
29035 };
29036
29037 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
29038     
29039     getAutoCreate : function(){
29040         var cfg = {
29041             tag : 'li',
29042             cls: 'dropdown-divider divider'
29043         };
29044         
29045         return cfg;
29046     }
29047    
29048 });
29049
29050  
29051
29052  /*
29053  * - LGPL
29054  *
29055  * Tooltip
29056  * 
29057  */
29058
29059 /**
29060  * @class Roo.bootstrap.Tooltip
29061  * Bootstrap Tooltip class
29062  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
29063  * to determine which dom element triggers the tooltip.
29064  * 
29065  * It needs to add support for additional attributes like tooltip-position
29066  * 
29067  * @constructor
29068  * Create a new Toolti
29069  * @param {Object} config The config object
29070  */
29071
29072 Roo.bootstrap.Tooltip = function(config){
29073     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
29074     
29075     this.alignment = Roo.bootstrap.Tooltip.alignment;
29076     
29077     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
29078         this.alignment = config.alignment;
29079     }
29080     
29081 };
29082
29083 Roo.apply(Roo.bootstrap.Tooltip, {
29084     /**
29085      * @function init initialize tooltip monitoring.
29086      * @static
29087      */
29088     currentEl : false,
29089     currentTip : false,
29090     currentRegion : false,
29091     
29092     //  init : delay?
29093     
29094     init : function()
29095     {
29096         Roo.get(document).on('mouseover', this.enter ,this);
29097         Roo.get(document).on('mouseout', this.leave, this);
29098          
29099         
29100         this.currentTip = new Roo.bootstrap.Tooltip();
29101     },
29102     
29103     enter : function(ev)
29104     {
29105         var dom = ev.getTarget();
29106         
29107         //Roo.log(['enter',dom]);
29108         var el = Roo.fly(dom);
29109         if (this.currentEl) {
29110             //Roo.log(dom);
29111             //Roo.log(this.currentEl);
29112             //Roo.log(this.currentEl.contains(dom));
29113             if (this.currentEl == el) {
29114                 return;
29115             }
29116             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
29117                 return;
29118             }
29119
29120         }
29121         
29122         if (this.currentTip.el) {
29123             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
29124         }    
29125         //Roo.log(ev);
29126         
29127         if(!el || el.dom == document){
29128             return;
29129         }
29130         
29131         var bindEl = el; 
29132         var pel = false;
29133         if (!el.attr('tooltip')) {
29134             pel = el.findParent("[tooltip]");
29135             if (pel) {
29136                 bindEl = Roo.get(pel);
29137             }
29138         }
29139         
29140        
29141         
29142         // you can not look for children, as if el is the body.. then everythign is the child..
29143         if (!pel && !el.attr('tooltip')) { //
29144             if (!el.select("[tooltip]").elements.length) {
29145                 return;
29146             }
29147             // is the mouse over this child...?
29148             bindEl = el.select("[tooltip]").first();
29149             var xy = ev.getXY();
29150             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
29151                 //Roo.log("not in region.");
29152                 return;
29153             }
29154             //Roo.log("child element over..");
29155             
29156         }
29157         this.currentEl = el;
29158         this.currentTip.bind(bindEl);
29159         this.currentRegion = Roo.lib.Region.getRegion(dom);
29160         this.currentTip.enter();
29161         
29162     },
29163     leave : function(ev)
29164     {
29165         var dom = ev.getTarget();
29166         //Roo.log(['leave',dom]);
29167         if (!this.currentEl) {
29168             return;
29169         }
29170         
29171         
29172         if (dom != this.currentEl.dom) {
29173             return;
29174         }
29175         var xy = ev.getXY();
29176         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
29177             return;
29178         }
29179         // only activate leave if mouse cursor is outside... bounding box..
29180         
29181         
29182         
29183         
29184         if (this.currentTip) {
29185             this.currentTip.leave();
29186         }
29187         //Roo.log('clear currentEl');
29188         this.currentEl = false;
29189         
29190         
29191     },
29192     alignment : {
29193         'left' : ['r-l', [-2,0], 'right'],
29194         'right' : ['l-r', [2,0], 'left'],
29195         'bottom' : ['t-b', [0,2], 'top'],
29196         'top' : [ 'b-t', [0,-2], 'bottom']
29197     }
29198     
29199 });
29200
29201
29202 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
29203     
29204     
29205     bindEl : false,
29206     
29207     delay : null, // can be { show : 300 , hide: 500}
29208     
29209     timeout : null,
29210     
29211     hoverState : null, //???
29212     
29213     placement : 'bottom', 
29214     
29215     alignment : false,
29216     
29217     getAutoCreate : function(){
29218     
29219         var cfg = {
29220            cls : 'tooltip',   
29221            role : 'tooltip',
29222            cn : [
29223                 {
29224                     cls : 'tooltip-arrow arrow'
29225                 },
29226                 {
29227                     cls : 'tooltip-inner'
29228                 }
29229            ]
29230         };
29231         
29232         return cfg;
29233     },
29234     bind : function(el)
29235     {
29236         this.bindEl = el;
29237     },
29238     
29239     initEvents : function()
29240     {
29241         this.arrowEl = this.el.select('.arrow', true).first();
29242         this.innerEl = this.el.select('.tooltip-inner', true).first();
29243     },
29244     
29245     enter : function () {
29246        
29247         if (this.timeout != null) {
29248             clearTimeout(this.timeout);
29249         }
29250         
29251         this.hoverState = 'in';
29252          //Roo.log("enter - show");
29253         if (!this.delay || !this.delay.show) {
29254             this.show();
29255             return;
29256         }
29257         var _t = this;
29258         this.timeout = setTimeout(function () {
29259             if (_t.hoverState == 'in') {
29260                 _t.show();
29261             }
29262         }, this.delay.show);
29263     },
29264     leave : function()
29265     {
29266         clearTimeout(this.timeout);
29267     
29268         this.hoverState = 'out';
29269          if (!this.delay || !this.delay.hide) {
29270             this.hide();
29271             return;
29272         }
29273        
29274         var _t = this;
29275         this.timeout = setTimeout(function () {
29276             //Roo.log("leave - timeout");
29277             
29278             if (_t.hoverState == 'out') {
29279                 _t.hide();
29280                 Roo.bootstrap.Tooltip.currentEl = false;
29281             }
29282         }, delay);
29283     },
29284     
29285     show : function (msg)
29286     {
29287         if (!this.el) {
29288             this.render(document.body);
29289         }
29290         // set content.
29291         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
29292         
29293         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
29294         
29295         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
29296         
29297         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
29298                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
29299         
29300         var placement = typeof this.placement == 'function' ?
29301             this.placement.call(this, this.el, on_el) :
29302             this.placement;
29303             
29304         var autoToken = /\s?auto?\s?/i;
29305         var autoPlace = autoToken.test(placement);
29306         if (autoPlace) {
29307             placement = placement.replace(autoToken, '') || 'top';
29308         }
29309         
29310         //this.el.detach()
29311         //this.el.setXY([0,0]);
29312         this.el.show();
29313         //this.el.dom.style.display='block';
29314         
29315         //this.el.appendTo(on_el);
29316         
29317         var p = this.getPosition();
29318         var box = this.el.getBox();
29319         
29320         if (autoPlace) {
29321             // fixme..
29322         }
29323         
29324         var align = this.alignment[placement];
29325         
29326         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29327         
29328         if(placement == 'top' || placement == 'bottom'){
29329             if(xy[0] < 0){
29330                 placement = 'right';
29331             }
29332             
29333             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29334                 placement = 'left';
29335             }
29336             
29337             var scroll = Roo.select('body', true).first().getScroll();
29338             
29339             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29340                 placement = 'top';
29341             }
29342             
29343             align = this.alignment[placement];
29344             
29345             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29346             
29347         }
29348         
29349         var elems = document.getElementsByTagName('div');
29350         var highest = Number.MIN_SAFE_INTEGER || -(Math.pow(2, 53) - 1);
29351         for (var i = 0; i < elems.length; i++) {
29352           var zindex = Number.parseInt(
29353                 document.defaultView.getComputedStyle(elems[i], null).getPropertyValue("z-index"),
29354                 10
29355           );
29356           if (zindex > highest) {
29357             highest = zindex;
29358           }
29359         }
29360         
29361         
29362         
29363         this.el.dom.style.zIndex = highest;
29364         
29365         this.el.alignTo(this.bindEl, align[0],align[1]);
29366         //var arrow = this.el.select('.arrow',true).first();
29367         //arrow.set(align[2], 
29368         
29369         this.el.addClass(placement);
29370         this.el.addClass("bs-tooltip-"+ placement);
29371         
29372         this.el.addClass('in fade show');
29373         
29374         this.hoverState = null;
29375         
29376         if (this.el.hasClass('fade')) {
29377             // fade it?
29378         }
29379         
29380         
29381         
29382         
29383         
29384     },
29385     hide : function()
29386     {
29387          
29388         if (!this.el) {
29389             return;
29390         }
29391         //this.el.setXY([0,0]);
29392         this.el.removeClass(['show', 'in']);
29393         //this.el.hide();
29394         
29395     }
29396     
29397 });
29398  
29399
29400  /*
29401  * - LGPL
29402  *
29403  * Location Picker
29404  * 
29405  */
29406
29407 /**
29408  * @class Roo.bootstrap.LocationPicker
29409  * @extends Roo.bootstrap.Component
29410  * Bootstrap LocationPicker class
29411  * @cfg {Number} latitude Position when init default 0
29412  * @cfg {Number} longitude Position when init default 0
29413  * @cfg {Number} zoom default 15
29414  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29415  * @cfg {Boolean} mapTypeControl default false
29416  * @cfg {Boolean} disableDoubleClickZoom default false
29417  * @cfg {Boolean} scrollwheel default true
29418  * @cfg {Boolean} streetViewControl default false
29419  * @cfg {Number} radius default 0
29420  * @cfg {String} locationName
29421  * @cfg {Boolean} draggable default true
29422  * @cfg {Boolean} enableAutocomplete default false
29423  * @cfg {Boolean} enableReverseGeocode default true
29424  * @cfg {String} markerTitle
29425  * 
29426  * @constructor
29427  * Create a new LocationPicker
29428  * @param {Object} config The config object
29429  */
29430
29431
29432 Roo.bootstrap.LocationPicker = function(config){
29433     
29434     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29435     
29436     this.addEvents({
29437         /**
29438          * @event initial
29439          * Fires when the picker initialized.
29440          * @param {Roo.bootstrap.LocationPicker} this
29441          * @param {Google Location} location
29442          */
29443         initial : true,
29444         /**
29445          * @event positionchanged
29446          * Fires when the picker position changed.
29447          * @param {Roo.bootstrap.LocationPicker} this
29448          * @param {Google Location} location
29449          */
29450         positionchanged : true,
29451         /**
29452          * @event resize
29453          * Fires when the map resize.
29454          * @param {Roo.bootstrap.LocationPicker} this
29455          */
29456         resize : true,
29457         /**
29458          * @event show
29459          * Fires when the map show.
29460          * @param {Roo.bootstrap.LocationPicker} this
29461          */
29462         show : true,
29463         /**
29464          * @event hide
29465          * Fires when the map hide.
29466          * @param {Roo.bootstrap.LocationPicker} this
29467          */
29468         hide : true,
29469         /**
29470          * @event mapClick
29471          * Fires when click the map.
29472          * @param {Roo.bootstrap.LocationPicker} this
29473          * @param {Map event} e
29474          */
29475         mapClick : true,
29476         /**
29477          * @event mapRightClick
29478          * Fires when right click the map.
29479          * @param {Roo.bootstrap.LocationPicker} this
29480          * @param {Map event} e
29481          */
29482         mapRightClick : true,
29483         /**
29484          * @event markerClick
29485          * Fires when click the marker.
29486          * @param {Roo.bootstrap.LocationPicker} this
29487          * @param {Map event} e
29488          */
29489         markerClick : true,
29490         /**
29491          * @event markerRightClick
29492          * Fires when right click the marker.
29493          * @param {Roo.bootstrap.LocationPicker} this
29494          * @param {Map event} e
29495          */
29496         markerRightClick : true,
29497         /**
29498          * @event OverlayViewDraw
29499          * Fires when OverlayView Draw
29500          * @param {Roo.bootstrap.LocationPicker} this
29501          */
29502         OverlayViewDraw : true,
29503         /**
29504          * @event OverlayViewOnAdd
29505          * Fires when OverlayView Draw
29506          * @param {Roo.bootstrap.LocationPicker} this
29507          */
29508         OverlayViewOnAdd : true,
29509         /**
29510          * @event OverlayViewOnRemove
29511          * Fires when OverlayView Draw
29512          * @param {Roo.bootstrap.LocationPicker} this
29513          */
29514         OverlayViewOnRemove : true,
29515         /**
29516          * @event OverlayViewShow
29517          * Fires when OverlayView Draw
29518          * @param {Roo.bootstrap.LocationPicker} this
29519          * @param {Pixel} cpx
29520          */
29521         OverlayViewShow : true,
29522         /**
29523          * @event OverlayViewHide
29524          * Fires when OverlayView Draw
29525          * @param {Roo.bootstrap.LocationPicker} this
29526          */
29527         OverlayViewHide : true,
29528         /**
29529          * @event loadexception
29530          * Fires when load google lib failed.
29531          * @param {Roo.bootstrap.LocationPicker} this
29532          */
29533         loadexception : true
29534     });
29535         
29536 };
29537
29538 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29539     
29540     gMapContext: false,
29541     
29542     latitude: 0,
29543     longitude: 0,
29544     zoom: 15,
29545     mapTypeId: false,
29546     mapTypeControl: false,
29547     disableDoubleClickZoom: false,
29548     scrollwheel: true,
29549     streetViewControl: false,
29550     radius: 0,
29551     locationName: '',
29552     draggable: true,
29553     enableAutocomplete: false,
29554     enableReverseGeocode: true,
29555     markerTitle: '',
29556     
29557     getAutoCreate: function()
29558     {
29559
29560         var cfg = {
29561             tag: 'div',
29562             cls: 'roo-location-picker'
29563         };
29564         
29565         return cfg
29566     },
29567     
29568     initEvents: function(ct, position)
29569     {       
29570         if(!this.el.getWidth() || this.isApplied()){
29571             return;
29572         }
29573         
29574         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29575         
29576         this.initial();
29577     },
29578     
29579     initial: function()
29580     {
29581         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29582             this.fireEvent('loadexception', this);
29583             return;
29584         }
29585         
29586         if(!this.mapTypeId){
29587             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29588         }
29589         
29590         this.gMapContext = this.GMapContext();
29591         
29592         this.initOverlayView();
29593         
29594         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29595         
29596         var _this = this;
29597                 
29598         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29599             _this.setPosition(_this.gMapContext.marker.position);
29600         });
29601         
29602         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29603             _this.fireEvent('mapClick', this, event);
29604             
29605         });
29606
29607         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29608             _this.fireEvent('mapRightClick', this, event);
29609             
29610         });
29611         
29612         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29613             _this.fireEvent('markerClick', this, event);
29614             
29615         });
29616
29617         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29618             _this.fireEvent('markerRightClick', this, event);
29619             
29620         });
29621         
29622         this.setPosition(this.gMapContext.location);
29623         
29624         this.fireEvent('initial', this, this.gMapContext.location);
29625     },
29626     
29627     initOverlayView: function()
29628     {
29629         var _this = this;
29630         
29631         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29632             
29633             draw: function()
29634             {
29635                 _this.fireEvent('OverlayViewDraw', _this);
29636             },
29637             
29638             onAdd: function()
29639             {
29640                 _this.fireEvent('OverlayViewOnAdd', _this);
29641             },
29642             
29643             onRemove: function()
29644             {
29645                 _this.fireEvent('OverlayViewOnRemove', _this);
29646             },
29647             
29648             show: function(cpx)
29649             {
29650                 _this.fireEvent('OverlayViewShow', _this, cpx);
29651             },
29652             
29653             hide: function()
29654             {
29655                 _this.fireEvent('OverlayViewHide', _this);
29656             }
29657             
29658         });
29659     },
29660     
29661     fromLatLngToContainerPixel: function(event)
29662     {
29663         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29664     },
29665     
29666     isApplied: function() 
29667     {
29668         return this.getGmapContext() == false ? false : true;
29669     },
29670     
29671     getGmapContext: function() 
29672     {
29673         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29674     },
29675     
29676     GMapContext: function() 
29677     {
29678         var position = new google.maps.LatLng(this.latitude, this.longitude);
29679         
29680         var _map = new google.maps.Map(this.el.dom, {
29681             center: position,
29682             zoom: this.zoom,
29683             mapTypeId: this.mapTypeId,
29684             mapTypeControl: this.mapTypeControl,
29685             disableDoubleClickZoom: this.disableDoubleClickZoom,
29686             scrollwheel: this.scrollwheel,
29687             streetViewControl: this.streetViewControl,
29688             locationName: this.locationName,
29689             draggable: this.draggable,
29690             enableAutocomplete: this.enableAutocomplete,
29691             enableReverseGeocode: this.enableReverseGeocode
29692         });
29693         
29694         var _marker = new google.maps.Marker({
29695             position: position,
29696             map: _map,
29697             title: this.markerTitle,
29698             draggable: this.draggable
29699         });
29700         
29701         return {
29702             map: _map,
29703             marker: _marker,
29704             circle: null,
29705             location: position,
29706             radius: this.radius,
29707             locationName: this.locationName,
29708             addressComponents: {
29709                 formatted_address: null,
29710                 addressLine1: null,
29711                 addressLine2: null,
29712                 streetName: null,
29713                 streetNumber: null,
29714                 city: null,
29715                 district: null,
29716                 state: null,
29717                 stateOrProvince: null
29718             },
29719             settings: this,
29720             domContainer: this.el.dom,
29721             geodecoder: new google.maps.Geocoder()
29722         };
29723     },
29724     
29725     drawCircle: function(center, radius, options) 
29726     {
29727         if (this.gMapContext.circle != null) {
29728             this.gMapContext.circle.setMap(null);
29729         }
29730         if (radius > 0) {
29731             radius *= 1;
29732             options = Roo.apply({}, options, {
29733                 strokeColor: "#0000FF",
29734                 strokeOpacity: .35,
29735                 strokeWeight: 2,
29736                 fillColor: "#0000FF",
29737                 fillOpacity: .2
29738             });
29739             
29740             options.map = this.gMapContext.map;
29741             options.radius = radius;
29742             options.center = center;
29743             this.gMapContext.circle = new google.maps.Circle(options);
29744             return this.gMapContext.circle;
29745         }
29746         
29747         return null;
29748     },
29749     
29750     setPosition: function(location) 
29751     {
29752         this.gMapContext.location = location;
29753         this.gMapContext.marker.setPosition(location);
29754         this.gMapContext.map.panTo(location);
29755         this.drawCircle(location, this.gMapContext.radius, {});
29756         
29757         var _this = this;
29758         
29759         if (this.gMapContext.settings.enableReverseGeocode) {
29760             this.gMapContext.geodecoder.geocode({
29761                 latLng: this.gMapContext.location
29762             }, function(results, status) {
29763                 
29764                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29765                     _this.gMapContext.locationName = results[0].formatted_address;
29766                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29767                     
29768                     _this.fireEvent('positionchanged', this, location);
29769                 }
29770             });
29771             
29772             return;
29773         }
29774         
29775         this.fireEvent('positionchanged', this, location);
29776     },
29777     
29778     resize: function()
29779     {
29780         google.maps.event.trigger(this.gMapContext.map, "resize");
29781         
29782         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29783         
29784         this.fireEvent('resize', this);
29785     },
29786     
29787     setPositionByLatLng: function(latitude, longitude)
29788     {
29789         this.setPosition(new google.maps.LatLng(latitude, longitude));
29790     },
29791     
29792     getCurrentPosition: function() 
29793     {
29794         return {
29795             latitude: this.gMapContext.location.lat(),
29796             longitude: this.gMapContext.location.lng()
29797         };
29798     },
29799     
29800     getAddressName: function() 
29801     {
29802         return this.gMapContext.locationName;
29803     },
29804     
29805     getAddressComponents: function() 
29806     {
29807         return this.gMapContext.addressComponents;
29808     },
29809     
29810     address_component_from_google_geocode: function(address_components) 
29811     {
29812         var result = {};
29813         
29814         for (var i = 0; i < address_components.length; i++) {
29815             var component = address_components[i];
29816             if (component.types.indexOf("postal_code") >= 0) {
29817                 result.postalCode = component.short_name;
29818             } else if (component.types.indexOf("street_number") >= 0) {
29819                 result.streetNumber = component.short_name;
29820             } else if (component.types.indexOf("route") >= 0) {
29821                 result.streetName = component.short_name;
29822             } else if (component.types.indexOf("neighborhood") >= 0) {
29823                 result.city = component.short_name;
29824             } else if (component.types.indexOf("locality") >= 0) {
29825                 result.city = component.short_name;
29826             } else if (component.types.indexOf("sublocality") >= 0) {
29827                 result.district = component.short_name;
29828             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29829                 result.stateOrProvince = component.short_name;
29830             } else if (component.types.indexOf("country") >= 0) {
29831                 result.country = component.short_name;
29832             }
29833         }
29834         
29835         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29836         result.addressLine2 = "";
29837         return result;
29838     },
29839     
29840     setZoomLevel: function(zoom)
29841     {
29842         this.gMapContext.map.setZoom(zoom);
29843     },
29844     
29845     show: function()
29846     {
29847         if(!this.el){
29848             return;
29849         }
29850         
29851         this.el.show();
29852         
29853         this.resize();
29854         
29855         this.fireEvent('show', this);
29856     },
29857     
29858     hide: function()
29859     {
29860         if(!this.el){
29861             return;
29862         }
29863         
29864         this.el.hide();
29865         
29866         this.fireEvent('hide', this);
29867     }
29868     
29869 });
29870
29871 Roo.apply(Roo.bootstrap.LocationPicker, {
29872     
29873     OverlayView : function(map, options)
29874     {
29875         options = options || {};
29876         
29877         this.setMap(map);
29878     }
29879     
29880     
29881 });/**
29882  * @class Roo.bootstrap.Alert
29883  * @extends Roo.bootstrap.Component
29884  * Bootstrap Alert class - shows an alert area box
29885  * eg
29886  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29887   Enter a valid email address
29888 </div>
29889  * @licence LGPL
29890  * @cfg {String} title The title of alert
29891  * @cfg {String} html The content of alert
29892  * @cfg {String} weight (success|info|warning|danger) Weight of the message
29893  * @cfg {String} fa font-awesomeicon
29894  * @cfg {Number} seconds default:-1 Number of seconds until it disapears (-1 means never.)
29895  * @cfg {Boolean} close true to show a x closer
29896  * 
29897  * 
29898  * @constructor
29899  * Create a new alert
29900  * @param {Object} config The config object
29901  */
29902
29903
29904 Roo.bootstrap.Alert = function(config){
29905     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29906     
29907 };
29908
29909 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29910     
29911     title: '',
29912     html: '',
29913     weight: false,
29914     fa: false,
29915     faicon: false, // BC
29916     close : false,
29917     
29918     
29919     getAutoCreate : function()
29920     {
29921         
29922         var cfg = {
29923             tag : 'div',
29924             cls : 'alert',
29925             cn : [
29926                 {
29927                     tag: 'button',
29928                     type :  "button",
29929                     cls: "close",
29930                     html : '×',
29931                     style : this.close ? '' : 'display:none'
29932                 },
29933                 {
29934                     tag : 'i',
29935                     cls : 'roo-alert-icon'
29936                     
29937                 },
29938                 {
29939                     tag : 'b',
29940                     cls : 'roo-alert-title',
29941                     html : this.title
29942                 },
29943                 {
29944                     tag : 'span',
29945                     cls : 'roo-alert-text',
29946                     html : this.html
29947                 }
29948             ]
29949         };
29950         
29951         if(this.faicon){
29952             cfg.cn[0].cls += ' fa ' + this.faicon;
29953         }
29954         if(this.fa){
29955             cfg.cn[0].cls += ' fa ' + this.fa;
29956         }
29957         
29958         if(this.weight){
29959             cfg.cls += ' alert-' + this.weight;
29960         }
29961         
29962         return cfg;
29963     },
29964     
29965     initEvents: function() 
29966     {
29967         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29968         this.titleEl =  this.el.select('.roo-alert-title',true).first();
29969         this.iconEl = this.el.select('.roo-alert-icon',true).first();
29970         this.htmlEl = this.el.select('.roo-alert-text',true).first();
29971         if (this.seconds > 0) {
29972             this.hide.defer(this.seconds, this);
29973         }
29974     },
29975     /**
29976      * Set the Title Message HTML
29977      * @param {String} html
29978      */
29979     setTitle : function(str)
29980     {
29981         this.titleEl.dom.innerHTML = str;
29982     },
29983      
29984      /**
29985      * Set the Body Message HTML
29986      * @param {String} html
29987      */
29988     setHtml : function(str)
29989     {
29990         this.htmlEl.dom.innerHTML = str;
29991     },
29992     /**
29993      * Set the Weight of the alert
29994      * @param {String} (success|info|warning|danger) weight
29995      */
29996     
29997     setWeight : function(weight)
29998     {
29999         if(this.weight){
30000             this.el.removeClass('alert-' + this.weight);
30001         }
30002         
30003         this.weight = weight;
30004         
30005         this.el.addClass('alert-' + this.weight);
30006     },
30007       /**
30008      * Set the Icon of the alert
30009      * @param {String} see fontawsome names (name without the 'fa-' bit)
30010      */
30011     setIcon : function(icon)
30012     {
30013         if(this.faicon){
30014             this.alertEl.removeClass(['fa', 'fa-' + this.faicon]);
30015         }
30016         
30017         this.faicon = icon;
30018         
30019         this.alertEl.addClass(['fa', 'fa-' + this.faicon]);
30020     },
30021     /**
30022      * Hide the Alert
30023      */
30024     hide: function() 
30025     {
30026         this.el.hide();   
30027     },
30028     /**
30029      * Show the Alert
30030      */
30031     show: function() 
30032     {  
30033         this.el.show();   
30034     }
30035     
30036 });
30037
30038  
30039 /*
30040 * Licence: LGPL
30041 */
30042
30043 /**
30044  * @class Roo.bootstrap.UploadCropbox
30045  * @extends Roo.bootstrap.Component
30046  * Bootstrap UploadCropbox class
30047  * @cfg {String} emptyText show when image has been loaded
30048  * @cfg {String} rotateNotify show when image too small to rotate
30049  * @cfg {Number} errorTimeout default 3000
30050  * @cfg {Number} minWidth default 300
30051  * @cfg {Number} minHeight default 300
30052  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
30053  * @cfg {Boolean} isDocument (true|false) default false
30054  * @cfg {String} url action url
30055  * @cfg {String} paramName default 'imageUpload'
30056  * @cfg {String} method default POST
30057  * @cfg {Boolean} loadMask (true|false) default true
30058  * @cfg {Boolean} loadingText default 'Loading...'
30059  * 
30060  * @constructor
30061  * Create a new UploadCropbox
30062  * @param {Object} config The config object
30063  */
30064
30065 Roo.bootstrap.UploadCropbox = function(config){
30066     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
30067     
30068     this.addEvents({
30069         /**
30070          * @event beforeselectfile
30071          * Fire before select file
30072          * @param {Roo.bootstrap.UploadCropbox} this
30073          */
30074         "beforeselectfile" : true,
30075         /**
30076          * @event initial
30077          * Fire after initEvent
30078          * @param {Roo.bootstrap.UploadCropbox} this
30079          */
30080         "initial" : true,
30081         /**
30082          * @event crop
30083          * Fire after initEvent
30084          * @param {Roo.bootstrap.UploadCropbox} this
30085          * @param {String} data
30086          */
30087         "crop" : true,
30088         /**
30089          * @event prepare
30090          * Fire when preparing the file data
30091          * @param {Roo.bootstrap.UploadCropbox} this
30092          * @param {Object} file
30093          */
30094         "prepare" : true,
30095         /**
30096          * @event exception
30097          * Fire when get exception
30098          * @param {Roo.bootstrap.UploadCropbox} this
30099          * @param {XMLHttpRequest} xhr
30100          */
30101         "exception" : true,
30102         /**
30103          * @event beforeloadcanvas
30104          * Fire before load the canvas
30105          * @param {Roo.bootstrap.UploadCropbox} this
30106          * @param {String} src
30107          */
30108         "beforeloadcanvas" : true,
30109         /**
30110          * @event trash
30111          * Fire when trash image
30112          * @param {Roo.bootstrap.UploadCropbox} this
30113          */
30114         "trash" : true,
30115         /**
30116          * @event download
30117          * Fire when download the image
30118          * @param {Roo.bootstrap.UploadCropbox} this
30119          */
30120         "download" : true,
30121         /**
30122          * @event footerbuttonclick
30123          * Fire when footerbuttonclick
30124          * @param {Roo.bootstrap.UploadCropbox} this
30125          * @param {String} type
30126          */
30127         "footerbuttonclick" : true,
30128         /**
30129          * @event resize
30130          * Fire when resize
30131          * @param {Roo.bootstrap.UploadCropbox} this
30132          */
30133         "resize" : true,
30134         /**
30135          * @event rotate
30136          * Fire when rotate the image
30137          * @param {Roo.bootstrap.UploadCropbox} this
30138          * @param {String} pos
30139          */
30140         "rotate" : true,
30141         /**
30142          * @event inspect
30143          * Fire when inspect the file
30144          * @param {Roo.bootstrap.UploadCropbox} this
30145          * @param {Object} file
30146          */
30147         "inspect" : true,
30148         /**
30149          * @event upload
30150          * Fire when xhr upload the file
30151          * @param {Roo.bootstrap.UploadCropbox} this
30152          * @param {Object} data
30153          */
30154         "upload" : true,
30155         /**
30156          * @event arrange
30157          * Fire when arrange the file data
30158          * @param {Roo.bootstrap.UploadCropbox} this
30159          * @param {Object} formData
30160          */
30161         "arrange" : true
30162     });
30163     
30164     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
30165 };
30166
30167 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
30168     
30169     emptyText : 'Click to upload image',
30170     rotateNotify : 'Image is too small to rotate',
30171     errorTimeout : 3000,
30172     scale : 0,
30173     baseScale : 1,
30174     rotate : 0,
30175     dragable : false,
30176     pinching : false,
30177     mouseX : 0,
30178     mouseY : 0,
30179     cropData : false,
30180     minWidth : 300,
30181     minHeight : 300,
30182     file : false,
30183     exif : {},
30184     baseRotate : 1,
30185     cropType : 'image/jpeg',
30186     buttons : false,
30187     canvasLoaded : false,
30188     isDocument : false,
30189     method : 'POST',
30190     paramName : 'imageUpload',
30191     loadMask : true,
30192     loadingText : 'Loading...',
30193     maskEl : false,
30194     
30195     getAutoCreate : function()
30196     {
30197         var cfg = {
30198             tag : 'div',
30199             cls : 'roo-upload-cropbox',
30200             cn : [
30201                 {
30202                     tag : 'input',
30203                     cls : 'roo-upload-cropbox-selector',
30204                     type : 'file'
30205                 },
30206                 {
30207                     tag : 'div',
30208                     cls : 'roo-upload-cropbox-body',
30209                     style : 'cursor:pointer',
30210                     cn : [
30211                         {
30212                             tag : 'div',
30213                             cls : 'roo-upload-cropbox-preview'
30214                         },
30215                         {
30216                             tag : 'div',
30217                             cls : 'roo-upload-cropbox-thumb'
30218                         },
30219                         {
30220                             tag : 'div',
30221                             cls : 'roo-upload-cropbox-empty-notify',
30222                             html : this.emptyText
30223                         },
30224                         {
30225                             tag : 'div',
30226                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
30227                             html : this.rotateNotify
30228                         }
30229                     ]
30230                 },
30231                 {
30232                     tag : 'div',
30233                     cls : 'roo-upload-cropbox-footer',
30234                     cn : {
30235                         tag : 'div',
30236                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
30237                         cn : []
30238                     }
30239                 }
30240             ]
30241         };
30242         
30243         return cfg;
30244     },
30245     
30246     onRender : function(ct, position)
30247     {
30248         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
30249         
30250         if (this.buttons.length) {
30251             
30252             Roo.each(this.buttons, function(bb) {
30253                 
30254                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
30255                 
30256                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
30257                 
30258             }, this);
30259         }
30260         
30261         if(this.loadMask){
30262             this.maskEl = this.el;
30263         }
30264     },
30265     
30266     initEvents : function()
30267     {
30268         this.urlAPI = (window.createObjectURL && window) || 
30269                                 (window.URL && URL.revokeObjectURL && URL) || 
30270                                 (window.webkitURL && webkitURL);
30271                         
30272         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
30273         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30274         
30275         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
30276         this.selectorEl.hide();
30277         
30278         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
30279         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30280         
30281         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
30282         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30283         this.thumbEl.hide();
30284         
30285         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
30286         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30287         
30288         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
30289         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30290         this.errorEl.hide();
30291         
30292         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
30293         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
30294         this.footerEl.hide();
30295         
30296         this.setThumbBoxSize();
30297         
30298         this.bind();
30299         
30300         this.resize();
30301         
30302         this.fireEvent('initial', this);
30303     },
30304
30305     bind : function()
30306     {
30307         var _this = this;
30308         
30309         window.addEventListener("resize", function() { _this.resize(); } );
30310         
30311         this.bodyEl.on('click', this.beforeSelectFile, this);
30312         
30313         if(Roo.isTouch){
30314             this.bodyEl.on('touchstart', this.onTouchStart, this);
30315             this.bodyEl.on('touchmove', this.onTouchMove, this);
30316             this.bodyEl.on('touchend', this.onTouchEnd, this);
30317         }
30318         
30319         if(!Roo.isTouch){
30320             this.bodyEl.on('mousedown', this.onMouseDown, this);
30321             this.bodyEl.on('mousemove', this.onMouseMove, this);
30322             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
30323             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
30324             Roo.get(document).on('mouseup', this.onMouseUp, this);
30325         }
30326         
30327         this.selectorEl.on('change', this.onFileSelected, this);
30328     },
30329     
30330     reset : function()
30331     {    
30332         this.scale = 0;
30333         this.baseScale = 1;
30334         this.rotate = 0;
30335         this.baseRotate = 1;
30336         this.dragable = false;
30337         this.pinching = false;
30338         this.mouseX = 0;
30339         this.mouseY = 0;
30340         this.cropData = false;
30341         this.notifyEl.dom.innerHTML = this.emptyText;
30342         
30343         this.selectorEl.dom.value = '';
30344         
30345     },
30346     
30347     resize : function()
30348     {
30349         if(this.fireEvent('resize', this) != false){
30350             this.setThumbBoxPosition();
30351             this.setCanvasPosition();
30352         }
30353     },
30354     
30355     onFooterButtonClick : function(e, el, o, type)
30356     {
30357         switch (type) {
30358             case 'rotate-left' :
30359                 this.onRotateLeft(e);
30360                 break;
30361             case 'rotate-right' :
30362                 this.onRotateRight(e);
30363                 break;
30364             case 'picture' :
30365                 this.beforeSelectFile(e);
30366                 break;
30367             case 'trash' :
30368                 this.trash(e);
30369                 break;
30370             case 'crop' :
30371                 this.crop(e);
30372                 break;
30373             case 'download' :
30374                 this.download(e);
30375                 break;
30376             default :
30377                 break;
30378         }
30379         
30380         this.fireEvent('footerbuttonclick', this, type);
30381     },
30382     
30383     beforeSelectFile : function(e)
30384     {
30385         e.preventDefault();
30386         
30387         if(this.fireEvent('beforeselectfile', this) != false){
30388             this.selectorEl.dom.click();
30389         }
30390     },
30391     
30392     onFileSelected : function(e)
30393     {
30394         e.preventDefault();
30395         
30396         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30397             return;
30398         }
30399         
30400         var file = this.selectorEl.dom.files[0];
30401         
30402         if(this.fireEvent('inspect', this, file) != false){
30403             this.prepare(file);
30404         }
30405         
30406     },
30407     
30408     trash : function(e)
30409     {
30410         this.fireEvent('trash', this);
30411     },
30412     
30413     download : function(e)
30414     {
30415         this.fireEvent('download', this);
30416     },
30417     
30418     loadCanvas : function(src)
30419     {   
30420         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30421             
30422             this.reset();
30423             
30424             this.imageEl = document.createElement('img');
30425             
30426             var _this = this;
30427             
30428             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30429             
30430             this.imageEl.src = src;
30431         }
30432     },
30433     
30434     onLoadCanvas : function()
30435     {   
30436         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30437         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30438         
30439         this.bodyEl.un('click', this.beforeSelectFile, this);
30440         
30441         this.notifyEl.hide();
30442         this.thumbEl.show();
30443         this.footerEl.show();
30444         
30445         this.baseRotateLevel();
30446         
30447         if(this.isDocument){
30448             this.setThumbBoxSize();
30449         }
30450         
30451         this.setThumbBoxPosition();
30452         
30453         this.baseScaleLevel();
30454         
30455         this.draw();
30456         
30457         this.resize();
30458         
30459         this.canvasLoaded = true;
30460         
30461         if(this.loadMask){
30462             this.maskEl.unmask();
30463         }
30464         
30465     },
30466     
30467     setCanvasPosition : function()
30468     {   
30469         if(!this.canvasEl){
30470             return;
30471         }
30472         
30473         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30474         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30475         
30476         this.previewEl.setLeft(pw);
30477         this.previewEl.setTop(ph);
30478         
30479     },
30480     
30481     onMouseDown : function(e)
30482     {   
30483         e.stopEvent();
30484         
30485         this.dragable = true;
30486         this.pinching = false;
30487         
30488         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30489             this.dragable = false;
30490             return;
30491         }
30492         
30493         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30494         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30495         
30496     },
30497     
30498     onMouseMove : function(e)
30499     {   
30500         e.stopEvent();
30501         
30502         if(!this.canvasLoaded){
30503             return;
30504         }
30505         
30506         if (!this.dragable){
30507             return;
30508         }
30509         
30510         var minX = Math.ceil(this.thumbEl.getLeft(true));
30511         var minY = Math.ceil(this.thumbEl.getTop(true));
30512         
30513         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30514         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30515         
30516         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30517         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30518         
30519         x = x - this.mouseX;
30520         y = y - this.mouseY;
30521         
30522         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30523         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30524         
30525         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30526         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30527         
30528         this.previewEl.setLeft(bgX);
30529         this.previewEl.setTop(bgY);
30530         
30531         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30532         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30533     },
30534     
30535     onMouseUp : function(e)
30536     {   
30537         e.stopEvent();
30538         
30539         this.dragable = false;
30540     },
30541     
30542     onMouseWheel : function(e)
30543     {   
30544         e.stopEvent();
30545         
30546         this.startScale = this.scale;
30547         
30548         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30549         
30550         if(!this.zoomable()){
30551             this.scale = this.startScale;
30552             return;
30553         }
30554         
30555         this.draw();
30556         
30557         return;
30558     },
30559     
30560     zoomable : function()
30561     {
30562         var minScale = this.thumbEl.getWidth() / this.minWidth;
30563         
30564         if(this.minWidth < this.minHeight){
30565             minScale = this.thumbEl.getHeight() / this.minHeight;
30566         }
30567         
30568         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30569         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30570         
30571         if(
30572                 this.isDocument &&
30573                 (this.rotate == 0 || this.rotate == 180) && 
30574                 (
30575                     width > this.imageEl.OriginWidth || 
30576                     height > this.imageEl.OriginHeight ||
30577                     (width < this.minWidth && height < this.minHeight)
30578                 )
30579         ){
30580             return false;
30581         }
30582         
30583         if(
30584                 this.isDocument &&
30585                 (this.rotate == 90 || this.rotate == 270) && 
30586                 (
30587                     width > this.imageEl.OriginWidth || 
30588                     height > this.imageEl.OriginHeight ||
30589                     (width < this.minHeight && height < this.minWidth)
30590                 )
30591         ){
30592             return false;
30593         }
30594         
30595         if(
30596                 !this.isDocument &&
30597                 (this.rotate == 0 || this.rotate == 180) && 
30598                 (
30599                     width < this.minWidth || 
30600                     width > this.imageEl.OriginWidth || 
30601                     height < this.minHeight || 
30602                     height > this.imageEl.OriginHeight
30603                 )
30604         ){
30605             return false;
30606         }
30607         
30608         if(
30609                 !this.isDocument &&
30610                 (this.rotate == 90 || this.rotate == 270) && 
30611                 (
30612                     width < this.minHeight || 
30613                     width > this.imageEl.OriginWidth || 
30614                     height < this.minWidth || 
30615                     height > this.imageEl.OriginHeight
30616                 )
30617         ){
30618             return false;
30619         }
30620         
30621         return true;
30622         
30623     },
30624     
30625     onRotateLeft : function(e)
30626     {   
30627         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30628             
30629             var minScale = this.thumbEl.getWidth() / this.minWidth;
30630             
30631             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30632             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30633             
30634             this.startScale = this.scale;
30635             
30636             while (this.getScaleLevel() < minScale){
30637             
30638                 this.scale = this.scale + 1;
30639                 
30640                 if(!this.zoomable()){
30641                     break;
30642                 }
30643                 
30644                 if(
30645                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30646                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30647                 ){
30648                     continue;
30649                 }
30650                 
30651                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30652
30653                 this.draw();
30654                 
30655                 return;
30656             }
30657             
30658             this.scale = this.startScale;
30659             
30660             this.onRotateFail();
30661             
30662             return false;
30663         }
30664         
30665         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30666
30667         if(this.isDocument){
30668             this.setThumbBoxSize();
30669             this.setThumbBoxPosition();
30670             this.setCanvasPosition();
30671         }
30672         
30673         this.draw();
30674         
30675         this.fireEvent('rotate', this, 'left');
30676         
30677     },
30678     
30679     onRotateRight : function(e)
30680     {
30681         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30682             
30683             var minScale = this.thumbEl.getWidth() / this.minWidth;
30684         
30685             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30686             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30687             
30688             this.startScale = this.scale;
30689             
30690             while (this.getScaleLevel() < minScale){
30691             
30692                 this.scale = this.scale + 1;
30693                 
30694                 if(!this.zoomable()){
30695                     break;
30696                 }
30697                 
30698                 if(
30699                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30700                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30701                 ){
30702                     continue;
30703                 }
30704                 
30705                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30706
30707                 this.draw();
30708                 
30709                 return;
30710             }
30711             
30712             this.scale = this.startScale;
30713             
30714             this.onRotateFail();
30715             
30716             return false;
30717         }
30718         
30719         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30720
30721         if(this.isDocument){
30722             this.setThumbBoxSize();
30723             this.setThumbBoxPosition();
30724             this.setCanvasPosition();
30725         }
30726         
30727         this.draw();
30728         
30729         this.fireEvent('rotate', this, 'right');
30730     },
30731     
30732     onRotateFail : function()
30733     {
30734         this.errorEl.show(true);
30735         
30736         var _this = this;
30737         
30738         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30739     },
30740     
30741     draw : function()
30742     {
30743         this.previewEl.dom.innerHTML = '';
30744         
30745         var canvasEl = document.createElement("canvas");
30746         
30747         var contextEl = canvasEl.getContext("2d");
30748         
30749         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30750         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30751         var center = this.imageEl.OriginWidth / 2;
30752         
30753         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30754             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30755             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30756             center = this.imageEl.OriginHeight / 2;
30757         }
30758         
30759         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30760         
30761         contextEl.translate(center, center);
30762         contextEl.rotate(this.rotate * Math.PI / 180);
30763
30764         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30765         
30766         this.canvasEl = document.createElement("canvas");
30767         
30768         this.contextEl = this.canvasEl.getContext("2d");
30769         
30770         switch (this.rotate) {
30771             case 0 :
30772                 
30773                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30774                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30775                 
30776                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30777                 
30778                 break;
30779             case 90 : 
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, Math.abs(this.canvasEl.width - this.canvasEl.height), 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, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30790                 
30791                 break;
30792             case 180 :
30793                 
30794                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30795                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30796                 
30797                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30798                     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);
30799                     break;
30800                 }
30801                 
30802                 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);
30803                 
30804                 break;
30805             case 270 :
30806                 
30807                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30808                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30809         
30810                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30811                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30812                     break;
30813                 }
30814                 
30815                 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);
30816                 
30817                 break;
30818             default : 
30819                 break;
30820         }
30821         
30822         this.previewEl.appendChild(this.canvasEl);
30823         
30824         this.setCanvasPosition();
30825     },
30826     
30827     crop : function()
30828     {
30829         if(!this.canvasLoaded){
30830             return;
30831         }
30832         
30833         var imageCanvas = document.createElement("canvas");
30834         
30835         var imageContext = imageCanvas.getContext("2d");
30836         
30837         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30838         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30839         
30840         var center = imageCanvas.width / 2;
30841         
30842         imageContext.translate(center, center);
30843         
30844         imageContext.rotate(this.rotate * Math.PI / 180);
30845         
30846         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30847         
30848         var canvas = document.createElement("canvas");
30849         
30850         var context = canvas.getContext("2d");
30851                 
30852         canvas.width = this.minWidth;
30853         canvas.height = this.minHeight;
30854
30855         switch (this.rotate) {
30856             case 0 :
30857                 
30858                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30859                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30860                 
30861                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30862                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30863                 
30864                 var targetWidth = this.minWidth - 2 * x;
30865                 var targetHeight = this.minHeight - 2 * y;
30866                 
30867                 var scale = 1;
30868                 
30869                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30870                     scale = targetWidth / width;
30871                 }
30872                 
30873                 if(x > 0 && y == 0){
30874                     scale = targetHeight / height;
30875                 }
30876                 
30877                 if(x > 0 && y > 0){
30878                     scale = targetWidth / width;
30879                     
30880                     if(width < height){
30881                         scale = targetHeight / height;
30882                     }
30883                 }
30884                 
30885                 context.scale(scale, scale);
30886                 
30887                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30888                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30889
30890                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30891                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30892
30893                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30894                 
30895                 break;
30896             case 90 : 
30897                 
30898                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30899                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30900                 
30901                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30902                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30903                 
30904                 var targetWidth = this.minWidth - 2 * x;
30905                 var targetHeight = this.minHeight - 2 * y;
30906                 
30907                 var scale = 1;
30908                 
30909                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30910                     scale = targetWidth / width;
30911                 }
30912                 
30913                 if(x > 0 && y == 0){
30914                     scale = targetHeight / height;
30915                 }
30916                 
30917                 if(x > 0 && y > 0){
30918                     scale = targetWidth / width;
30919                     
30920                     if(width < height){
30921                         scale = targetHeight / height;
30922                     }
30923                 }
30924                 
30925                 context.scale(scale, scale);
30926                 
30927                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30928                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30929
30930                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30931                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30932                 
30933                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30934                 
30935                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30936                 
30937                 break;
30938             case 180 :
30939                 
30940                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30941                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30942                 
30943                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30944                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30945                 
30946                 var targetWidth = this.minWidth - 2 * x;
30947                 var targetHeight = this.minHeight - 2 * y;
30948                 
30949                 var scale = 1;
30950                 
30951                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30952                     scale = targetWidth / width;
30953                 }
30954                 
30955                 if(x > 0 && y == 0){
30956                     scale = targetHeight / height;
30957                 }
30958                 
30959                 if(x > 0 && y > 0){
30960                     scale = targetWidth / width;
30961                     
30962                     if(width < height){
30963                         scale = targetHeight / height;
30964                     }
30965                 }
30966                 
30967                 context.scale(scale, scale);
30968                 
30969                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30970                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30971
30972                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30973                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30974
30975                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30976                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30977                 
30978                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30979                 
30980                 break;
30981             case 270 :
30982                 
30983                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30984                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30985                 
30986                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30987                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30988                 
30989                 var targetWidth = this.minWidth - 2 * x;
30990                 var targetHeight = this.minHeight - 2 * y;
30991                 
30992                 var scale = 1;
30993                 
30994                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30995                     scale = targetWidth / width;
30996                 }
30997                 
30998                 if(x > 0 && y == 0){
30999                     scale = targetHeight / height;
31000                 }
31001                 
31002                 if(x > 0 && y > 0){
31003                     scale = targetWidth / width;
31004                     
31005                     if(width < height){
31006                         scale = targetHeight / height;
31007                     }
31008                 }
31009                 
31010                 context.scale(scale, scale);
31011                 
31012                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
31013                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
31014
31015                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
31016                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
31017                 
31018                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
31019                 
31020                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
31021                 
31022                 break;
31023             default : 
31024                 break;
31025         }
31026         
31027         this.cropData = canvas.toDataURL(this.cropType);
31028         
31029         if(this.fireEvent('crop', this, this.cropData) !== false){
31030             this.process(this.file, this.cropData);
31031         }
31032         
31033         return;
31034         
31035     },
31036     
31037     setThumbBoxSize : function()
31038     {
31039         var width, height;
31040         
31041         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
31042             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
31043             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
31044             
31045             this.minWidth = width;
31046             this.minHeight = height;
31047             
31048             if(this.rotate == 90 || this.rotate == 270){
31049                 this.minWidth = height;
31050                 this.minHeight = width;
31051             }
31052         }
31053         
31054         height = 300;
31055         width = Math.ceil(this.minWidth * height / this.minHeight);
31056         
31057         if(this.minWidth > this.minHeight){
31058             width = 300;
31059             height = Math.ceil(this.minHeight * width / this.minWidth);
31060         }
31061         
31062         this.thumbEl.setStyle({
31063             width : width + 'px',
31064             height : height + 'px'
31065         });
31066
31067         return;
31068             
31069     },
31070     
31071     setThumbBoxPosition : function()
31072     {
31073         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
31074         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
31075         
31076         this.thumbEl.setLeft(x);
31077         this.thumbEl.setTop(y);
31078         
31079     },
31080     
31081     baseRotateLevel : function()
31082     {
31083         this.baseRotate = 1;
31084         
31085         if(
31086                 typeof(this.exif) != 'undefined' &&
31087                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
31088                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
31089         ){
31090             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
31091         }
31092         
31093         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
31094         
31095     },
31096     
31097     baseScaleLevel : function()
31098     {
31099         var width, height;
31100         
31101         if(this.isDocument){
31102             
31103             if(this.baseRotate == 6 || this.baseRotate == 8){
31104             
31105                 height = this.thumbEl.getHeight();
31106                 this.baseScale = height / this.imageEl.OriginWidth;
31107
31108                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
31109                     width = this.thumbEl.getWidth();
31110                     this.baseScale = width / this.imageEl.OriginHeight;
31111                 }
31112
31113                 return;
31114             }
31115
31116             height = this.thumbEl.getHeight();
31117             this.baseScale = height / this.imageEl.OriginHeight;
31118
31119             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
31120                 width = this.thumbEl.getWidth();
31121                 this.baseScale = width / this.imageEl.OriginWidth;
31122             }
31123
31124             return;
31125         }
31126         
31127         if(this.baseRotate == 6 || this.baseRotate == 8){
31128             
31129             width = this.thumbEl.getHeight();
31130             this.baseScale = width / this.imageEl.OriginHeight;
31131             
31132             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
31133                 height = this.thumbEl.getWidth();
31134                 this.baseScale = height / this.imageEl.OriginHeight;
31135             }
31136             
31137             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31138                 height = this.thumbEl.getWidth();
31139                 this.baseScale = height / this.imageEl.OriginHeight;
31140                 
31141                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
31142                     width = this.thumbEl.getHeight();
31143                     this.baseScale = width / this.imageEl.OriginWidth;
31144                 }
31145             }
31146             
31147             return;
31148         }
31149         
31150         width = this.thumbEl.getWidth();
31151         this.baseScale = width / this.imageEl.OriginWidth;
31152         
31153         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
31154             height = this.thumbEl.getHeight();
31155             this.baseScale = height / this.imageEl.OriginHeight;
31156         }
31157         
31158         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
31159             
31160             height = this.thumbEl.getHeight();
31161             this.baseScale = height / this.imageEl.OriginHeight;
31162             
31163             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
31164                 width = this.thumbEl.getWidth();
31165                 this.baseScale = width / this.imageEl.OriginWidth;
31166             }
31167             
31168         }
31169         
31170         return;
31171     },
31172     
31173     getScaleLevel : function()
31174     {
31175         return this.baseScale * Math.pow(1.1, this.scale);
31176     },
31177     
31178     onTouchStart : function(e)
31179     {
31180         if(!this.canvasLoaded){
31181             this.beforeSelectFile(e);
31182             return;
31183         }
31184         
31185         var touches = e.browserEvent.touches;
31186         
31187         if(!touches){
31188             return;
31189         }
31190         
31191         if(touches.length == 1){
31192             this.onMouseDown(e);
31193             return;
31194         }
31195         
31196         if(touches.length != 2){
31197             return;
31198         }
31199         
31200         var coords = [];
31201         
31202         for(var i = 0, finger; finger = touches[i]; i++){
31203             coords.push(finger.pageX, finger.pageY);
31204         }
31205         
31206         var x = Math.pow(coords[0] - coords[2], 2);
31207         var y = Math.pow(coords[1] - coords[3], 2);
31208         
31209         this.startDistance = Math.sqrt(x + y);
31210         
31211         this.startScale = this.scale;
31212         
31213         this.pinching = true;
31214         this.dragable = false;
31215         
31216     },
31217     
31218     onTouchMove : function(e)
31219     {
31220         if(!this.pinching && !this.dragable){
31221             return;
31222         }
31223         
31224         var touches = e.browserEvent.touches;
31225         
31226         if(!touches){
31227             return;
31228         }
31229         
31230         if(this.dragable){
31231             this.onMouseMove(e);
31232             return;
31233         }
31234         
31235         var coords = [];
31236         
31237         for(var i = 0, finger; finger = touches[i]; i++){
31238             coords.push(finger.pageX, finger.pageY);
31239         }
31240         
31241         var x = Math.pow(coords[0] - coords[2], 2);
31242         var y = Math.pow(coords[1] - coords[3], 2);
31243         
31244         this.endDistance = Math.sqrt(x + y);
31245         
31246         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
31247         
31248         if(!this.zoomable()){
31249             this.scale = this.startScale;
31250             return;
31251         }
31252         
31253         this.draw();
31254         
31255     },
31256     
31257     onTouchEnd : function(e)
31258     {
31259         this.pinching = false;
31260         this.dragable = false;
31261         
31262     },
31263     
31264     process : function(file, crop)
31265     {
31266         if(this.loadMask){
31267             this.maskEl.mask(this.loadingText);
31268         }
31269         
31270         this.xhr = new XMLHttpRequest();
31271         
31272         file.xhr = this.xhr;
31273
31274         this.xhr.open(this.method, this.url, true);
31275         
31276         var headers = {
31277             "Accept": "application/json",
31278             "Cache-Control": "no-cache",
31279             "X-Requested-With": "XMLHttpRequest"
31280         };
31281         
31282         for (var headerName in headers) {
31283             var headerValue = headers[headerName];
31284             if (headerValue) {
31285                 this.xhr.setRequestHeader(headerName, headerValue);
31286             }
31287         }
31288         
31289         var _this = this;
31290         
31291         this.xhr.onload = function()
31292         {
31293             _this.xhrOnLoad(_this.xhr);
31294         }
31295         
31296         this.xhr.onerror = function()
31297         {
31298             _this.xhrOnError(_this.xhr);
31299         }
31300         
31301         var formData = new FormData();
31302
31303         formData.append('returnHTML', 'NO');
31304         
31305         if(crop){
31306             formData.append('crop', crop);
31307         }
31308         
31309         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
31310             formData.append(this.paramName, file, file.name);
31311         }
31312         
31313         if(typeof(file.filename) != 'undefined'){
31314             formData.append('filename', file.filename);
31315         }
31316         
31317         if(typeof(file.mimetype) != 'undefined'){
31318             formData.append('mimetype', file.mimetype);
31319         }
31320         
31321         if(this.fireEvent('arrange', this, formData) != false){
31322             this.xhr.send(formData);
31323         };
31324     },
31325     
31326     xhrOnLoad : function(xhr)
31327     {
31328         if(this.loadMask){
31329             this.maskEl.unmask();
31330         }
31331         
31332         if (xhr.readyState !== 4) {
31333             this.fireEvent('exception', this, xhr);
31334             return;
31335         }
31336
31337         var response = Roo.decode(xhr.responseText);
31338         
31339         if(!response.success){
31340             this.fireEvent('exception', this, xhr);
31341             return;
31342         }
31343         
31344         var response = Roo.decode(xhr.responseText);
31345         
31346         this.fireEvent('upload', this, response);
31347         
31348     },
31349     
31350     xhrOnError : function()
31351     {
31352         if(this.loadMask){
31353             this.maskEl.unmask();
31354         }
31355         
31356         Roo.log('xhr on error');
31357         
31358         var response = Roo.decode(xhr.responseText);
31359           
31360         Roo.log(response);
31361         
31362     },
31363     
31364     prepare : function(file)
31365     {   
31366         if(this.loadMask){
31367             this.maskEl.mask(this.loadingText);
31368         }
31369         
31370         this.file = false;
31371         this.exif = {};
31372         
31373         if(typeof(file) === 'string'){
31374             this.loadCanvas(file);
31375             return;
31376         }
31377         
31378         if(!file || !this.urlAPI){
31379             return;
31380         }
31381         
31382         this.file = file;
31383         this.cropType = file.type;
31384         
31385         var _this = this;
31386         
31387         if(this.fireEvent('prepare', this, this.file) != false){
31388             
31389             var reader = new FileReader();
31390             
31391             reader.onload = function (e) {
31392                 if (e.target.error) {
31393                     Roo.log(e.target.error);
31394                     return;
31395                 }
31396                 
31397                 var buffer = e.target.result,
31398                     dataView = new DataView(buffer),
31399                     offset = 2,
31400                     maxOffset = dataView.byteLength - 4,
31401                     markerBytes,
31402                     markerLength;
31403                 
31404                 if (dataView.getUint16(0) === 0xffd8) {
31405                     while (offset < maxOffset) {
31406                         markerBytes = dataView.getUint16(offset);
31407                         
31408                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31409                             markerLength = dataView.getUint16(offset + 2) + 2;
31410                             if (offset + markerLength > dataView.byteLength) {
31411                                 Roo.log('Invalid meta data: Invalid segment size.');
31412                                 break;
31413                             }
31414                             
31415                             if(markerBytes == 0xffe1){
31416                                 _this.parseExifData(
31417                                     dataView,
31418                                     offset,
31419                                     markerLength
31420                                 );
31421                             }
31422                             
31423                             offset += markerLength;
31424                             
31425                             continue;
31426                         }
31427                         
31428                         break;
31429                     }
31430                     
31431                 }
31432                 
31433                 var url = _this.urlAPI.createObjectURL(_this.file);
31434                 
31435                 _this.loadCanvas(url);
31436                 
31437                 return;
31438             }
31439             
31440             reader.readAsArrayBuffer(this.file);
31441             
31442         }
31443         
31444     },
31445     
31446     parseExifData : function(dataView, offset, length)
31447     {
31448         var tiffOffset = offset + 10,
31449             littleEndian,
31450             dirOffset;
31451     
31452         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31453             // No Exif data, might be XMP data instead
31454             return;
31455         }
31456         
31457         // Check for the ASCII code for "Exif" (0x45786966):
31458         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31459             // No Exif data, might be XMP data instead
31460             return;
31461         }
31462         if (tiffOffset + 8 > dataView.byteLength) {
31463             Roo.log('Invalid Exif data: Invalid segment size.');
31464             return;
31465         }
31466         // Check for the two null bytes:
31467         if (dataView.getUint16(offset + 8) !== 0x0000) {
31468             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31469             return;
31470         }
31471         // Check the byte alignment:
31472         switch (dataView.getUint16(tiffOffset)) {
31473         case 0x4949:
31474             littleEndian = true;
31475             break;
31476         case 0x4D4D:
31477             littleEndian = false;
31478             break;
31479         default:
31480             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31481             return;
31482         }
31483         // Check for the TIFF tag marker (0x002A):
31484         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31485             Roo.log('Invalid Exif data: Missing TIFF marker.');
31486             return;
31487         }
31488         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31489         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31490         
31491         this.parseExifTags(
31492             dataView,
31493             tiffOffset,
31494             tiffOffset + dirOffset,
31495             littleEndian
31496         );
31497     },
31498     
31499     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31500     {
31501         var tagsNumber,
31502             dirEndOffset,
31503             i;
31504         if (dirOffset + 6 > dataView.byteLength) {
31505             Roo.log('Invalid Exif data: Invalid directory offset.');
31506             return;
31507         }
31508         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31509         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31510         if (dirEndOffset + 4 > dataView.byteLength) {
31511             Roo.log('Invalid Exif data: Invalid directory size.');
31512             return;
31513         }
31514         for (i = 0; i < tagsNumber; i += 1) {
31515             this.parseExifTag(
31516                 dataView,
31517                 tiffOffset,
31518                 dirOffset + 2 + 12 * i, // tag offset
31519                 littleEndian
31520             );
31521         }
31522         // Return the offset to the next directory:
31523         return dataView.getUint32(dirEndOffset, littleEndian);
31524     },
31525     
31526     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31527     {
31528         var tag = dataView.getUint16(offset, littleEndian);
31529         
31530         this.exif[tag] = this.getExifValue(
31531             dataView,
31532             tiffOffset,
31533             offset,
31534             dataView.getUint16(offset + 2, littleEndian), // tag type
31535             dataView.getUint32(offset + 4, littleEndian), // tag length
31536             littleEndian
31537         );
31538     },
31539     
31540     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31541     {
31542         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31543             tagSize,
31544             dataOffset,
31545             values,
31546             i,
31547             str,
31548             c;
31549     
31550         if (!tagType) {
31551             Roo.log('Invalid Exif data: Invalid tag type.');
31552             return;
31553         }
31554         
31555         tagSize = tagType.size * length;
31556         // Determine if the value is contained in the dataOffset bytes,
31557         // or if the value at the dataOffset is a pointer to the actual data:
31558         dataOffset = tagSize > 4 ?
31559                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31560         if (dataOffset + tagSize > dataView.byteLength) {
31561             Roo.log('Invalid Exif data: Invalid data offset.');
31562             return;
31563         }
31564         if (length === 1) {
31565             return tagType.getValue(dataView, dataOffset, littleEndian);
31566         }
31567         values = [];
31568         for (i = 0; i < length; i += 1) {
31569             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31570         }
31571         
31572         if (tagType.ascii) {
31573             str = '';
31574             // Concatenate the chars:
31575             for (i = 0; i < values.length; i += 1) {
31576                 c = values[i];
31577                 // Ignore the terminating NULL byte(s):
31578                 if (c === '\u0000') {
31579                     break;
31580                 }
31581                 str += c;
31582             }
31583             return str;
31584         }
31585         return values;
31586     }
31587     
31588 });
31589
31590 Roo.apply(Roo.bootstrap.UploadCropbox, {
31591     tags : {
31592         'Orientation': 0x0112
31593     },
31594     
31595     Orientation: {
31596             1: 0, //'top-left',
31597 //            2: 'top-right',
31598             3: 180, //'bottom-right',
31599 //            4: 'bottom-left',
31600 //            5: 'left-top',
31601             6: 90, //'right-top',
31602 //            7: 'right-bottom',
31603             8: 270 //'left-bottom'
31604     },
31605     
31606     exifTagTypes : {
31607         // byte, 8-bit unsigned int:
31608         1: {
31609             getValue: function (dataView, dataOffset) {
31610                 return dataView.getUint8(dataOffset);
31611             },
31612             size: 1
31613         },
31614         // ascii, 8-bit byte:
31615         2: {
31616             getValue: function (dataView, dataOffset) {
31617                 return String.fromCharCode(dataView.getUint8(dataOffset));
31618             },
31619             size: 1,
31620             ascii: true
31621         },
31622         // short, 16 bit int:
31623         3: {
31624             getValue: function (dataView, dataOffset, littleEndian) {
31625                 return dataView.getUint16(dataOffset, littleEndian);
31626             },
31627             size: 2
31628         },
31629         // long, 32 bit int:
31630         4: {
31631             getValue: function (dataView, dataOffset, littleEndian) {
31632                 return dataView.getUint32(dataOffset, littleEndian);
31633             },
31634             size: 4
31635         },
31636         // rational = two long values, first is numerator, second is denominator:
31637         5: {
31638             getValue: function (dataView, dataOffset, littleEndian) {
31639                 return dataView.getUint32(dataOffset, littleEndian) /
31640                     dataView.getUint32(dataOffset + 4, littleEndian);
31641             },
31642             size: 8
31643         },
31644         // slong, 32 bit signed int:
31645         9: {
31646             getValue: function (dataView, dataOffset, littleEndian) {
31647                 return dataView.getInt32(dataOffset, littleEndian);
31648             },
31649             size: 4
31650         },
31651         // srational, two slongs, first is numerator, second is denominator:
31652         10: {
31653             getValue: function (dataView, dataOffset, littleEndian) {
31654                 return dataView.getInt32(dataOffset, littleEndian) /
31655                     dataView.getInt32(dataOffset + 4, littleEndian);
31656             },
31657             size: 8
31658         }
31659     },
31660     
31661     footer : {
31662         STANDARD : [
31663             {
31664                 tag : 'div',
31665                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31666                 action : 'rotate-left',
31667                 cn : [
31668                     {
31669                         tag : 'button',
31670                         cls : 'btn btn-default',
31671                         html : '<i class="fa fa-undo"></i>'
31672                     }
31673                 ]
31674             },
31675             {
31676                 tag : 'div',
31677                 cls : 'btn-group roo-upload-cropbox-picture',
31678                 action : 'picture',
31679                 cn : [
31680                     {
31681                         tag : 'button',
31682                         cls : 'btn btn-default',
31683                         html : '<i class="fa fa-picture-o"></i>'
31684                     }
31685                 ]
31686             },
31687             {
31688                 tag : 'div',
31689                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31690                 action : 'rotate-right',
31691                 cn : [
31692                     {
31693                         tag : 'button',
31694                         cls : 'btn btn-default',
31695                         html : '<i class="fa fa-repeat"></i>'
31696                     }
31697                 ]
31698             }
31699         ],
31700         DOCUMENT : [
31701             {
31702                 tag : 'div',
31703                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31704                 action : 'rotate-left',
31705                 cn : [
31706                     {
31707                         tag : 'button',
31708                         cls : 'btn btn-default',
31709                         html : '<i class="fa fa-undo"></i>'
31710                     }
31711                 ]
31712             },
31713             {
31714                 tag : 'div',
31715                 cls : 'btn-group roo-upload-cropbox-download',
31716                 action : 'download',
31717                 cn : [
31718                     {
31719                         tag : 'button',
31720                         cls : 'btn btn-default',
31721                         html : '<i class="fa fa-download"></i>'
31722                     }
31723                 ]
31724             },
31725             {
31726                 tag : 'div',
31727                 cls : 'btn-group roo-upload-cropbox-crop',
31728                 action : 'crop',
31729                 cn : [
31730                     {
31731                         tag : 'button',
31732                         cls : 'btn btn-default',
31733                         html : '<i class="fa fa-crop"></i>'
31734                     }
31735                 ]
31736             },
31737             {
31738                 tag : 'div',
31739                 cls : 'btn-group roo-upload-cropbox-trash',
31740                 action : 'trash',
31741                 cn : [
31742                     {
31743                         tag : 'button',
31744                         cls : 'btn btn-default',
31745                         html : '<i class="fa fa-trash"></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         ROTATOR : [
31763             {
31764                 tag : 'div',
31765                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31766                 action : 'rotate-left',
31767                 cn : [
31768                     {
31769                         tag : 'button',
31770                         cls : 'btn btn-default',
31771                         html : '<i class="fa fa-undo"></i>'
31772                     }
31773                 ]
31774             },
31775             {
31776                 tag : 'div',
31777                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31778                 action : 'rotate-right',
31779                 cn : [
31780                     {
31781                         tag : 'button',
31782                         cls : 'btn btn-default',
31783                         html : '<i class="fa fa-repeat"></i>'
31784                     }
31785                 ]
31786             }
31787         ]
31788     }
31789 });
31790
31791 /*
31792 * Licence: LGPL
31793 */
31794
31795 /**
31796  * @class Roo.bootstrap.DocumentManager
31797  * @extends Roo.bootstrap.Component
31798  * Bootstrap DocumentManager class
31799  * @cfg {String} paramName default 'imageUpload'
31800  * @cfg {String} toolTipName default 'filename'
31801  * @cfg {String} method default POST
31802  * @cfg {String} url action url
31803  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31804  * @cfg {Boolean} multiple multiple upload default true
31805  * @cfg {Number} thumbSize default 300
31806  * @cfg {String} fieldLabel
31807  * @cfg {Number} labelWidth default 4
31808  * @cfg {String} labelAlign (left|top) default left
31809  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31810 * @cfg {Number} labellg set the width of label (1-12)
31811  * @cfg {Number} labelmd set the width of label (1-12)
31812  * @cfg {Number} labelsm set the width of label (1-12)
31813  * @cfg {Number} labelxs set the width of label (1-12)
31814  * 
31815  * @constructor
31816  * Create a new DocumentManager
31817  * @param {Object} config The config object
31818  */
31819
31820 Roo.bootstrap.DocumentManager = function(config){
31821     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31822     
31823     this.files = [];
31824     this.delegates = [];
31825     
31826     this.addEvents({
31827         /**
31828          * @event initial
31829          * Fire when initial the DocumentManager
31830          * @param {Roo.bootstrap.DocumentManager} this
31831          */
31832         "initial" : true,
31833         /**
31834          * @event inspect
31835          * inspect selected file
31836          * @param {Roo.bootstrap.DocumentManager} this
31837          * @param {File} file
31838          */
31839         "inspect" : true,
31840         /**
31841          * @event exception
31842          * Fire when xhr load exception
31843          * @param {Roo.bootstrap.DocumentManager} this
31844          * @param {XMLHttpRequest} xhr
31845          */
31846         "exception" : true,
31847         /**
31848          * @event afterupload
31849          * Fire when xhr load exception
31850          * @param {Roo.bootstrap.DocumentManager} this
31851          * @param {XMLHttpRequest} xhr
31852          */
31853         "afterupload" : true,
31854         /**
31855          * @event prepare
31856          * prepare the form data
31857          * @param {Roo.bootstrap.DocumentManager} this
31858          * @param {Object} formData
31859          */
31860         "prepare" : true,
31861         /**
31862          * @event remove
31863          * Fire when remove the file
31864          * @param {Roo.bootstrap.DocumentManager} this
31865          * @param {Object} file
31866          */
31867         "remove" : true,
31868         /**
31869          * @event refresh
31870          * Fire after refresh the file
31871          * @param {Roo.bootstrap.DocumentManager} this
31872          */
31873         "refresh" : true,
31874         /**
31875          * @event click
31876          * Fire after click the image
31877          * @param {Roo.bootstrap.DocumentManager} this
31878          * @param {Object} file
31879          */
31880         "click" : true,
31881         /**
31882          * @event edit
31883          * Fire when upload a image and editable set to true
31884          * @param {Roo.bootstrap.DocumentManager} this
31885          * @param {Object} file
31886          */
31887         "edit" : true,
31888         /**
31889          * @event beforeselectfile
31890          * Fire before select file
31891          * @param {Roo.bootstrap.DocumentManager} this
31892          */
31893         "beforeselectfile" : true,
31894         /**
31895          * @event process
31896          * Fire before process file
31897          * @param {Roo.bootstrap.DocumentManager} this
31898          * @param {Object} file
31899          */
31900         "process" : true,
31901         /**
31902          * @event previewrendered
31903          * Fire when preview rendered
31904          * @param {Roo.bootstrap.DocumentManager} this
31905          * @param {Object} file
31906          */
31907         "previewrendered" : true,
31908         /**
31909          */
31910         "previewResize" : true
31911         
31912     });
31913 };
31914
31915 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31916     
31917     boxes : 0,
31918     inputName : '',
31919     thumbSize : 300,
31920     multiple : true,
31921     files : false,
31922     method : 'POST',
31923     url : '',
31924     paramName : 'imageUpload',
31925     toolTipName : 'filename',
31926     fieldLabel : '',
31927     labelWidth : 4,
31928     labelAlign : 'left',
31929     editable : true,
31930     delegates : false,
31931     xhr : false, 
31932     
31933     labellg : 0,
31934     labelmd : 0,
31935     labelsm : 0,
31936     labelxs : 0,
31937     
31938     getAutoCreate : function()
31939     {   
31940         var managerWidget = {
31941             tag : 'div',
31942             cls : 'roo-document-manager',
31943             cn : [
31944                 {
31945                     tag : 'input',
31946                     cls : 'roo-document-manager-selector',
31947                     type : 'file'
31948                 },
31949                 {
31950                     tag : 'div',
31951                     cls : 'roo-document-manager-uploader',
31952                     cn : [
31953                         {
31954                             tag : 'div',
31955                             cls : 'roo-document-manager-upload-btn',
31956                             html : '<i class="fa fa-plus"></i>'
31957                         }
31958                     ]
31959                     
31960                 }
31961             ]
31962         };
31963         
31964         var content = [
31965             {
31966                 tag : 'div',
31967                 cls : 'column col-md-12',
31968                 cn : managerWidget
31969             }
31970         ];
31971         
31972         if(this.fieldLabel.length){
31973             
31974             content = [
31975                 {
31976                     tag : 'div',
31977                     cls : 'column col-md-12',
31978                     html : this.fieldLabel
31979                 },
31980                 {
31981                     tag : 'div',
31982                     cls : 'column col-md-12',
31983                     cn : managerWidget
31984                 }
31985             ];
31986
31987             if(this.labelAlign == 'left'){
31988                 content = [
31989                     {
31990                         tag : 'div',
31991                         cls : 'column',
31992                         html : this.fieldLabel
31993                     },
31994                     {
31995                         tag : 'div',
31996                         cls : 'column',
31997                         cn : managerWidget
31998                     }
31999                 ];
32000                 
32001                 if(this.labelWidth > 12){
32002                     content[0].style = "width: " + this.labelWidth + 'px';
32003                 }
32004
32005                 if(this.labelWidth < 13 && this.labelmd == 0){
32006                     this.labelmd = this.labelWidth;
32007                 }
32008
32009                 if(this.labellg > 0){
32010                     content[0].cls += ' col-lg-' + this.labellg;
32011                     content[1].cls += ' col-lg-' + (12 - this.labellg);
32012                 }
32013
32014                 if(this.labelmd > 0){
32015                     content[0].cls += ' col-md-' + this.labelmd;
32016                     content[1].cls += ' col-md-' + (12 - this.labelmd);
32017                 }
32018
32019                 if(this.labelsm > 0){
32020                     content[0].cls += ' col-sm-' + this.labelsm;
32021                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
32022                 }
32023
32024                 if(this.labelxs > 0){
32025                     content[0].cls += ' col-xs-' + this.labelxs;
32026                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
32027                 }
32028                 
32029             }
32030         }
32031         
32032         var cfg = {
32033             tag : 'div',
32034             cls : 'row clearfix',
32035             cn : content
32036         };
32037         
32038         return cfg;
32039         
32040     },
32041     
32042     initEvents : function()
32043     {
32044         this.managerEl = this.el.select('.roo-document-manager', true).first();
32045         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32046         
32047         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
32048         this.selectorEl.hide();
32049         
32050         if(this.multiple){
32051             this.selectorEl.attr('multiple', 'multiple');
32052         }
32053         
32054         this.selectorEl.on('change', this.onFileSelected, this);
32055         
32056         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
32057         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32058         
32059         this.uploader.on('click', this.onUploaderClick, this);
32060         
32061         this.renderProgressDialog();
32062         
32063         var _this = this;
32064         
32065         window.addEventListener("resize", function() { _this.refresh(); } );
32066         
32067         this.fireEvent('initial', this);
32068     },
32069     
32070     renderProgressDialog : function()
32071     {
32072         var _this = this;
32073         
32074         this.progressDialog = new Roo.bootstrap.Modal({
32075             cls : 'roo-document-manager-progress-dialog',
32076             allow_close : false,
32077             animate : false,
32078             title : '',
32079             buttons : [
32080                 {
32081                     name  :'cancel',
32082                     weight : 'danger',
32083                     html : 'Cancel'
32084                 }
32085             ], 
32086             listeners : { 
32087                 btnclick : function() {
32088                     _this.uploadCancel();
32089                     this.hide();
32090                 }
32091             }
32092         });
32093          
32094         this.progressDialog.render(Roo.get(document.body));
32095          
32096         this.progress = new Roo.bootstrap.Progress({
32097             cls : 'roo-document-manager-progress',
32098             active : true,
32099             striped : true
32100         });
32101         
32102         this.progress.render(this.progressDialog.getChildContainer());
32103         
32104         this.progressBar = new Roo.bootstrap.ProgressBar({
32105             cls : 'roo-document-manager-progress-bar',
32106             aria_valuenow : 0,
32107             aria_valuemin : 0,
32108             aria_valuemax : 12,
32109             panel : 'success'
32110         });
32111         
32112         this.progressBar.render(this.progress.getChildContainer());
32113     },
32114     
32115     onUploaderClick : function(e)
32116     {
32117         e.preventDefault();
32118      
32119         if(this.fireEvent('beforeselectfile', this) != false){
32120             this.selectorEl.dom.click();
32121         }
32122         
32123     },
32124     
32125     onFileSelected : function(e)
32126     {
32127         e.preventDefault();
32128         
32129         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
32130             return;
32131         }
32132         
32133         Roo.each(this.selectorEl.dom.files, function(file){
32134             if(this.fireEvent('inspect', this, file) != false){
32135                 this.files.push(file);
32136             }
32137         }, this);
32138         
32139         this.queue();
32140         
32141     },
32142     
32143     queue : function()
32144     {
32145         this.selectorEl.dom.value = '';
32146         
32147         if(!this.files || !this.files.length){
32148             return;
32149         }
32150         
32151         if(this.boxes > 0 && this.files.length > this.boxes){
32152             this.files = this.files.slice(0, this.boxes);
32153         }
32154         
32155         this.uploader.show();
32156         
32157         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32158             this.uploader.hide();
32159         }
32160         
32161         var _this = this;
32162         
32163         var files = [];
32164         
32165         var docs = [];
32166         
32167         Roo.each(this.files, function(file){
32168             
32169             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32170                 var f = this.renderPreview(file);
32171                 files.push(f);
32172                 return;
32173             }
32174             
32175             if(file.type.indexOf('image') != -1){
32176                 this.delegates.push(
32177                     (function(){
32178                         _this.process(file);
32179                     }).createDelegate(this)
32180                 );
32181         
32182                 return;
32183             }
32184             
32185             docs.push(
32186                 (function(){
32187                     _this.process(file);
32188                 }).createDelegate(this)
32189             );
32190             
32191         }, this);
32192         
32193         this.files = files;
32194         
32195         this.delegates = this.delegates.concat(docs);
32196         
32197         if(!this.delegates.length){
32198             this.refresh();
32199             return;
32200         }
32201         
32202         this.progressBar.aria_valuemax = this.delegates.length;
32203         
32204         this.arrange();
32205         
32206         return;
32207     },
32208     
32209     arrange : function()
32210     {
32211         if(!this.delegates.length){
32212             this.progressDialog.hide();
32213             this.refresh();
32214             return;
32215         }
32216         
32217         var delegate = this.delegates.shift();
32218         
32219         this.progressDialog.show();
32220         
32221         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
32222         
32223         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
32224         
32225         delegate();
32226     },
32227     
32228     refresh : function()
32229     {
32230         this.uploader.show();
32231         
32232         if(this.boxes > 0 && this.files.length > this.boxes - 1){
32233             this.uploader.hide();
32234         }
32235         
32236         Roo.isTouch ? this.closable(false) : this.closable(true);
32237         
32238         this.fireEvent('refresh', this);
32239     },
32240     
32241     onRemove : function(e, el, o)
32242     {
32243         e.preventDefault();
32244         
32245         this.fireEvent('remove', this, o);
32246         
32247     },
32248     
32249     remove : function(o)
32250     {
32251         var files = [];
32252         
32253         Roo.each(this.files, function(file){
32254             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
32255                 files.push(file);
32256                 return;
32257             }
32258
32259             o.target.remove();
32260
32261         }, this);
32262         
32263         this.files = files;
32264         
32265         this.refresh();
32266     },
32267     
32268     clear : function()
32269     {
32270         Roo.each(this.files, function(file){
32271             if(!file.target){
32272                 return;
32273             }
32274             
32275             file.target.remove();
32276
32277         }, this);
32278         
32279         this.files = [];
32280         
32281         this.refresh();
32282     },
32283     
32284     onClick : function(e, el, o)
32285     {
32286         e.preventDefault();
32287         
32288         this.fireEvent('click', this, o);
32289         
32290     },
32291     
32292     closable : function(closable)
32293     {
32294         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
32295             
32296             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
32297             
32298             if(closable){
32299                 el.show();
32300                 return;
32301             }
32302             
32303             el.hide();
32304             
32305         }, this);
32306     },
32307     
32308     xhrOnLoad : function(xhr)
32309     {
32310         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32311             el.remove();
32312         }, this);
32313         
32314         if (xhr.readyState !== 4) {
32315             this.arrange();
32316             this.fireEvent('exception', this, xhr);
32317             return;
32318         }
32319
32320         var response = Roo.decode(xhr.responseText);
32321         
32322         if(!response.success){
32323             this.arrange();
32324             this.fireEvent('exception', this, xhr);
32325             return;
32326         }
32327         
32328         var file = this.renderPreview(response.data);
32329         
32330         this.files.push(file);
32331         
32332         this.arrange();
32333         
32334         this.fireEvent('afterupload', this, xhr);
32335         
32336     },
32337     
32338     xhrOnError : function(xhr)
32339     {
32340         Roo.log('xhr on error');
32341         
32342         var response = Roo.decode(xhr.responseText);
32343           
32344         Roo.log(response);
32345         
32346         this.arrange();
32347     },
32348     
32349     process : function(file)
32350     {
32351         if(this.fireEvent('process', this, file) !== false){
32352             if(this.editable && file.type.indexOf('image') != -1){
32353                 this.fireEvent('edit', this, file);
32354                 return;
32355             }
32356
32357             this.uploadStart(file, false);
32358
32359             return;
32360         }
32361         
32362     },
32363     
32364     uploadStart : function(file, crop)
32365     {
32366         this.xhr = new XMLHttpRequest();
32367         
32368         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32369             this.arrange();
32370             return;
32371         }
32372         
32373         file.xhr = this.xhr;
32374             
32375         this.managerEl.createChild({
32376             tag : 'div',
32377             cls : 'roo-document-manager-loading',
32378             cn : [
32379                 {
32380                     tag : 'div',
32381                     tooltip : file.name,
32382                     cls : 'roo-document-manager-thumb',
32383                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32384                 }
32385             ]
32386
32387         });
32388
32389         this.xhr.open(this.method, this.url, true);
32390         
32391         var headers = {
32392             "Accept": "application/json",
32393             "Cache-Control": "no-cache",
32394             "X-Requested-With": "XMLHttpRequest"
32395         };
32396         
32397         for (var headerName in headers) {
32398             var headerValue = headers[headerName];
32399             if (headerValue) {
32400                 this.xhr.setRequestHeader(headerName, headerValue);
32401             }
32402         }
32403         
32404         var _this = this;
32405         
32406         this.xhr.onload = function()
32407         {
32408             _this.xhrOnLoad(_this.xhr);
32409         }
32410         
32411         this.xhr.onerror = function()
32412         {
32413             _this.xhrOnError(_this.xhr);
32414         }
32415         
32416         var formData = new FormData();
32417
32418         formData.append('returnHTML', 'NO');
32419         
32420         if(crop){
32421             formData.append('crop', crop);
32422         }
32423         
32424         formData.append(this.paramName, file, file.name);
32425         
32426         var options = {
32427             file : file, 
32428             manually : false
32429         };
32430         
32431         if(this.fireEvent('prepare', this, formData, options) != false){
32432             
32433             if(options.manually){
32434                 return;
32435             }
32436             
32437             this.xhr.send(formData);
32438             return;
32439         };
32440         
32441         this.uploadCancel();
32442     },
32443     
32444     uploadCancel : function()
32445     {
32446         if (this.xhr) {
32447             this.xhr.abort();
32448         }
32449         
32450         this.delegates = [];
32451         
32452         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32453             el.remove();
32454         }, this);
32455         
32456         this.arrange();
32457     },
32458     
32459     renderPreview : function(file)
32460     {
32461         if(typeof(file.target) != 'undefined' && file.target){
32462             return file;
32463         }
32464         
32465         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32466         
32467         var previewEl = this.managerEl.createChild({
32468             tag : 'div',
32469             cls : 'roo-document-manager-preview',
32470             cn : [
32471                 {
32472                     tag : 'div',
32473                     tooltip : file[this.toolTipName],
32474                     cls : 'roo-document-manager-thumb',
32475                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32476                 },
32477                 {
32478                     tag : 'button',
32479                     cls : 'close',
32480                     html : '<i class="fa fa-times-circle"></i>'
32481                 }
32482             ]
32483         });
32484
32485         var close = previewEl.select('button.close', true).first();
32486
32487         close.on('click', this.onRemove, this, file);
32488
32489         file.target = previewEl;
32490
32491         var image = previewEl.select('img', true).first();
32492         
32493         var _this = this;
32494         
32495         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32496         
32497         image.on('click', this.onClick, this, file);
32498         
32499         this.fireEvent('previewrendered', this, file);
32500         
32501         return file;
32502         
32503     },
32504     
32505     onPreviewLoad : function(file, image)
32506     {
32507         if(typeof(file.target) == 'undefined' || !file.target){
32508             return;
32509         }
32510         
32511         var width = image.dom.naturalWidth || image.dom.width;
32512         var height = image.dom.naturalHeight || image.dom.height;
32513         
32514         if(!this.previewResize) {
32515             return;
32516         }
32517         
32518         if(width > height){
32519             file.target.addClass('wide');
32520             return;
32521         }
32522         
32523         file.target.addClass('tall');
32524         return;
32525         
32526     },
32527     
32528     uploadFromSource : function(file, crop)
32529     {
32530         this.xhr = new XMLHttpRequest();
32531         
32532         this.managerEl.createChild({
32533             tag : 'div',
32534             cls : 'roo-document-manager-loading',
32535             cn : [
32536                 {
32537                     tag : 'div',
32538                     tooltip : file.name,
32539                     cls : 'roo-document-manager-thumb',
32540                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32541                 }
32542             ]
32543
32544         });
32545
32546         this.xhr.open(this.method, this.url, true);
32547         
32548         var headers = {
32549             "Accept": "application/json",
32550             "Cache-Control": "no-cache",
32551             "X-Requested-With": "XMLHttpRequest"
32552         };
32553         
32554         for (var headerName in headers) {
32555             var headerValue = headers[headerName];
32556             if (headerValue) {
32557                 this.xhr.setRequestHeader(headerName, headerValue);
32558             }
32559         }
32560         
32561         var _this = this;
32562         
32563         this.xhr.onload = function()
32564         {
32565             _this.xhrOnLoad(_this.xhr);
32566         }
32567         
32568         this.xhr.onerror = function()
32569         {
32570             _this.xhrOnError(_this.xhr);
32571         }
32572         
32573         var formData = new FormData();
32574
32575         formData.append('returnHTML', 'NO');
32576         
32577         formData.append('crop', crop);
32578         
32579         if(typeof(file.filename) != 'undefined'){
32580             formData.append('filename', file.filename);
32581         }
32582         
32583         if(typeof(file.mimetype) != 'undefined'){
32584             formData.append('mimetype', file.mimetype);
32585         }
32586         
32587         Roo.log(formData);
32588         
32589         if(this.fireEvent('prepare', this, formData) != false){
32590             this.xhr.send(formData);
32591         };
32592     }
32593 });
32594
32595 /*
32596 * Licence: LGPL
32597 */
32598
32599 /**
32600  * @class Roo.bootstrap.DocumentViewer
32601  * @extends Roo.bootstrap.Component
32602  * Bootstrap DocumentViewer class
32603  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32604  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32605  * 
32606  * @constructor
32607  * Create a new DocumentViewer
32608  * @param {Object} config The config object
32609  */
32610
32611 Roo.bootstrap.DocumentViewer = function(config){
32612     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32613     
32614     this.addEvents({
32615         /**
32616          * @event initial
32617          * Fire after initEvent
32618          * @param {Roo.bootstrap.DocumentViewer} this
32619          */
32620         "initial" : true,
32621         /**
32622          * @event click
32623          * Fire after click
32624          * @param {Roo.bootstrap.DocumentViewer} this
32625          */
32626         "click" : true,
32627         /**
32628          * @event download
32629          * Fire after download button
32630          * @param {Roo.bootstrap.DocumentViewer} this
32631          */
32632         "download" : true,
32633         /**
32634          * @event trash
32635          * Fire after trash button
32636          * @param {Roo.bootstrap.DocumentViewer} this
32637          */
32638         "trash" : true
32639         
32640     });
32641 };
32642
32643 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32644     
32645     showDownload : true,
32646     
32647     showTrash : true,
32648     
32649     getAutoCreate : function()
32650     {
32651         var cfg = {
32652             tag : 'div',
32653             cls : 'roo-document-viewer',
32654             cn : [
32655                 {
32656                     tag : 'div',
32657                     cls : 'roo-document-viewer-body',
32658                     cn : [
32659                         {
32660                             tag : 'div',
32661                             cls : 'roo-document-viewer-thumb',
32662                             cn : [
32663                                 {
32664                                     tag : 'img',
32665                                     cls : 'roo-document-viewer-image'
32666                                 }
32667                             ]
32668                         }
32669                     ]
32670                 },
32671                 {
32672                     tag : 'div',
32673                     cls : 'roo-document-viewer-footer',
32674                     cn : {
32675                         tag : 'div',
32676                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32677                         cn : [
32678                             {
32679                                 tag : 'div',
32680                                 cls : 'btn-group roo-document-viewer-download',
32681                                 cn : [
32682                                     {
32683                                         tag : 'button',
32684                                         cls : 'btn btn-default',
32685                                         html : '<i class="fa fa-download"></i>'
32686                                     }
32687                                 ]
32688                             },
32689                             {
32690                                 tag : 'div',
32691                                 cls : 'btn-group roo-document-viewer-trash',
32692                                 cn : [
32693                                     {
32694                                         tag : 'button',
32695                                         cls : 'btn btn-default',
32696                                         html : '<i class="fa fa-trash"></i>'
32697                                     }
32698                                 ]
32699                             }
32700                         ]
32701                     }
32702                 }
32703             ]
32704         };
32705         
32706         return cfg;
32707     },
32708     
32709     initEvents : function()
32710     {
32711         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32712         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32713         
32714         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32715         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32716         
32717         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32718         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32719         
32720         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32721         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32722         
32723         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32724         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32725         
32726         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32727         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32728         
32729         this.bodyEl.on('click', this.onClick, this);
32730         this.downloadBtn.on('click', this.onDownload, this);
32731         this.trashBtn.on('click', this.onTrash, this);
32732         
32733         this.downloadBtn.hide();
32734         this.trashBtn.hide();
32735         
32736         if(this.showDownload){
32737             this.downloadBtn.show();
32738         }
32739         
32740         if(this.showTrash){
32741             this.trashBtn.show();
32742         }
32743         
32744         if(!this.showDownload && !this.showTrash) {
32745             this.footerEl.hide();
32746         }
32747         
32748     },
32749     
32750     initial : function()
32751     {
32752         this.fireEvent('initial', this);
32753         
32754     },
32755     
32756     onClick : function(e)
32757     {
32758         e.preventDefault();
32759         
32760         this.fireEvent('click', this);
32761     },
32762     
32763     onDownload : function(e)
32764     {
32765         e.preventDefault();
32766         
32767         this.fireEvent('download', this);
32768     },
32769     
32770     onTrash : function(e)
32771     {
32772         e.preventDefault();
32773         
32774         this.fireEvent('trash', this);
32775     }
32776     
32777 });
32778 /*
32779  * - LGPL
32780  *
32781  * nav progress bar
32782  * 
32783  */
32784
32785 /**
32786  * @class Roo.bootstrap.NavProgressBar
32787  * @extends Roo.bootstrap.Component
32788  * Bootstrap NavProgressBar class
32789  * 
32790  * @constructor
32791  * Create a new nav progress bar
32792  * @param {Object} config The config object
32793  */
32794
32795 Roo.bootstrap.NavProgressBar = function(config){
32796     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32797
32798     this.bullets = this.bullets || [];
32799    
32800 //    Roo.bootstrap.NavProgressBar.register(this);
32801      this.addEvents({
32802         /**
32803              * @event changed
32804              * Fires when the active item changes
32805              * @param {Roo.bootstrap.NavProgressBar} this
32806              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32807              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32808          */
32809         'changed': true
32810      });
32811     
32812 };
32813
32814 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32815     
32816     bullets : [],
32817     barItems : [],
32818     
32819     getAutoCreate : function()
32820     {
32821         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32822         
32823         cfg = {
32824             tag : 'div',
32825             cls : 'roo-navigation-bar-group',
32826             cn : [
32827                 {
32828                     tag : 'div',
32829                     cls : 'roo-navigation-top-bar'
32830                 },
32831                 {
32832                     tag : 'div',
32833                     cls : 'roo-navigation-bullets-bar',
32834                     cn : [
32835                         {
32836                             tag : 'ul',
32837                             cls : 'roo-navigation-bar'
32838                         }
32839                     ]
32840                 },
32841                 
32842                 {
32843                     tag : 'div',
32844                     cls : 'roo-navigation-bottom-bar'
32845                 }
32846             ]
32847             
32848         };
32849         
32850         return cfg;
32851         
32852     },
32853     
32854     initEvents: function() 
32855     {
32856         
32857     },
32858     
32859     onRender : function(ct, position) 
32860     {
32861         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32862         
32863         if(this.bullets.length){
32864             Roo.each(this.bullets, function(b){
32865                this.addItem(b);
32866             }, this);
32867         }
32868         
32869         this.format();
32870         
32871     },
32872     
32873     addItem : function(cfg)
32874     {
32875         var item = new Roo.bootstrap.NavProgressItem(cfg);
32876         
32877         item.parentId = this.id;
32878         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32879         
32880         if(cfg.html){
32881             var top = new Roo.bootstrap.Element({
32882                 tag : 'div',
32883                 cls : 'roo-navigation-bar-text'
32884             });
32885             
32886             var bottom = new Roo.bootstrap.Element({
32887                 tag : 'div',
32888                 cls : 'roo-navigation-bar-text'
32889             });
32890             
32891             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32892             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32893             
32894             var topText = new Roo.bootstrap.Element({
32895                 tag : 'span',
32896                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32897             });
32898             
32899             var bottomText = new Roo.bootstrap.Element({
32900                 tag : 'span',
32901                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32902             });
32903             
32904             topText.onRender(top.el, null);
32905             bottomText.onRender(bottom.el, null);
32906             
32907             item.topEl = top;
32908             item.bottomEl = bottom;
32909         }
32910         
32911         this.barItems.push(item);
32912         
32913         return item;
32914     },
32915     
32916     getActive : function()
32917     {
32918         var active = false;
32919         
32920         Roo.each(this.barItems, function(v){
32921             
32922             if (!v.isActive()) {
32923                 return;
32924             }
32925             
32926             active = v;
32927             return false;
32928             
32929         });
32930         
32931         return active;
32932     },
32933     
32934     setActiveItem : function(item)
32935     {
32936         var prev = false;
32937         
32938         Roo.each(this.barItems, function(v){
32939             if (v.rid == item.rid) {
32940                 return ;
32941             }
32942             
32943             if (v.isActive()) {
32944                 v.setActive(false);
32945                 prev = v;
32946             }
32947         });
32948
32949         item.setActive(true);
32950         
32951         this.fireEvent('changed', this, item, prev);
32952     },
32953     
32954     getBarItem: function(rid)
32955     {
32956         var ret = false;
32957         
32958         Roo.each(this.barItems, function(e) {
32959             if (e.rid != rid) {
32960                 return;
32961             }
32962             
32963             ret =  e;
32964             return false;
32965         });
32966         
32967         return ret;
32968     },
32969     
32970     indexOfItem : function(item)
32971     {
32972         var index = false;
32973         
32974         Roo.each(this.barItems, function(v, i){
32975             
32976             if (v.rid != item.rid) {
32977                 return;
32978             }
32979             
32980             index = i;
32981             return false
32982         });
32983         
32984         return index;
32985     },
32986     
32987     setActiveNext : function()
32988     {
32989         var i = this.indexOfItem(this.getActive());
32990         
32991         if (i > this.barItems.length) {
32992             return;
32993         }
32994         
32995         this.setActiveItem(this.barItems[i+1]);
32996     },
32997     
32998     setActivePrev : function()
32999     {
33000         var i = this.indexOfItem(this.getActive());
33001         
33002         if (i  < 1) {
33003             return;
33004         }
33005         
33006         this.setActiveItem(this.barItems[i-1]);
33007     },
33008     
33009     format : function()
33010     {
33011         if(!this.barItems.length){
33012             return;
33013         }
33014      
33015         var width = 100 / this.barItems.length;
33016         
33017         Roo.each(this.barItems, function(i){
33018             i.el.setStyle('width', width + '%');
33019             i.topEl.el.setStyle('width', width + '%');
33020             i.bottomEl.el.setStyle('width', width + '%');
33021         }, this);
33022         
33023     }
33024     
33025 });
33026 /*
33027  * - LGPL
33028  *
33029  * Nav Progress Item
33030  * 
33031  */
33032
33033 /**
33034  * @class Roo.bootstrap.NavProgressItem
33035  * @extends Roo.bootstrap.Component
33036  * Bootstrap NavProgressItem class
33037  * @cfg {String} rid the reference id
33038  * @cfg {Boolean} active (true|false) Is item active default false
33039  * @cfg {Boolean} disabled (true|false) Is item active default false
33040  * @cfg {String} html
33041  * @cfg {String} position (top|bottom) text position default bottom
33042  * @cfg {String} icon show icon instead of number
33043  * 
33044  * @constructor
33045  * Create a new NavProgressItem
33046  * @param {Object} config The config object
33047  */
33048 Roo.bootstrap.NavProgressItem = function(config){
33049     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
33050     this.addEvents({
33051         // raw events
33052         /**
33053          * @event click
33054          * The raw click event for the entire grid.
33055          * @param {Roo.bootstrap.NavProgressItem} this
33056          * @param {Roo.EventObject} e
33057          */
33058         "click" : true
33059     });
33060    
33061 };
33062
33063 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
33064     
33065     rid : '',
33066     active : false,
33067     disabled : false,
33068     html : '',
33069     position : 'bottom',
33070     icon : false,
33071     
33072     getAutoCreate : function()
33073     {
33074         var iconCls = 'roo-navigation-bar-item-icon';
33075         
33076         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
33077         
33078         var cfg = {
33079             tag: 'li',
33080             cls: 'roo-navigation-bar-item',
33081             cn : [
33082                 {
33083                     tag : 'i',
33084                     cls : iconCls
33085                 }
33086             ]
33087         };
33088         
33089         if(this.active){
33090             cfg.cls += ' active';
33091         }
33092         if(this.disabled){
33093             cfg.cls += ' disabled';
33094         }
33095         
33096         return cfg;
33097     },
33098     
33099     disable : function()
33100     {
33101         this.setDisabled(true);
33102     },
33103     
33104     enable : function()
33105     {
33106         this.setDisabled(false);
33107     },
33108     
33109     initEvents: function() 
33110     {
33111         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
33112         
33113         this.iconEl.on('click', this.onClick, this);
33114     },
33115     
33116     onClick : function(e)
33117     {
33118         e.preventDefault();
33119         
33120         if(this.disabled){
33121             return;
33122         }
33123         
33124         if(this.fireEvent('click', this, e) === false){
33125             return;
33126         };
33127         
33128         this.parent().setActiveItem(this);
33129     },
33130     
33131     isActive: function () 
33132     {
33133         return this.active;
33134     },
33135     
33136     setActive : function(state)
33137     {
33138         if(this.active == state){
33139             return;
33140         }
33141         
33142         this.active = state;
33143         
33144         if (state) {
33145             this.el.addClass('active');
33146             return;
33147         }
33148         
33149         this.el.removeClass('active');
33150         
33151         return;
33152     },
33153     
33154     setDisabled : function(state)
33155     {
33156         if(this.disabled == state){
33157             return;
33158         }
33159         
33160         this.disabled = state;
33161         
33162         if (state) {
33163             this.el.addClass('disabled');
33164             return;
33165         }
33166         
33167         this.el.removeClass('disabled');
33168     },
33169     
33170     tooltipEl : function()
33171     {
33172         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
33173     }
33174 });
33175  
33176
33177  /*
33178  * - LGPL
33179  *
33180  * FieldLabel
33181  * 
33182  */
33183
33184 /**
33185  * @class Roo.bootstrap.FieldLabel
33186  * @extends Roo.bootstrap.Component
33187  * Bootstrap FieldLabel class
33188  * @cfg {String} html contents of the element
33189  * @cfg {String} tag tag of the element default label
33190  * @cfg {String} cls class of the element
33191  * @cfg {String} target label target 
33192  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
33193  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
33194  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
33195  * @cfg {String} iconTooltip default "This field is required"
33196  * @cfg {String} indicatorpos (left|right) default left
33197  * 
33198  * @constructor
33199  * Create a new FieldLabel
33200  * @param {Object} config The config object
33201  */
33202
33203 Roo.bootstrap.FieldLabel = function(config){
33204     Roo.bootstrap.Element.superclass.constructor.call(this, config);
33205     
33206     this.addEvents({
33207             /**
33208              * @event invalid
33209              * Fires after the field has been marked as invalid.
33210              * @param {Roo.form.FieldLabel} this
33211              * @param {String} msg The validation message
33212              */
33213             invalid : true,
33214             /**
33215              * @event valid
33216              * Fires after the field has been validated with no errors.
33217              * @param {Roo.form.FieldLabel} this
33218              */
33219             valid : true
33220         });
33221 };
33222
33223 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
33224     
33225     tag: 'label',
33226     cls: '',
33227     html: '',
33228     target: '',
33229     allowBlank : true,
33230     invalidClass : 'has-warning',
33231     validClass : 'has-success',
33232     iconTooltip : 'This field is required',
33233     indicatorpos : 'left',
33234     
33235     getAutoCreate : function(){
33236         
33237         var cls = "";
33238         if (!this.allowBlank) {
33239             cls  = "visible";
33240         }
33241         
33242         var cfg = {
33243             tag : this.tag,
33244             cls : 'roo-bootstrap-field-label ' + this.cls,
33245             for : this.target,
33246             cn : [
33247                 {
33248                     tag : 'i',
33249                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
33250                     tooltip : this.iconTooltip
33251                 },
33252                 {
33253                     tag : 'span',
33254                     html : this.html
33255                 }
33256             ] 
33257         };
33258         
33259         if(this.indicatorpos == 'right'){
33260             var cfg = {
33261                 tag : this.tag,
33262                 cls : 'roo-bootstrap-field-label ' + this.cls,
33263                 for : this.target,
33264                 cn : [
33265                     {
33266                         tag : 'span',
33267                         html : this.html
33268                     },
33269                     {
33270                         tag : 'i',
33271                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
33272                         tooltip : this.iconTooltip
33273                     }
33274                 ] 
33275             };
33276         }
33277         
33278         return cfg;
33279     },
33280     
33281     initEvents: function() 
33282     {
33283         Roo.bootstrap.Element.superclass.initEvents.call(this);
33284         
33285         this.indicator = this.indicatorEl();
33286         
33287         if(this.indicator){
33288             this.indicator.removeClass('visible');
33289             this.indicator.addClass('invisible');
33290         }
33291         
33292         Roo.bootstrap.FieldLabel.register(this);
33293     },
33294     
33295     indicatorEl : function()
33296     {
33297         var indicator = this.el.select('i.roo-required-indicator',true).first();
33298         
33299         if(!indicator){
33300             return false;
33301         }
33302         
33303         return indicator;
33304         
33305     },
33306     
33307     /**
33308      * Mark this field as valid
33309      */
33310     markValid : function()
33311     {
33312         if(this.indicator){
33313             this.indicator.removeClass('visible');
33314             this.indicator.addClass('invisible');
33315         }
33316         if (Roo.bootstrap.version == 3) {
33317             this.el.removeClass(this.invalidClass);
33318             this.el.addClass(this.validClass);
33319         } else {
33320             this.el.removeClass('is-invalid');
33321             this.el.addClass('is-valid');
33322         }
33323         
33324         
33325         this.fireEvent('valid', this);
33326     },
33327     
33328     /**
33329      * Mark this field as invalid
33330      * @param {String} msg The validation message
33331      */
33332     markInvalid : function(msg)
33333     {
33334         if(this.indicator){
33335             this.indicator.removeClass('invisible');
33336             this.indicator.addClass('visible');
33337         }
33338           if (Roo.bootstrap.version == 3) {
33339             this.el.removeClass(this.validClass);
33340             this.el.addClass(this.invalidClass);
33341         } else {
33342             this.el.removeClass('is-valid');
33343             this.el.addClass('is-invalid');
33344         }
33345         
33346         
33347         this.fireEvent('invalid', this, msg);
33348     }
33349     
33350    
33351 });
33352
33353 Roo.apply(Roo.bootstrap.FieldLabel, {
33354     
33355     groups: {},
33356     
33357      /**
33358     * register a FieldLabel Group
33359     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
33360     */
33361     register : function(label)
33362     {
33363         if(this.groups.hasOwnProperty(label.target)){
33364             return;
33365         }
33366      
33367         this.groups[label.target] = label;
33368         
33369     },
33370     /**
33371     * fetch a FieldLabel Group based on the target
33372     * @param {string} target
33373     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33374     */
33375     get: function(target) {
33376         if (typeof(this.groups[target]) == 'undefined') {
33377             return false;
33378         }
33379         
33380         return this.groups[target] ;
33381     }
33382 });
33383
33384  
33385
33386  /*
33387  * - LGPL
33388  *
33389  * page DateSplitField.
33390  * 
33391  */
33392
33393
33394 /**
33395  * @class Roo.bootstrap.DateSplitField
33396  * @extends Roo.bootstrap.Component
33397  * Bootstrap DateSplitField class
33398  * @cfg {string} fieldLabel - the label associated
33399  * @cfg {Number} labelWidth set the width of label (0-12)
33400  * @cfg {String} labelAlign (top|left)
33401  * @cfg {Boolean} dayAllowBlank (true|false) default false
33402  * @cfg {Boolean} monthAllowBlank (true|false) default false
33403  * @cfg {Boolean} yearAllowBlank (true|false) default false
33404  * @cfg {string} dayPlaceholder 
33405  * @cfg {string} monthPlaceholder
33406  * @cfg {string} yearPlaceholder
33407  * @cfg {string} dayFormat default 'd'
33408  * @cfg {string} monthFormat default 'm'
33409  * @cfg {string} yearFormat default 'Y'
33410  * @cfg {Number} labellg set the width of label (1-12)
33411  * @cfg {Number} labelmd set the width of label (1-12)
33412  * @cfg {Number} labelsm set the width of label (1-12)
33413  * @cfg {Number} labelxs set the width of label (1-12)
33414
33415  *     
33416  * @constructor
33417  * Create a new DateSplitField
33418  * @param {Object} config The config object
33419  */
33420
33421 Roo.bootstrap.DateSplitField = function(config){
33422     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33423     
33424     this.addEvents({
33425         // raw events
33426          /**
33427          * @event years
33428          * getting the data of years
33429          * @param {Roo.bootstrap.DateSplitField} this
33430          * @param {Object} years
33431          */
33432         "years" : true,
33433         /**
33434          * @event days
33435          * getting the data of days
33436          * @param {Roo.bootstrap.DateSplitField} this
33437          * @param {Object} days
33438          */
33439         "days" : true,
33440         /**
33441          * @event invalid
33442          * Fires after the field has been marked as invalid.
33443          * @param {Roo.form.Field} this
33444          * @param {String} msg The validation message
33445          */
33446         invalid : true,
33447        /**
33448          * @event valid
33449          * Fires after the field has been validated with no errors.
33450          * @param {Roo.form.Field} this
33451          */
33452         valid : true
33453     });
33454 };
33455
33456 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33457     
33458     fieldLabel : '',
33459     labelAlign : 'top',
33460     labelWidth : 3,
33461     dayAllowBlank : false,
33462     monthAllowBlank : false,
33463     yearAllowBlank : false,
33464     dayPlaceholder : '',
33465     monthPlaceholder : '',
33466     yearPlaceholder : '',
33467     dayFormat : 'd',
33468     monthFormat : 'm',
33469     yearFormat : 'Y',
33470     isFormField : true,
33471     labellg : 0,
33472     labelmd : 0,
33473     labelsm : 0,
33474     labelxs : 0,
33475     
33476     getAutoCreate : function()
33477     {
33478         var cfg = {
33479             tag : 'div',
33480             cls : 'row roo-date-split-field-group',
33481             cn : [
33482                 {
33483                     tag : 'input',
33484                     type : 'hidden',
33485                     cls : 'form-hidden-field roo-date-split-field-group-value',
33486                     name : this.name
33487                 }
33488             ]
33489         };
33490         
33491         var labelCls = 'col-md-12';
33492         var contentCls = 'col-md-4';
33493         
33494         if(this.fieldLabel){
33495             
33496             var label = {
33497                 tag : 'div',
33498                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33499                 cn : [
33500                     {
33501                         tag : 'label',
33502                         html : this.fieldLabel
33503                     }
33504                 ]
33505             };
33506             
33507             if(this.labelAlign == 'left'){
33508             
33509                 if(this.labelWidth > 12){
33510                     label.style = "width: " + this.labelWidth + 'px';
33511                 }
33512
33513                 if(this.labelWidth < 13 && this.labelmd == 0){
33514                     this.labelmd = this.labelWidth;
33515                 }
33516
33517                 if(this.labellg > 0){
33518                     labelCls = ' col-lg-' + this.labellg;
33519                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33520                 }
33521
33522                 if(this.labelmd > 0){
33523                     labelCls = ' col-md-' + this.labelmd;
33524                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33525                 }
33526
33527                 if(this.labelsm > 0){
33528                     labelCls = ' col-sm-' + this.labelsm;
33529                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33530                 }
33531
33532                 if(this.labelxs > 0){
33533                     labelCls = ' col-xs-' + this.labelxs;
33534                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33535                 }
33536             }
33537             
33538             label.cls += ' ' + labelCls;
33539             
33540             cfg.cn.push(label);
33541         }
33542         
33543         Roo.each(['day', 'month', 'year'], function(t){
33544             cfg.cn.push({
33545                 tag : 'div',
33546                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33547             });
33548         }, this);
33549         
33550         return cfg;
33551     },
33552     
33553     inputEl: function ()
33554     {
33555         return this.el.select('.roo-date-split-field-group-value', true).first();
33556     },
33557     
33558     onRender : function(ct, position) 
33559     {
33560         var _this = this;
33561         
33562         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33563         
33564         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33565         
33566         this.dayField = new Roo.bootstrap.ComboBox({
33567             allowBlank : this.dayAllowBlank,
33568             alwaysQuery : true,
33569             displayField : 'value',
33570             editable : false,
33571             fieldLabel : '',
33572             forceSelection : true,
33573             mode : 'local',
33574             placeholder : this.dayPlaceholder,
33575             selectOnFocus : true,
33576             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33577             triggerAction : 'all',
33578             typeAhead : true,
33579             valueField : 'value',
33580             store : new Roo.data.SimpleStore({
33581                 data : (function() {    
33582                     var days = [];
33583                     _this.fireEvent('days', _this, days);
33584                     return days;
33585                 })(),
33586                 fields : [ 'value' ]
33587             }),
33588             listeners : {
33589                 select : function (_self, record, index)
33590                 {
33591                     _this.setValue(_this.getValue());
33592                 }
33593             }
33594         });
33595
33596         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33597         
33598         this.monthField = new Roo.bootstrap.MonthField({
33599             after : '<i class=\"fa fa-calendar\"></i>',
33600             allowBlank : this.monthAllowBlank,
33601             placeholder : this.monthPlaceholder,
33602             readOnly : true,
33603             listeners : {
33604                 render : function (_self)
33605                 {
33606                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33607                         e.preventDefault();
33608                         _self.focus();
33609                     });
33610                 },
33611                 select : function (_self, oldvalue, newvalue)
33612                 {
33613                     _this.setValue(_this.getValue());
33614                 }
33615             }
33616         });
33617         
33618         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33619         
33620         this.yearField = new Roo.bootstrap.ComboBox({
33621             allowBlank : this.yearAllowBlank,
33622             alwaysQuery : true,
33623             displayField : 'value',
33624             editable : false,
33625             fieldLabel : '',
33626             forceSelection : true,
33627             mode : 'local',
33628             placeholder : this.yearPlaceholder,
33629             selectOnFocus : true,
33630             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33631             triggerAction : 'all',
33632             typeAhead : true,
33633             valueField : 'value',
33634             store : new Roo.data.SimpleStore({
33635                 data : (function() {
33636                     var years = [];
33637                     _this.fireEvent('years', _this, years);
33638                     return years;
33639                 })(),
33640                 fields : [ 'value' ]
33641             }),
33642             listeners : {
33643                 select : function (_self, record, index)
33644                 {
33645                     _this.setValue(_this.getValue());
33646                 }
33647             }
33648         });
33649
33650         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33651     },
33652     
33653     setValue : function(v, format)
33654     {
33655         this.inputEl.dom.value = v;
33656         
33657         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33658         
33659         var d = Date.parseDate(v, f);
33660         
33661         if(!d){
33662             this.validate();
33663             return;
33664         }
33665         
33666         this.setDay(d.format(this.dayFormat));
33667         this.setMonth(d.format(this.monthFormat));
33668         this.setYear(d.format(this.yearFormat));
33669         
33670         this.validate();
33671         
33672         return;
33673     },
33674     
33675     setDay : function(v)
33676     {
33677         this.dayField.setValue(v);
33678         this.inputEl.dom.value = this.getValue();
33679         this.validate();
33680         return;
33681     },
33682     
33683     setMonth : function(v)
33684     {
33685         this.monthField.setValue(v, true);
33686         this.inputEl.dom.value = this.getValue();
33687         this.validate();
33688         return;
33689     },
33690     
33691     setYear : function(v)
33692     {
33693         this.yearField.setValue(v);
33694         this.inputEl.dom.value = this.getValue();
33695         this.validate();
33696         return;
33697     },
33698     
33699     getDay : function()
33700     {
33701         return this.dayField.getValue();
33702     },
33703     
33704     getMonth : function()
33705     {
33706         return this.monthField.getValue();
33707     },
33708     
33709     getYear : function()
33710     {
33711         return this.yearField.getValue();
33712     },
33713     
33714     getValue : function()
33715     {
33716         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33717         
33718         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33719         
33720         return date;
33721     },
33722     
33723     reset : function()
33724     {
33725         this.setDay('');
33726         this.setMonth('');
33727         this.setYear('');
33728         this.inputEl.dom.value = '';
33729         this.validate();
33730         return;
33731     },
33732     
33733     validate : function()
33734     {
33735         var d = this.dayField.validate();
33736         var m = this.monthField.validate();
33737         var y = this.yearField.validate();
33738         
33739         var valid = true;
33740         
33741         if(
33742                 (!this.dayAllowBlank && !d) ||
33743                 (!this.monthAllowBlank && !m) ||
33744                 (!this.yearAllowBlank && !y)
33745         ){
33746             valid = false;
33747         }
33748         
33749         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33750             return valid;
33751         }
33752         
33753         if(valid){
33754             this.markValid();
33755             return valid;
33756         }
33757         
33758         this.markInvalid();
33759         
33760         return valid;
33761     },
33762     
33763     markValid : function()
33764     {
33765         
33766         var label = this.el.select('label', true).first();
33767         var icon = this.el.select('i.fa-star', true).first();
33768
33769         if(label && icon){
33770             icon.remove();
33771         }
33772         
33773         this.fireEvent('valid', this);
33774     },
33775     
33776      /**
33777      * Mark this field as invalid
33778      * @param {String} msg The validation message
33779      */
33780     markInvalid : function(msg)
33781     {
33782         
33783         var label = this.el.select('label', true).first();
33784         var icon = this.el.select('i.fa-star', true).first();
33785
33786         if(label && !icon){
33787             this.el.select('.roo-date-split-field-label', true).createChild({
33788                 tag : 'i',
33789                 cls : 'text-danger fa fa-lg fa-star',
33790                 tooltip : 'This field is required',
33791                 style : 'margin-right:5px;'
33792             }, label, true);
33793         }
33794         
33795         this.fireEvent('invalid', this, msg);
33796     },
33797     
33798     clearInvalid : function()
33799     {
33800         var label = this.el.select('label', true).first();
33801         var icon = this.el.select('i.fa-star', true).first();
33802
33803         if(label && icon){
33804             icon.remove();
33805         }
33806         
33807         this.fireEvent('valid', this);
33808     },
33809     
33810     getName: function()
33811     {
33812         return this.name;
33813     }
33814     
33815 });
33816
33817  /**
33818  *
33819  * This is based on 
33820  * http://masonry.desandro.com
33821  *
33822  * The idea is to render all the bricks based on vertical width...
33823  *
33824  * The original code extends 'outlayer' - we might need to use that....
33825  * 
33826  */
33827
33828
33829 /**
33830  * @class Roo.bootstrap.LayoutMasonry
33831  * @extends Roo.bootstrap.Component
33832  * Bootstrap Layout Masonry class
33833  * 
33834  * @constructor
33835  * Create a new Element
33836  * @param {Object} config The config object
33837  */
33838
33839 Roo.bootstrap.LayoutMasonry = function(config){
33840     
33841     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33842     
33843     this.bricks = [];
33844     
33845     Roo.bootstrap.LayoutMasonry.register(this);
33846     
33847     this.addEvents({
33848         // raw events
33849         /**
33850          * @event layout
33851          * Fire after layout the items
33852          * @param {Roo.bootstrap.LayoutMasonry} this
33853          * @param {Roo.EventObject} e
33854          */
33855         "layout" : true
33856     });
33857     
33858 };
33859
33860 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33861     
33862     /**
33863      * @cfg {Boolean} isLayoutInstant = no animation?
33864      */   
33865     isLayoutInstant : false, // needed?
33866    
33867     /**
33868      * @cfg {Number} boxWidth  width of the columns
33869      */   
33870     boxWidth : 450,
33871     
33872       /**
33873      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33874      */   
33875     boxHeight : 0,
33876     
33877     /**
33878      * @cfg {Number} padWidth padding below box..
33879      */   
33880     padWidth : 10, 
33881     
33882     /**
33883      * @cfg {Number} gutter gutter width..
33884      */   
33885     gutter : 10,
33886     
33887      /**
33888      * @cfg {Number} maxCols maximum number of columns
33889      */   
33890     
33891     maxCols: 0,
33892     
33893     /**
33894      * @cfg {Boolean} isAutoInitial defalut true
33895      */   
33896     isAutoInitial : true, 
33897     
33898     containerWidth: 0,
33899     
33900     /**
33901      * @cfg {Boolean} isHorizontal defalut false
33902      */   
33903     isHorizontal : false, 
33904
33905     currentSize : null,
33906     
33907     tag: 'div',
33908     
33909     cls: '',
33910     
33911     bricks: null, //CompositeElement
33912     
33913     cols : 1,
33914     
33915     _isLayoutInited : false,
33916     
33917 //    isAlternative : false, // only use for vertical layout...
33918     
33919     /**
33920      * @cfg {Number} alternativePadWidth padding below box..
33921      */   
33922     alternativePadWidth : 50,
33923     
33924     selectedBrick : [],
33925     
33926     getAutoCreate : function(){
33927         
33928         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33929         
33930         var cfg = {
33931             tag: this.tag,
33932             cls: 'blog-masonary-wrapper ' + this.cls,
33933             cn : {
33934                 cls : 'mas-boxes masonary'
33935             }
33936         };
33937         
33938         return cfg;
33939     },
33940     
33941     getChildContainer: function( )
33942     {
33943         if (this.boxesEl) {
33944             return this.boxesEl;
33945         }
33946         
33947         this.boxesEl = this.el.select('.mas-boxes').first();
33948         
33949         return this.boxesEl;
33950     },
33951     
33952     
33953     initEvents : function()
33954     {
33955         var _this = this;
33956         
33957         if(this.isAutoInitial){
33958             Roo.log('hook children rendered');
33959             this.on('childrenrendered', function() {
33960                 Roo.log('children rendered');
33961                 _this.initial();
33962             } ,this);
33963         }
33964     },
33965     
33966     initial : function()
33967     {
33968         this.selectedBrick = [];
33969         
33970         this.currentSize = this.el.getBox(true);
33971         
33972         Roo.EventManager.onWindowResize(this.resize, this); 
33973
33974         if(!this.isAutoInitial){
33975             this.layout();
33976             return;
33977         }
33978         
33979         this.layout();
33980         
33981         return;
33982         //this.layout.defer(500,this);
33983         
33984     },
33985     
33986     resize : function()
33987     {
33988         var cs = this.el.getBox(true);
33989         
33990         if (
33991                 this.currentSize.width == cs.width && 
33992                 this.currentSize.x == cs.x && 
33993                 this.currentSize.height == cs.height && 
33994                 this.currentSize.y == cs.y 
33995         ) {
33996             Roo.log("no change in with or X or Y");
33997             return;
33998         }
33999         
34000         this.currentSize = cs;
34001         
34002         this.layout();
34003         
34004     },
34005     
34006     layout : function()
34007     {   
34008         this._resetLayout();
34009         
34010         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34011         
34012         this.layoutItems( isInstant );
34013       
34014         this._isLayoutInited = true;
34015         
34016         this.fireEvent('layout', this);
34017         
34018     },
34019     
34020     _resetLayout : function()
34021     {
34022         if(this.isHorizontal){
34023             this.horizontalMeasureColumns();
34024             return;
34025         }
34026         
34027         this.verticalMeasureColumns();
34028         
34029     },
34030     
34031     verticalMeasureColumns : function()
34032     {
34033         this.getContainerWidth();
34034         
34035 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34036 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
34037 //            return;
34038 //        }
34039         
34040         var boxWidth = this.boxWidth + this.padWidth;
34041         
34042         if(this.containerWidth < this.boxWidth){
34043             boxWidth = this.containerWidth
34044         }
34045         
34046         var containerWidth = this.containerWidth;
34047         
34048         var cols = Math.floor(containerWidth / boxWidth);
34049         
34050         this.cols = Math.max( cols, 1 );
34051         
34052         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34053         
34054         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
34055         
34056         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
34057         
34058         this.colWidth = boxWidth + avail - this.padWidth;
34059         
34060         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
34061         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
34062     },
34063     
34064     horizontalMeasureColumns : function()
34065     {
34066         this.getContainerWidth();
34067         
34068         var boxWidth = this.boxWidth;
34069         
34070         if(this.containerWidth < boxWidth){
34071             boxWidth = this.containerWidth;
34072         }
34073         
34074         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
34075         
34076         this.el.setHeight(boxWidth);
34077         
34078     },
34079     
34080     getContainerWidth : function()
34081     {
34082         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34083     },
34084     
34085     layoutItems : function( isInstant )
34086     {
34087         Roo.log(this.bricks);
34088         
34089         var items = Roo.apply([], this.bricks);
34090         
34091         if(this.isHorizontal){
34092             this._horizontalLayoutItems( items , isInstant );
34093             return;
34094         }
34095         
34096 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
34097 //            this._verticalAlternativeLayoutItems( items , isInstant );
34098 //            return;
34099 //        }
34100         
34101         this._verticalLayoutItems( items , isInstant );
34102         
34103     },
34104     
34105     _verticalLayoutItems : function ( items , isInstant)
34106     {
34107         if ( !items || !items.length ) {
34108             return;
34109         }
34110         
34111         var standard = [
34112             ['xs', 'xs', 'xs', 'tall'],
34113             ['xs', 'xs', 'tall'],
34114             ['xs', 'xs', 'sm'],
34115             ['xs', 'xs', 'xs'],
34116             ['xs', 'tall'],
34117             ['xs', 'sm'],
34118             ['xs', 'xs'],
34119             ['xs'],
34120             
34121             ['sm', 'xs', 'xs'],
34122             ['sm', 'xs'],
34123             ['sm'],
34124             
34125             ['tall', 'xs', 'xs', 'xs'],
34126             ['tall', 'xs', 'xs'],
34127             ['tall', 'xs'],
34128             ['tall']
34129             
34130         ];
34131         
34132         var queue = [];
34133         
34134         var boxes = [];
34135         
34136         var box = [];
34137         
34138         Roo.each(items, function(item, k){
34139             
34140             switch (item.size) {
34141                 // these layouts take up a full box,
34142                 case 'md' :
34143                 case 'md-left' :
34144                 case 'md-right' :
34145                 case 'wide' :
34146                     
34147                     if(box.length){
34148                         boxes.push(box);
34149                         box = [];
34150                     }
34151                     
34152                     boxes.push([item]);
34153                     
34154                     break;
34155                     
34156                 case 'xs' :
34157                 case 'sm' :
34158                 case 'tall' :
34159                     
34160                     box.push(item);
34161                     
34162                     break;
34163                 default :
34164                     break;
34165                     
34166             }
34167             
34168         }, this);
34169         
34170         if(box.length){
34171             boxes.push(box);
34172             box = [];
34173         }
34174         
34175         var filterPattern = function(box, length)
34176         {
34177             if(!box.length){
34178                 return;
34179             }
34180             
34181             var match = false;
34182             
34183             var pattern = box.slice(0, length);
34184             
34185             var format = [];
34186             
34187             Roo.each(pattern, function(i){
34188                 format.push(i.size);
34189             }, this);
34190             
34191             Roo.each(standard, function(s){
34192                 
34193                 if(String(s) != String(format)){
34194                     return;
34195                 }
34196                 
34197                 match = true;
34198                 return false;
34199                 
34200             }, this);
34201             
34202             if(!match && length == 1){
34203                 return;
34204             }
34205             
34206             if(!match){
34207                 filterPattern(box, length - 1);
34208                 return;
34209             }
34210                 
34211             queue.push(pattern);
34212
34213             box = box.slice(length, box.length);
34214
34215             filterPattern(box, 4);
34216
34217             return;
34218             
34219         }
34220         
34221         Roo.each(boxes, function(box, k){
34222             
34223             if(!box.length){
34224                 return;
34225             }
34226             
34227             if(box.length == 1){
34228                 queue.push(box);
34229                 return;
34230             }
34231             
34232             filterPattern(box, 4);
34233             
34234         }, this);
34235         
34236         this._processVerticalLayoutQueue( queue, isInstant );
34237         
34238     },
34239     
34240 //    _verticalAlternativeLayoutItems : function( items , isInstant )
34241 //    {
34242 //        if ( !items || !items.length ) {
34243 //            return;
34244 //        }
34245 //
34246 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
34247 //        
34248 //    },
34249     
34250     _horizontalLayoutItems : function ( items , isInstant)
34251     {
34252         if ( !items || !items.length || items.length < 3) {
34253             return;
34254         }
34255         
34256         items.reverse();
34257         
34258         var eItems = items.slice(0, 3);
34259         
34260         items = items.slice(3, items.length);
34261         
34262         var standard = [
34263             ['xs', 'xs', 'xs', 'wide'],
34264             ['xs', 'xs', 'wide'],
34265             ['xs', 'xs', 'sm'],
34266             ['xs', 'xs', 'xs'],
34267             ['xs', 'wide'],
34268             ['xs', 'sm'],
34269             ['xs', 'xs'],
34270             ['xs'],
34271             
34272             ['sm', 'xs', 'xs'],
34273             ['sm', 'xs'],
34274             ['sm'],
34275             
34276             ['wide', 'xs', 'xs', 'xs'],
34277             ['wide', 'xs', 'xs'],
34278             ['wide', 'xs'],
34279             ['wide'],
34280             
34281             ['wide-thin']
34282         ];
34283         
34284         var queue = [];
34285         
34286         var boxes = [];
34287         
34288         var box = [];
34289         
34290         Roo.each(items, function(item, k){
34291             
34292             switch (item.size) {
34293                 case 'md' :
34294                 case 'md-left' :
34295                 case 'md-right' :
34296                 case 'tall' :
34297                     
34298                     if(box.length){
34299                         boxes.push(box);
34300                         box = [];
34301                     }
34302                     
34303                     boxes.push([item]);
34304                     
34305                     break;
34306                     
34307                 case 'xs' :
34308                 case 'sm' :
34309                 case 'wide' :
34310                 case 'wide-thin' :
34311                     
34312                     box.push(item);
34313                     
34314                     break;
34315                 default :
34316                     break;
34317                     
34318             }
34319             
34320         }, this);
34321         
34322         if(box.length){
34323             boxes.push(box);
34324             box = [];
34325         }
34326         
34327         var filterPattern = function(box, length)
34328         {
34329             if(!box.length){
34330                 return;
34331             }
34332             
34333             var match = false;
34334             
34335             var pattern = box.slice(0, length);
34336             
34337             var format = [];
34338             
34339             Roo.each(pattern, function(i){
34340                 format.push(i.size);
34341             }, this);
34342             
34343             Roo.each(standard, function(s){
34344                 
34345                 if(String(s) != String(format)){
34346                     return;
34347                 }
34348                 
34349                 match = true;
34350                 return false;
34351                 
34352             }, this);
34353             
34354             if(!match && length == 1){
34355                 return;
34356             }
34357             
34358             if(!match){
34359                 filterPattern(box, length - 1);
34360                 return;
34361             }
34362                 
34363             queue.push(pattern);
34364
34365             box = box.slice(length, box.length);
34366
34367             filterPattern(box, 4);
34368
34369             return;
34370             
34371         }
34372         
34373         Roo.each(boxes, function(box, k){
34374             
34375             if(!box.length){
34376                 return;
34377             }
34378             
34379             if(box.length == 1){
34380                 queue.push(box);
34381                 return;
34382             }
34383             
34384             filterPattern(box, 4);
34385             
34386         }, this);
34387         
34388         
34389         var prune = [];
34390         
34391         var pos = this.el.getBox(true);
34392         
34393         var minX = pos.x;
34394         
34395         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34396         
34397         var hit_end = false;
34398         
34399         Roo.each(queue, function(box){
34400             
34401             if(hit_end){
34402                 
34403                 Roo.each(box, function(b){
34404                 
34405                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34406                     b.el.hide();
34407
34408                 }, this);
34409
34410                 return;
34411             }
34412             
34413             var mx = 0;
34414             
34415             Roo.each(box, function(b){
34416                 
34417                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34418                 b.el.show();
34419
34420                 mx = Math.max(mx, b.x);
34421                 
34422             }, this);
34423             
34424             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34425             
34426             if(maxX < minX){
34427                 
34428                 Roo.each(box, function(b){
34429                 
34430                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34431                     b.el.hide();
34432                     
34433                 }, this);
34434                 
34435                 hit_end = true;
34436                 
34437                 return;
34438             }
34439             
34440             prune.push(box);
34441             
34442         }, this);
34443         
34444         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34445     },
34446     
34447     /** Sets position of item in DOM
34448     * @param {Element} item
34449     * @param {Number} x - horizontal position
34450     * @param {Number} y - vertical position
34451     * @param {Boolean} isInstant - disables transitions
34452     */
34453     _processVerticalLayoutQueue : function( queue, isInstant )
34454     {
34455         var pos = this.el.getBox(true);
34456         var x = pos.x;
34457         var y = pos.y;
34458         var maxY = [];
34459         
34460         for (var i = 0; i < this.cols; i++){
34461             maxY[i] = pos.y;
34462         }
34463         
34464         Roo.each(queue, function(box, k){
34465             
34466             var col = k % this.cols;
34467             
34468             Roo.each(box, function(b,kk){
34469                 
34470                 b.el.position('absolute');
34471                 
34472                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34473                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34474                 
34475                 if(b.size == 'md-left' || b.size == 'md-right'){
34476                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34477                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34478                 }
34479                 
34480                 b.el.setWidth(width);
34481                 b.el.setHeight(height);
34482                 // iframe?
34483                 b.el.select('iframe',true).setSize(width,height);
34484                 
34485             }, this);
34486             
34487             for (var i = 0; i < this.cols; i++){
34488                 
34489                 if(maxY[i] < maxY[col]){
34490                     col = i;
34491                     continue;
34492                 }
34493                 
34494                 col = Math.min(col, i);
34495                 
34496             }
34497             
34498             x = pos.x + col * (this.colWidth + this.padWidth);
34499             
34500             y = maxY[col];
34501             
34502             var positions = [];
34503             
34504             switch (box.length){
34505                 case 1 :
34506                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34507                     break;
34508                 case 2 :
34509                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34510                     break;
34511                 case 3 :
34512                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34513                     break;
34514                 case 4 :
34515                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34516                     break;
34517                 default :
34518                     break;
34519             }
34520             
34521             Roo.each(box, function(b,kk){
34522                 
34523                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34524                 
34525                 var sz = b.el.getSize();
34526                 
34527                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34528                 
34529             }, this);
34530             
34531         }, this);
34532         
34533         var mY = 0;
34534         
34535         for (var i = 0; i < this.cols; i++){
34536             mY = Math.max(mY, maxY[i]);
34537         }
34538         
34539         this.el.setHeight(mY - pos.y);
34540         
34541     },
34542     
34543 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34544 //    {
34545 //        var pos = this.el.getBox(true);
34546 //        var x = pos.x;
34547 //        var y = pos.y;
34548 //        var maxX = pos.right;
34549 //        
34550 //        var maxHeight = 0;
34551 //        
34552 //        Roo.each(items, function(item, k){
34553 //            
34554 //            var c = k % 2;
34555 //            
34556 //            item.el.position('absolute');
34557 //                
34558 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34559 //
34560 //            item.el.setWidth(width);
34561 //
34562 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34563 //
34564 //            item.el.setHeight(height);
34565 //            
34566 //            if(c == 0){
34567 //                item.el.setXY([x, y], isInstant ? false : true);
34568 //            } else {
34569 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34570 //            }
34571 //            
34572 //            y = y + height + this.alternativePadWidth;
34573 //            
34574 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34575 //            
34576 //        }, this);
34577 //        
34578 //        this.el.setHeight(maxHeight);
34579 //        
34580 //    },
34581     
34582     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34583     {
34584         var pos = this.el.getBox(true);
34585         
34586         var minX = pos.x;
34587         var minY = pos.y;
34588         
34589         var maxX = pos.right;
34590         
34591         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34592         
34593         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34594         
34595         Roo.each(queue, function(box, k){
34596             
34597             Roo.each(box, function(b, kk){
34598                 
34599                 b.el.position('absolute');
34600                 
34601                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34602                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34603                 
34604                 if(b.size == 'md-left' || b.size == 'md-right'){
34605                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34606                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34607                 }
34608                 
34609                 b.el.setWidth(width);
34610                 b.el.setHeight(height);
34611                 
34612             }, this);
34613             
34614             if(!box.length){
34615                 return;
34616             }
34617             
34618             var positions = [];
34619             
34620             switch (box.length){
34621                 case 1 :
34622                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34623                     break;
34624                 case 2 :
34625                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34626                     break;
34627                 case 3 :
34628                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34629                     break;
34630                 case 4 :
34631                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34632                     break;
34633                 default :
34634                     break;
34635             }
34636             
34637             Roo.each(box, function(b,kk){
34638                 
34639                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34640                 
34641                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34642                 
34643             }, this);
34644             
34645         }, this);
34646         
34647     },
34648     
34649     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34650     {
34651         Roo.each(eItems, function(b,k){
34652             
34653             b.size = (k == 0) ? 'sm' : 'xs';
34654             b.x = (k == 0) ? 2 : 1;
34655             b.y = (k == 0) ? 2 : 1;
34656             
34657             b.el.position('absolute');
34658             
34659             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34660                 
34661             b.el.setWidth(width);
34662             
34663             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34664             
34665             b.el.setHeight(height);
34666             
34667         }, this);
34668
34669         var positions = [];
34670         
34671         positions.push({
34672             x : maxX - this.unitWidth * 2 - this.gutter,
34673             y : minY
34674         });
34675         
34676         positions.push({
34677             x : maxX - this.unitWidth,
34678             y : minY + (this.unitWidth + this.gutter) * 2
34679         });
34680         
34681         positions.push({
34682             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34683             y : minY
34684         });
34685         
34686         Roo.each(eItems, function(b,k){
34687             
34688             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34689
34690         }, this);
34691         
34692     },
34693     
34694     getVerticalOneBoxColPositions : function(x, y, box)
34695     {
34696         var pos = [];
34697         
34698         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34699         
34700         if(box[0].size == 'md-left'){
34701             rand = 0;
34702         }
34703         
34704         if(box[0].size == 'md-right'){
34705             rand = 1;
34706         }
34707         
34708         pos.push({
34709             x : x + (this.unitWidth + this.gutter) * rand,
34710             y : y
34711         });
34712         
34713         return pos;
34714     },
34715     
34716     getVerticalTwoBoxColPositions : function(x, y, box)
34717     {
34718         var pos = [];
34719         
34720         if(box[0].size == 'xs'){
34721             
34722             pos.push({
34723                 x : x,
34724                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34725             });
34726
34727             pos.push({
34728                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34729                 y : y
34730             });
34731             
34732             return pos;
34733             
34734         }
34735         
34736         pos.push({
34737             x : x,
34738             y : y
34739         });
34740
34741         pos.push({
34742             x : x + (this.unitWidth + this.gutter) * 2,
34743             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34744         });
34745         
34746         return pos;
34747         
34748     },
34749     
34750     getVerticalThreeBoxColPositions : function(x, y, box)
34751     {
34752         var pos = [];
34753         
34754         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34755             
34756             pos.push({
34757                 x : x,
34758                 y : y
34759             });
34760
34761             pos.push({
34762                 x : x + (this.unitWidth + this.gutter) * 1,
34763                 y : y
34764             });
34765             
34766             pos.push({
34767                 x : x + (this.unitWidth + this.gutter) * 2,
34768                 y : y
34769             });
34770             
34771             return pos;
34772             
34773         }
34774         
34775         if(box[0].size == 'xs' && box[1].size == 'xs'){
34776             
34777             pos.push({
34778                 x : x,
34779                 y : y
34780             });
34781
34782             pos.push({
34783                 x : x,
34784                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34785             });
34786             
34787             pos.push({
34788                 x : x + (this.unitWidth + this.gutter) * 1,
34789                 y : y
34790             });
34791             
34792             return pos;
34793             
34794         }
34795         
34796         pos.push({
34797             x : x,
34798             y : y
34799         });
34800
34801         pos.push({
34802             x : x + (this.unitWidth + this.gutter) * 2,
34803             y : y
34804         });
34805
34806         pos.push({
34807             x : x + (this.unitWidth + this.gutter) * 2,
34808             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34809         });
34810             
34811         return pos;
34812         
34813     },
34814     
34815     getVerticalFourBoxColPositions : function(x, y, box)
34816     {
34817         var pos = [];
34818         
34819         if(box[0].size == 'xs'){
34820             
34821             pos.push({
34822                 x : x,
34823                 y : y
34824             });
34825
34826             pos.push({
34827                 x : x,
34828                 y : y + (this.unitHeight + this.gutter) * 1
34829             });
34830             
34831             pos.push({
34832                 x : x,
34833                 y : y + (this.unitHeight + this.gutter) * 2
34834             });
34835             
34836             pos.push({
34837                 x : x + (this.unitWidth + this.gutter) * 1,
34838                 y : y
34839             });
34840             
34841             return pos;
34842             
34843         }
34844         
34845         pos.push({
34846             x : x,
34847             y : y
34848         });
34849
34850         pos.push({
34851             x : x + (this.unitWidth + this.gutter) * 2,
34852             y : y
34853         });
34854
34855         pos.push({
34856             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34857             y : y + (this.unitHeight + this.gutter) * 1
34858         });
34859
34860         pos.push({
34861             x : x + (this.unitWidth + this.gutter) * 2,
34862             y : y + (this.unitWidth + this.gutter) * 2
34863         });
34864
34865         return pos;
34866         
34867     },
34868     
34869     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34870     {
34871         var pos = [];
34872         
34873         if(box[0].size == 'md-left'){
34874             pos.push({
34875                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34876                 y : minY
34877             });
34878             
34879             return pos;
34880         }
34881         
34882         if(box[0].size == 'md-right'){
34883             pos.push({
34884                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34885                 y : minY + (this.unitWidth + this.gutter) * 1
34886             });
34887             
34888             return pos;
34889         }
34890         
34891         var rand = Math.floor(Math.random() * (4 - box[0].y));
34892         
34893         pos.push({
34894             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34895             y : minY + (this.unitWidth + this.gutter) * rand
34896         });
34897         
34898         return pos;
34899         
34900     },
34901     
34902     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34903     {
34904         var pos = [];
34905         
34906         if(box[0].size == 'xs'){
34907             
34908             pos.push({
34909                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34910                 y : minY
34911             });
34912
34913             pos.push({
34914                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34915                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34916             });
34917             
34918             return pos;
34919             
34920         }
34921         
34922         pos.push({
34923             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34924             y : minY
34925         });
34926
34927         pos.push({
34928             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34929             y : minY + (this.unitWidth + this.gutter) * 2
34930         });
34931         
34932         return pos;
34933         
34934     },
34935     
34936     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34937     {
34938         var pos = [];
34939         
34940         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34941             
34942             pos.push({
34943                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34944                 y : minY
34945             });
34946
34947             pos.push({
34948                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34949                 y : minY + (this.unitWidth + this.gutter) * 1
34950             });
34951             
34952             pos.push({
34953                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34954                 y : minY + (this.unitWidth + this.gutter) * 2
34955             });
34956             
34957             return pos;
34958             
34959         }
34960         
34961         if(box[0].size == 'xs' && box[1].size == 'xs'){
34962             
34963             pos.push({
34964                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34965                 y : minY
34966             });
34967
34968             pos.push({
34969                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34970                 y : minY
34971             });
34972             
34973             pos.push({
34974                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34975                 y : minY + (this.unitWidth + this.gutter) * 1
34976             });
34977             
34978             return pos;
34979             
34980         }
34981         
34982         pos.push({
34983             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34984             y : minY
34985         });
34986
34987         pos.push({
34988             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34989             y : minY + (this.unitWidth + this.gutter) * 2
34990         });
34991
34992         pos.push({
34993             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34994             y : minY + (this.unitWidth + this.gutter) * 2
34995         });
34996             
34997         return pos;
34998         
34999     },
35000     
35001     getHorizontalFourBoxColPositions : function(maxX, minY, box)
35002     {
35003         var pos = [];
35004         
35005         if(box[0].size == 'xs'){
35006             
35007             pos.push({
35008                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35009                 y : minY
35010             });
35011
35012             pos.push({
35013                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35014                 y : minY
35015             });
35016             
35017             pos.push({
35018                 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),
35019                 y : minY
35020             });
35021             
35022             pos.push({
35023                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
35024                 y : minY + (this.unitWidth + this.gutter) * 1
35025             });
35026             
35027             return pos;
35028             
35029         }
35030         
35031         pos.push({
35032             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
35033             y : minY
35034         });
35035         
35036         pos.push({
35037             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
35038             y : minY + (this.unitWidth + this.gutter) * 2
35039         });
35040         
35041         pos.push({
35042             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
35043             y : minY + (this.unitWidth + this.gutter) * 2
35044         });
35045         
35046         pos.push({
35047             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),
35048             y : minY + (this.unitWidth + this.gutter) * 2
35049         });
35050
35051         return pos;
35052         
35053     },
35054     
35055     /**
35056     * remove a Masonry Brick
35057     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
35058     */
35059     removeBrick : function(brick_id)
35060     {
35061         if (!brick_id) {
35062             return;
35063         }
35064         
35065         for (var i = 0; i<this.bricks.length; i++) {
35066             if (this.bricks[i].id == brick_id) {
35067                 this.bricks.splice(i,1);
35068                 this.el.dom.removeChild(Roo.get(brick_id).dom);
35069                 this.initial();
35070             }
35071         }
35072     },
35073     
35074     /**
35075     * adds a Masonry Brick
35076     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35077     */
35078     addBrick : function(cfg)
35079     {
35080         var cn = new Roo.bootstrap.MasonryBrick(cfg);
35081         //this.register(cn);
35082         cn.parentId = this.id;
35083         cn.render(this.el);
35084         return cn;
35085     },
35086     
35087     /**
35088     * register a Masonry Brick
35089     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35090     */
35091     
35092     register : function(brick)
35093     {
35094         this.bricks.push(brick);
35095         brick.masonryId = this.id;
35096     },
35097     
35098     /**
35099     * clear all the Masonry Brick
35100     */
35101     clearAll : function()
35102     {
35103         this.bricks = [];
35104         //this.getChildContainer().dom.innerHTML = "";
35105         this.el.dom.innerHTML = '';
35106     },
35107     
35108     getSelected : function()
35109     {
35110         if (!this.selectedBrick) {
35111             return false;
35112         }
35113         
35114         return this.selectedBrick;
35115     }
35116 });
35117
35118 Roo.apply(Roo.bootstrap.LayoutMasonry, {
35119     
35120     groups: {},
35121      /**
35122     * register a Masonry Layout
35123     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
35124     */
35125     
35126     register : function(layout)
35127     {
35128         this.groups[layout.id] = layout;
35129     },
35130     /**
35131     * fetch a  Masonry Layout based on the masonry layout ID
35132     * @param {string} the masonry layout to add
35133     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
35134     */
35135     
35136     get: function(layout_id) {
35137         if (typeof(this.groups[layout_id]) == 'undefined') {
35138             return false;
35139         }
35140         return this.groups[layout_id] ;
35141     }
35142     
35143     
35144     
35145 });
35146
35147  
35148
35149  /**
35150  *
35151  * This is based on 
35152  * http://masonry.desandro.com
35153  *
35154  * The idea is to render all the bricks based on vertical width...
35155  *
35156  * The original code extends 'outlayer' - we might need to use that....
35157  * 
35158  */
35159
35160
35161 /**
35162  * @class Roo.bootstrap.LayoutMasonryAuto
35163  * @extends Roo.bootstrap.Component
35164  * Bootstrap Layout Masonry class
35165  * 
35166  * @constructor
35167  * Create a new Element
35168  * @param {Object} config The config object
35169  */
35170
35171 Roo.bootstrap.LayoutMasonryAuto = function(config){
35172     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
35173 };
35174
35175 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
35176     
35177       /**
35178      * @cfg {Boolean} isFitWidth  - resize the width..
35179      */   
35180     isFitWidth : false,  // options..
35181     /**
35182      * @cfg {Boolean} isOriginLeft = left align?
35183      */   
35184     isOriginLeft : true,
35185     /**
35186      * @cfg {Boolean} isOriginTop = top align?
35187      */   
35188     isOriginTop : false,
35189     /**
35190      * @cfg {Boolean} isLayoutInstant = no animation?
35191      */   
35192     isLayoutInstant : false, // needed?
35193     /**
35194      * @cfg {Boolean} isResizingContainer = not sure if this is used..
35195      */   
35196     isResizingContainer : true,
35197     /**
35198      * @cfg {Number} columnWidth  width of the columns 
35199      */   
35200     
35201     columnWidth : 0,
35202     
35203     /**
35204      * @cfg {Number} maxCols maximum number of columns
35205      */   
35206     
35207     maxCols: 0,
35208     /**
35209      * @cfg {Number} padHeight padding below box..
35210      */   
35211     
35212     padHeight : 10, 
35213     
35214     /**
35215      * @cfg {Boolean} isAutoInitial defalut true
35216      */   
35217     
35218     isAutoInitial : true, 
35219     
35220     // private?
35221     gutter : 0,
35222     
35223     containerWidth: 0,
35224     initialColumnWidth : 0,
35225     currentSize : null,
35226     
35227     colYs : null, // array.
35228     maxY : 0,
35229     padWidth: 10,
35230     
35231     
35232     tag: 'div',
35233     cls: '',
35234     bricks: null, //CompositeElement
35235     cols : 0, // array?
35236     // element : null, // wrapped now this.el
35237     _isLayoutInited : null, 
35238     
35239     
35240     getAutoCreate : function(){
35241         
35242         var cfg = {
35243             tag: this.tag,
35244             cls: 'blog-masonary-wrapper ' + this.cls,
35245             cn : {
35246                 cls : 'mas-boxes masonary'
35247             }
35248         };
35249         
35250         return cfg;
35251     },
35252     
35253     getChildContainer: function( )
35254     {
35255         if (this.boxesEl) {
35256             return this.boxesEl;
35257         }
35258         
35259         this.boxesEl = this.el.select('.mas-boxes').first();
35260         
35261         return this.boxesEl;
35262     },
35263     
35264     
35265     initEvents : function()
35266     {
35267         var _this = this;
35268         
35269         if(this.isAutoInitial){
35270             Roo.log('hook children rendered');
35271             this.on('childrenrendered', function() {
35272                 Roo.log('children rendered');
35273                 _this.initial();
35274             } ,this);
35275         }
35276         
35277     },
35278     
35279     initial : function()
35280     {
35281         this.reloadItems();
35282
35283         this.currentSize = this.el.getBox(true);
35284
35285         /// was window resize... - let's see if this works..
35286         Roo.EventManager.onWindowResize(this.resize, this); 
35287
35288         if(!this.isAutoInitial){
35289             this.layout();
35290             return;
35291         }
35292         
35293         this.layout.defer(500,this);
35294     },
35295     
35296     reloadItems: function()
35297     {
35298         this.bricks = this.el.select('.masonry-brick', true);
35299         
35300         this.bricks.each(function(b) {
35301             //Roo.log(b.getSize());
35302             if (!b.attr('originalwidth')) {
35303                 b.attr('originalwidth',  b.getSize().width);
35304             }
35305             
35306         });
35307         
35308         Roo.log(this.bricks.elements.length);
35309     },
35310     
35311     resize : function()
35312     {
35313         Roo.log('resize');
35314         var cs = this.el.getBox(true);
35315         
35316         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
35317             Roo.log("no change in with or X");
35318             return;
35319         }
35320         this.currentSize = cs;
35321         this.layout();
35322     },
35323     
35324     layout : function()
35325     {
35326          Roo.log('layout');
35327         this._resetLayout();
35328         //this._manageStamps();
35329       
35330         // don't animate first layout
35331         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
35332         this.layoutItems( isInstant );
35333       
35334         // flag for initalized
35335         this._isLayoutInited = true;
35336     },
35337     
35338     layoutItems : function( isInstant )
35339     {
35340         //var items = this._getItemsForLayout( this.items );
35341         // original code supports filtering layout items.. we just ignore it..
35342         
35343         this._layoutItems( this.bricks , isInstant );
35344       
35345         this._postLayout();
35346     },
35347     _layoutItems : function ( items , isInstant)
35348     {
35349        //this.fireEvent( 'layout', this, items );
35350     
35351
35352         if ( !items || !items.elements.length ) {
35353           // no items, emit event with empty array
35354             return;
35355         }
35356
35357         var queue = [];
35358         items.each(function(item) {
35359             Roo.log("layout item");
35360             Roo.log(item);
35361             // get x/y object from method
35362             var position = this._getItemLayoutPosition( item );
35363             // enqueue
35364             position.item = item;
35365             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35366             queue.push( position );
35367         }, this);
35368       
35369         this._processLayoutQueue( queue );
35370     },
35371     /** Sets position of item in DOM
35372     * @param {Element} item
35373     * @param {Number} x - horizontal position
35374     * @param {Number} y - vertical position
35375     * @param {Boolean} isInstant - disables transitions
35376     */
35377     _processLayoutQueue : function( queue )
35378     {
35379         for ( var i=0, len = queue.length; i < len; i++ ) {
35380             var obj = queue[i];
35381             obj.item.position('absolute');
35382             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35383         }
35384     },
35385       
35386     
35387     /**
35388     * Any logic you want to do after each layout,
35389     * i.e. size the container
35390     */
35391     _postLayout : function()
35392     {
35393         this.resizeContainer();
35394     },
35395     
35396     resizeContainer : function()
35397     {
35398         if ( !this.isResizingContainer ) {
35399             return;
35400         }
35401         var size = this._getContainerSize();
35402         if ( size ) {
35403             this.el.setSize(size.width,size.height);
35404             this.boxesEl.setSize(size.width,size.height);
35405         }
35406     },
35407     
35408     
35409     
35410     _resetLayout : function()
35411     {
35412         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35413         this.colWidth = this.el.getWidth();
35414         //this.gutter = this.el.getWidth(); 
35415         
35416         this.measureColumns();
35417
35418         // reset column Y
35419         var i = this.cols;
35420         this.colYs = [];
35421         while (i--) {
35422             this.colYs.push( 0 );
35423         }
35424     
35425         this.maxY = 0;
35426     },
35427
35428     measureColumns : function()
35429     {
35430         this.getContainerWidth();
35431       // if columnWidth is 0, default to outerWidth of first item
35432         if ( !this.columnWidth ) {
35433             var firstItem = this.bricks.first();
35434             Roo.log(firstItem);
35435             this.columnWidth  = this.containerWidth;
35436             if (firstItem && firstItem.attr('originalwidth') ) {
35437                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35438             }
35439             // columnWidth fall back to item of first element
35440             Roo.log("set column width?");
35441                         this.initialColumnWidth = this.columnWidth  ;
35442
35443             // if first elem has no width, default to size of container
35444             
35445         }
35446         
35447         
35448         if (this.initialColumnWidth) {
35449             this.columnWidth = this.initialColumnWidth;
35450         }
35451         
35452         
35453             
35454         // column width is fixed at the top - however if container width get's smaller we should
35455         // reduce it...
35456         
35457         // this bit calcs how man columns..
35458             
35459         var columnWidth = this.columnWidth += this.gutter;
35460       
35461         // calculate columns
35462         var containerWidth = this.containerWidth + this.gutter;
35463         
35464         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35465         // fix rounding errors, typically with gutters
35466         var excess = columnWidth - containerWidth % columnWidth;
35467         
35468         
35469         // if overshoot is less than a pixel, round up, otherwise floor it
35470         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35471         cols = Math[ mathMethod ]( cols );
35472         this.cols = Math.max( cols, 1 );
35473         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35474         
35475          // padding positioning..
35476         var totalColWidth = this.cols * this.columnWidth;
35477         var padavail = this.containerWidth - totalColWidth;
35478         // so for 2 columns - we need 3 'pads'
35479         
35480         var padNeeded = (1+this.cols) * this.padWidth;
35481         
35482         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35483         
35484         this.columnWidth += padExtra
35485         //this.padWidth = Math.floor(padavail /  ( this.cols));
35486         
35487         // adjust colum width so that padding is fixed??
35488         
35489         // we have 3 columns ... total = width * 3
35490         // we have X left over... that should be used by 
35491         
35492         //if (this.expandC) {
35493             
35494         //}
35495         
35496         
35497         
35498     },
35499     
35500     getContainerWidth : function()
35501     {
35502        /* // container is parent if fit width
35503         var container = this.isFitWidth ? this.element.parentNode : this.element;
35504         // check that this.size and size are there
35505         // IE8 triggers resize on body size change, so they might not be
35506         
35507         var size = getSize( container );  //FIXME
35508         this.containerWidth = size && size.innerWidth; //FIXME
35509         */
35510          
35511         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35512         
35513     },
35514     
35515     _getItemLayoutPosition : function( item )  // what is item?
35516     {
35517         // we resize the item to our columnWidth..
35518       
35519         item.setWidth(this.columnWidth);
35520         item.autoBoxAdjust  = false;
35521         
35522         var sz = item.getSize();
35523  
35524         // how many columns does this brick span
35525         var remainder = this.containerWidth % this.columnWidth;
35526         
35527         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35528         // round if off by 1 pixel, otherwise use ceil
35529         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35530         colSpan = Math.min( colSpan, this.cols );
35531         
35532         // normally this should be '1' as we dont' currently allow multi width columns..
35533         
35534         var colGroup = this._getColGroup( colSpan );
35535         // get the minimum Y value from the columns
35536         var minimumY = Math.min.apply( Math, colGroup );
35537         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35538         
35539         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35540          
35541         // position the brick
35542         var position = {
35543             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35544             y: this.currentSize.y + minimumY + this.padHeight
35545         };
35546         
35547         Roo.log(position);
35548         // apply setHeight to necessary columns
35549         var setHeight = minimumY + sz.height + this.padHeight;
35550         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35551         
35552         var setSpan = this.cols + 1 - colGroup.length;
35553         for ( var i = 0; i < setSpan; i++ ) {
35554           this.colYs[ shortColIndex + i ] = setHeight ;
35555         }
35556       
35557         return position;
35558     },
35559     
35560     /**
35561      * @param {Number} colSpan - number of columns the element spans
35562      * @returns {Array} colGroup
35563      */
35564     _getColGroup : function( colSpan )
35565     {
35566         if ( colSpan < 2 ) {
35567           // if brick spans only one column, use all the column Ys
35568           return this.colYs;
35569         }
35570       
35571         var colGroup = [];
35572         // how many different places could this brick fit horizontally
35573         var groupCount = this.cols + 1 - colSpan;
35574         // for each group potential horizontal position
35575         for ( var i = 0; i < groupCount; i++ ) {
35576           // make an array of colY values for that one group
35577           var groupColYs = this.colYs.slice( i, i + colSpan );
35578           // and get the max value of the array
35579           colGroup[i] = Math.max.apply( Math, groupColYs );
35580         }
35581         return colGroup;
35582     },
35583     /*
35584     _manageStamp : function( stamp )
35585     {
35586         var stampSize =  stamp.getSize();
35587         var offset = stamp.getBox();
35588         // get the columns that this stamp affects
35589         var firstX = this.isOriginLeft ? offset.x : offset.right;
35590         var lastX = firstX + stampSize.width;
35591         var firstCol = Math.floor( firstX / this.columnWidth );
35592         firstCol = Math.max( 0, firstCol );
35593         
35594         var lastCol = Math.floor( lastX / this.columnWidth );
35595         // lastCol should not go over if multiple of columnWidth #425
35596         lastCol -= lastX % this.columnWidth ? 0 : 1;
35597         lastCol = Math.min( this.cols - 1, lastCol );
35598         
35599         // set colYs to bottom of the stamp
35600         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35601             stampSize.height;
35602             
35603         for ( var i = firstCol; i <= lastCol; i++ ) {
35604           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35605         }
35606     },
35607     */
35608     
35609     _getContainerSize : function()
35610     {
35611         this.maxY = Math.max.apply( Math, this.colYs );
35612         var size = {
35613             height: this.maxY
35614         };
35615       
35616         if ( this.isFitWidth ) {
35617             size.width = this._getContainerFitWidth();
35618         }
35619       
35620         return size;
35621     },
35622     
35623     _getContainerFitWidth : function()
35624     {
35625         var unusedCols = 0;
35626         // count unused columns
35627         var i = this.cols;
35628         while ( --i ) {
35629           if ( this.colYs[i] !== 0 ) {
35630             break;
35631           }
35632           unusedCols++;
35633         }
35634         // fit container to columns that have been used
35635         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35636     },
35637     
35638     needsResizeLayout : function()
35639     {
35640         var previousWidth = this.containerWidth;
35641         this.getContainerWidth();
35642         return previousWidth !== this.containerWidth;
35643     }
35644  
35645 });
35646
35647  
35648
35649  /*
35650  * - LGPL
35651  *
35652  * element
35653  * 
35654  */
35655
35656 /**
35657  * @class Roo.bootstrap.MasonryBrick
35658  * @extends Roo.bootstrap.Component
35659  * Bootstrap MasonryBrick class
35660  * 
35661  * @constructor
35662  * Create a new MasonryBrick
35663  * @param {Object} config The config object
35664  */
35665
35666 Roo.bootstrap.MasonryBrick = function(config){
35667     
35668     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35669     
35670     Roo.bootstrap.MasonryBrick.register(this);
35671     
35672     this.addEvents({
35673         // raw events
35674         /**
35675          * @event click
35676          * When a MasonryBrick is clcik
35677          * @param {Roo.bootstrap.MasonryBrick} this
35678          * @param {Roo.EventObject} e
35679          */
35680         "click" : true
35681     });
35682 };
35683
35684 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35685     
35686     /**
35687      * @cfg {String} title
35688      */   
35689     title : '',
35690     /**
35691      * @cfg {String} html
35692      */   
35693     html : '',
35694     /**
35695      * @cfg {String} bgimage
35696      */   
35697     bgimage : '',
35698     /**
35699      * @cfg {String} videourl
35700      */   
35701     videourl : '',
35702     /**
35703      * @cfg {String} cls
35704      */   
35705     cls : '',
35706     /**
35707      * @cfg {String} href
35708      */   
35709     href : '',
35710     /**
35711      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35712      */   
35713     size : 'xs',
35714     
35715     /**
35716      * @cfg {String} placetitle (center|bottom)
35717      */   
35718     placetitle : '',
35719     
35720     /**
35721      * @cfg {Boolean} isFitContainer defalut true
35722      */   
35723     isFitContainer : true, 
35724     
35725     /**
35726      * @cfg {Boolean} preventDefault defalut false
35727      */   
35728     preventDefault : false, 
35729     
35730     /**
35731      * @cfg {Boolean} inverse defalut false
35732      */   
35733     maskInverse : false, 
35734     
35735     getAutoCreate : function()
35736     {
35737         if(!this.isFitContainer){
35738             return this.getSplitAutoCreate();
35739         }
35740         
35741         var cls = 'masonry-brick masonry-brick-full';
35742         
35743         if(this.href.length){
35744             cls += ' masonry-brick-link';
35745         }
35746         
35747         if(this.bgimage.length){
35748             cls += ' masonry-brick-image';
35749         }
35750         
35751         if(this.maskInverse){
35752             cls += ' mask-inverse';
35753         }
35754         
35755         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35756             cls += ' enable-mask';
35757         }
35758         
35759         if(this.size){
35760             cls += ' masonry-' + this.size + '-brick';
35761         }
35762         
35763         if(this.placetitle.length){
35764             
35765             switch (this.placetitle) {
35766                 case 'center' :
35767                     cls += ' masonry-center-title';
35768                     break;
35769                 case 'bottom' :
35770                     cls += ' masonry-bottom-title';
35771                     break;
35772                 default:
35773                     break;
35774             }
35775             
35776         } else {
35777             if(!this.html.length && !this.bgimage.length){
35778                 cls += ' masonry-center-title';
35779             }
35780
35781             if(!this.html.length && this.bgimage.length){
35782                 cls += ' masonry-bottom-title';
35783             }
35784         }
35785         
35786         if(this.cls){
35787             cls += ' ' + this.cls;
35788         }
35789         
35790         var cfg = {
35791             tag: (this.href.length) ? 'a' : 'div',
35792             cls: cls,
35793             cn: [
35794                 {
35795                     tag: 'div',
35796                     cls: 'masonry-brick-mask'
35797                 },
35798                 {
35799                     tag: 'div',
35800                     cls: 'masonry-brick-paragraph',
35801                     cn: []
35802                 }
35803             ]
35804         };
35805         
35806         if(this.href.length){
35807             cfg.href = this.href;
35808         }
35809         
35810         var cn = cfg.cn[1].cn;
35811         
35812         if(this.title.length){
35813             cn.push({
35814                 tag: 'h4',
35815                 cls: 'masonry-brick-title',
35816                 html: this.title
35817             });
35818         }
35819         
35820         if(this.html.length){
35821             cn.push({
35822                 tag: 'p',
35823                 cls: 'masonry-brick-text',
35824                 html: this.html
35825             });
35826         }
35827         
35828         if (!this.title.length && !this.html.length) {
35829             cfg.cn[1].cls += ' hide';
35830         }
35831         
35832         if(this.bgimage.length){
35833             cfg.cn.push({
35834                 tag: 'img',
35835                 cls: 'masonry-brick-image-view',
35836                 src: this.bgimage
35837             });
35838         }
35839         
35840         if(this.videourl.length){
35841             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35842             // youtube support only?
35843             cfg.cn.push({
35844                 tag: 'iframe',
35845                 cls: 'masonry-brick-image-view',
35846                 src: vurl,
35847                 frameborder : 0,
35848                 allowfullscreen : true
35849             });
35850         }
35851         
35852         return cfg;
35853         
35854     },
35855     
35856     getSplitAutoCreate : function()
35857     {
35858         var cls = 'masonry-brick masonry-brick-split';
35859         
35860         if(this.href.length){
35861             cls += ' masonry-brick-link';
35862         }
35863         
35864         if(this.bgimage.length){
35865             cls += ' masonry-brick-image';
35866         }
35867         
35868         if(this.size){
35869             cls += ' masonry-' + this.size + '-brick';
35870         }
35871         
35872         switch (this.placetitle) {
35873             case 'center' :
35874                 cls += ' masonry-center-title';
35875                 break;
35876             case 'bottom' :
35877                 cls += ' masonry-bottom-title';
35878                 break;
35879             default:
35880                 if(!this.bgimage.length){
35881                     cls += ' masonry-center-title';
35882                 }
35883
35884                 if(this.bgimage.length){
35885                     cls += ' masonry-bottom-title';
35886                 }
35887                 break;
35888         }
35889         
35890         if(this.cls){
35891             cls += ' ' + this.cls;
35892         }
35893         
35894         var cfg = {
35895             tag: (this.href.length) ? 'a' : 'div',
35896             cls: cls,
35897             cn: [
35898                 {
35899                     tag: 'div',
35900                     cls: 'masonry-brick-split-head',
35901                     cn: [
35902                         {
35903                             tag: 'div',
35904                             cls: 'masonry-brick-paragraph',
35905                             cn: []
35906                         }
35907                     ]
35908                 },
35909                 {
35910                     tag: 'div',
35911                     cls: 'masonry-brick-split-body',
35912                     cn: []
35913                 }
35914             ]
35915         };
35916         
35917         if(this.href.length){
35918             cfg.href = this.href;
35919         }
35920         
35921         if(this.title.length){
35922             cfg.cn[0].cn[0].cn.push({
35923                 tag: 'h4',
35924                 cls: 'masonry-brick-title',
35925                 html: this.title
35926             });
35927         }
35928         
35929         if(this.html.length){
35930             cfg.cn[1].cn.push({
35931                 tag: 'p',
35932                 cls: 'masonry-brick-text',
35933                 html: this.html
35934             });
35935         }
35936
35937         if(this.bgimage.length){
35938             cfg.cn[0].cn.push({
35939                 tag: 'img',
35940                 cls: 'masonry-brick-image-view',
35941                 src: this.bgimage
35942             });
35943         }
35944         
35945         if(this.videourl.length){
35946             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35947             // youtube support only?
35948             cfg.cn[0].cn.cn.push({
35949                 tag: 'iframe',
35950                 cls: 'masonry-brick-image-view',
35951                 src: vurl,
35952                 frameborder : 0,
35953                 allowfullscreen : true
35954             });
35955         }
35956         
35957         return cfg;
35958     },
35959     
35960     initEvents: function() 
35961     {
35962         switch (this.size) {
35963             case 'xs' :
35964                 this.x = 1;
35965                 this.y = 1;
35966                 break;
35967             case 'sm' :
35968                 this.x = 2;
35969                 this.y = 2;
35970                 break;
35971             case 'md' :
35972             case 'md-left' :
35973             case 'md-right' :
35974                 this.x = 3;
35975                 this.y = 3;
35976                 break;
35977             case 'tall' :
35978                 this.x = 2;
35979                 this.y = 3;
35980                 break;
35981             case 'wide' :
35982                 this.x = 3;
35983                 this.y = 2;
35984                 break;
35985             case 'wide-thin' :
35986                 this.x = 3;
35987                 this.y = 1;
35988                 break;
35989                         
35990             default :
35991                 break;
35992         }
35993         
35994         if(Roo.isTouch){
35995             this.el.on('touchstart', this.onTouchStart, this);
35996             this.el.on('touchmove', this.onTouchMove, this);
35997             this.el.on('touchend', this.onTouchEnd, this);
35998             this.el.on('contextmenu', this.onContextMenu, this);
35999         } else {
36000             this.el.on('mouseenter'  ,this.enter, this);
36001             this.el.on('mouseleave', this.leave, this);
36002             this.el.on('click', this.onClick, this);
36003         }
36004         
36005         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
36006             this.parent().bricks.push(this);   
36007         }
36008         
36009     },
36010     
36011     onClick: function(e, el)
36012     {
36013         var time = this.endTimer - this.startTimer;
36014         // Roo.log(e.preventDefault());
36015         if(Roo.isTouch){
36016             if(time > 1000){
36017                 e.preventDefault();
36018                 return;
36019             }
36020         }
36021         
36022         if(!this.preventDefault){
36023             return;
36024         }
36025         
36026         e.preventDefault();
36027         
36028         if (this.activeClass != '') {
36029             this.selectBrick();
36030         }
36031         
36032         this.fireEvent('click', this, e);
36033     },
36034     
36035     enter: function(e, el)
36036     {
36037         e.preventDefault();
36038         
36039         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
36040             return;
36041         }
36042         
36043         if(this.bgimage.length && this.html.length){
36044             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36045         }
36046     },
36047     
36048     leave: function(e, el)
36049     {
36050         e.preventDefault();
36051         
36052         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
36053             return;
36054         }
36055         
36056         if(this.bgimage.length && this.html.length){
36057             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36058         }
36059     },
36060     
36061     onTouchStart: function(e, el)
36062     {
36063 //        e.preventDefault();
36064         
36065         this.touchmoved = false;
36066         
36067         if(!this.isFitContainer){
36068             return;
36069         }
36070         
36071         if(!this.bgimage.length || !this.html.length){
36072             return;
36073         }
36074         
36075         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
36076         
36077         this.timer = new Date().getTime();
36078         
36079     },
36080     
36081     onTouchMove: function(e, el)
36082     {
36083         this.touchmoved = true;
36084     },
36085     
36086     onContextMenu : function(e,el)
36087     {
36088         e.preventDefault();
36089         e.stopPropagation();
36090         return false;
36091     },
36092     
36093     onTouchEnd: function(e, el)
36094     {
36095 //        e.preventDefault();
36096         
36097         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
36098         
36099             this.leave(e,el);
36100             
36101             return;
36102         }
36103         
36104         if(!this.bgimage.length || !this.html.length){
36105             
36106             if(this.href.length){
36107                 window.location.href = this.href;
36108             }
36109             
36110             return;
36111         }
36112         
36113         if(!this.isFitContainer){
36114             return;
36115         }
36116         
36117         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
36118         
36119         window.location.href = this.href;
36120     },
36121     
36122     //selection on single brick only
36123     selectBrick : function() {
36124         
36125         if (!this.parentId) {
36126             return;
36127         }
36128         
36129         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
36130         var index = m.selectedBrick.indexOf(this.id);
36131         
36132         if ( index > -1) {
36133             m.selectedBrick.splice(index,1);
36134             this.el.removeClass(this.activeClass);
36135             return;
36136         }
36137         
36138         for(var i = 0; i < m.selectedBrick.length; i++) {
36139             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
36140             b.el.removeClass(b.activeClass);
36141         }
36142         
36143         m.selectedBrick = [];
36144         
36145         m.selectedBrick.push(this.id);
36146         this.el.addClass(this.activeClass);
36147         return;
36148     },
36149     
36150     isSelected : function(){
36151         return this.el.hasClass(this.activeClass);
36152         
36153     }
36154 });
36155
36156 Roo.apply(Roo.bootstrap.MasonryBrick, {
36157     
36158     //groups: {},
36159     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
36160      /**
36161     * register a Masonry Brick
36162     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
36163     */
36164     
36165     register : function(brick)
36166     {
36167         //this.groups[brick.id] = brick;
36168         this.groups.add(brick.id, brick);
36169     },
36170     /**
36171     * fetch a  masonry brick based on the masonry brick ID
36172     * @param {string} the masonry brick to add
36173     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
36174     */
36175     
36176     get: function(brick_id) 
36177     {
36178         // if (typeof(this.groups[brick_id]) == 'undefined') {
36179         //     return false;
36180         // }
36181         // return this.groups[brick_id] ;
36182         
36183         if(this.groups.key(brick_id)) {
36184             return this.groups.key(brick_id);
36185         }
36186         
36187         return false;
36188     }
36189     
36190     
36191     
36192 });
36193
36194  /*
36195  * - LGPL
36196  *
36197  * element
36198  * 
36199  */
36200
36201 /**
36202  * @class Roo.bootstrap.Brick
36203  * @extends Roo.bootstrap.Component
36204  * Bootstrap Brick class
36205  * 
36206  * @constructor
36207  * Create a new Brick
36208  * @param {Object} config The config object
36209  */
36210
36211 Roo.bootstrap.Brick = function(config){
36212     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
36213     
36214     this.addEvents({
36215         // raw events
36216         /**
36217          * @event click
36218          * When a Brick is click
36219          * @param {Roo.bootstrap.Brick} this
36220          * @param {Roo.EventObject} e
36221          */
36222         "click" : true
36223     });
36224 };
36225
36226 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
36227     
36228     /**
36229      * @cfg {String} title
36230      */   
36231     title : '',
36232     /**
36233      * @cfg {String} html
36234      */   
36235     html : '',
36236     /**
36237      * @cfg {String} bgimage
36238      */   
36239     bgimage : '',
36240     /**
36241      * @cfg {String} cls
36242      */   
36243     cls : '',
36244     /**
36245      * @cfg {String} href
36246      */   
36247     href : '',
36248     /**
36249      * @cfg {String} video
36250      */   
36251     video : '',
36252     /**
36253      * @cfg {Boolean} square
36254      */   
36255     square : true,
36256     
36257     getAutoCreate : function()
36258     {
36259         var cls = 'roo-brick';
36260         
36261         if(this.href.length){
36262             cls += ' roo-brick-link';
36263         }
36264         
36265         if(this.bgimage.length){
36266             cls += ' roo-brick-image';
36267         }
36268         
36269         if(!this.html.length && !this.bgimage.length){
36270             cls += ' roo-brick-center-title';
36271         }
36272         
36273         if(!this.html.length && this.bgimage.length){
36274             cls += ' roo-brick-bottom-title';
36275         }
36276         
36277         if(this.cls){
36278             cls += ' ' + this.cls;
36279         }
36280         
36281         var cfg = {
36282             tag: (this.href.length) ? 'a' : 'div',
36283             cls: cls,
36284             cn: [
36285                 {
36286                     tag: 'div',
36287                     cls: 'roo-brick-paragraph',
36288                     cn: []
36289                 }
36290             ]
36291         };
36292         
36293         if(this.href.length){
36294             cfg.href = this.href;
36295         }
36296         
36297         var cn = cfg.cn[0].cn;
36298         
36299         if(this.title.length){
36300             cn.push({
36301                 tag: 'h4',
36302                 cls: 'roo-brick-title',
36303                 html: this.title
36304             });
36305         }
36306         
36307         if(this.html.length){
36308             cn.push({
36309                 tag: 'p',
36310                 cls: 'roo-brick-text',
36311                 html: this.html
36312             });
36313         } else {
36314             cn.cls += ' hide';
36315         }
36316         
36317         if(this.bgimage.length){
36318             cfg.cn.push({
36319                 tag: 'img',
36320                 cls: 'roo-brick-image-view',
36321                 src: this.bgimage
36322             });
36323         }
36324         
36325         return cfg;
36326     },
36327     
36328     initEvents: function() 
36329     {
36330         if(this.title.length || this.html.length){
36331             this.el.on('mouseenter'  ,this.enter, this);
36332             this.el.on('mouseleave', this.leave, this);
36333         }
36334         
36335         Roo.EventManager.onWindowResize(this.resize, this); 
36336         
36337         if(this.bgimage.length){
36338             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
36339             this.imageEl.on('load', this.onImageLoad, this);
36340             return;
36341         }
36342         
36343         this.resize();
36344     },
36345     
36346     onImageLoad : function()
36347     {
36348         this.resize();
36349     },
36350     
36351     resize : function()
36352     {
36353         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
36354         
36355         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
36356         
36357         if(this.bgimage.length){
36358             var image = this.el.select('.roo-brick-image-view', true).first();
36359             
36360             image.setWidth(paragraph.getWidth());
36361             
36362             if(this.square){
36363                 image.setHeight(paragraph.getWidth());
36364             }
36365             
36366             this.el.setHeight(image.getHeight());
36367             paragraph.setHeight(image.getHeight());
36368             
36369         }
36370         
36371     },
36372     
36373     enter: function(e, el)
36374     {
36375         e.preventDefault();
36376         
36377         if(this.bgimage.length){
36378             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36379             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36380         }
36381     },
36382     
36383     leave: function(e, el)
36384     {
36385         e.preventDefault();
36386         
36387         if(this.bgimage.length){
36388             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36389             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36390         }
36391     }
36392     
36393 });
36394
36395  
36396
36397  /*
36398  * - LGPL
36399  *
36400  * Number field 
36401  */
36402
36403 /**
36404  * @class Roo.bootstrap.NumberField
36405  * @extends Roo.bootstrap.Input
36406  * Bootstrap NumberField class
36407  * 
36408  * 
36409  * 
36410  * 
36411  * @constructor
36412  * Create a new NumberField
36413  * @param {Object} config The config object
36414  */
36415
36416 Roo.bootstrap.NumberField = function(config){
36417     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36418 };
36419
36420 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36421     
36422     /**
36423      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36424      */
36425     allowDecimals : true,
36426     /**
36427      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36428      */
36429     decimalSeparator : ".",
36430     /**
36431      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36432      */
36433     decimalPrecision : 2,
36434     /**
36435      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36436      */
36437     allowNegative : true,
36438     
36439     /**
36440      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36441      */
36442     allowZero: true,
36443     /**
36444      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36445      */
36446     minValue : Number.NEGATIVE_INFINITY,
36447     /**
36448      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36449      */
36450     maxValue : Number.MAX_VALUE,
36451     /**
36452      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36453      */
36454     minText : "The minimum value for this field is {0}",
36455     /**
36456      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36457      */
36458     maxText : "The maximum value for this field is {0}",
36459     /**
36460      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36461      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36462      */
36463     nanText : "{0} is not a valid number",
36464     /**
36465      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36466      */
36467     thousandsDelimiter : false,
36468     /**
36469      * @cfg {String} valueAlign alignment of value
36470      */
36471     valueAlign : "left",
36472
36473     getAutoCreate : function()
36474     {
36475         var hiddenInput = {
36476             tag: 'input',
36477             type: 'hidden',
36478             id: Roo.id(),
36479             cls: 'hidden-number-input'
36480         };
36481         
36482         if (this.name) {
36483             hiddenInput.name = this.name;
36484         }
36485         
36486         this.name = '';
36487         
36488         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36489         
36490         this.name = hiddenInput.name;
36491         
36492         if(cfg.cn.length > 0) {
36493             cfg.cn.push(hiddenInput);
36494         }
36495         
36496         return cfg;
36497     },
36498
36499     // private
36500     initEvents : function()
36501     {   
36502         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36503         
36504         var allowed = "0123456789";
36505         
36506         if(this.allowDecimals){
36507             allowed += this.decimalSeparator;
36508         }
36509         
36510         if(this.allowNegative){
36511             allowed += "-";
36512         }
36513         
36514         if(this.thousandsDelimiter) {
36515             allowed += ",";
36516         }
36517         
36518         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36519         
36520         var keyPress = function(e){
36521             
36522             var k = e.getKey();
36523             
36524             var c = e.getCharCode();
36525             
36526             if(
36527                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36528                     allowed.indexOf(String.fromCharCode(c)) === -1
36529             ){
36530                 e.stopEvent();
36531                 return;
36532             }
36533             
36534             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36535                 return;
36536             }
36537             
36538             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36539                 e.stopEvent();
36540             }
36541         };
36542         
36543         this.el.on("keypress", keyPress, this);
36544     },
36545     
36546     validateValue : function(value)
36547     {
36548         
36549         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36550             return false;
36551         }
36552         
36553         var num = this.parseValue(value);
36554         
36555         if(isNaN(num)){
36556             this.markInvalid(String.format(this.nanText, value));
36557             return false;
36558         }
36559         
36560         if(num < this.minValue){
36561             this.markInvalid(String.format(this.minText, this.minValue));
36562             return false;
36563         }
36564         
36565         if(num > this.maxValue){
36566             this.markInvalid(String.format(this.maxText, this.maxValue));
36567             return false;
36568         }
36569         
36570         return true;
36571     },
36572
36573     getValue : function()
36574     {
36575         var v = this.hiddenEl().getValue();
36576         
36577         return this.fixPrecision(this.parseValue(v));
36578     },
36579
36580     parseValue : function(value)
36581     {
36582         if(this.thousandsDelimiter) {
36583             value += "";
36584             r = new RegExp(",", "g");
36585             value = value.replace(r, "");
36586         }
36587         
36588         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36589         return isNaN(value) ? '' : value;
36590     },
36591
36592     fixPrecision : function(value)
36593     {
36594         if(this.thousandsDelimiter) {
36595             value += "";
36596             r = new RegExp(",", "g");
36597             value = value.replace(r, "");
36598         }
36599         
36600         var nan = isNaN(value);
36601         
36602         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36603             return nan ? '' : value;
36604         }
36605         return parseFloat(value).toFixed(this.decimalPrecision);
36606     },
36607
36608     setValue : function(v)
36609     {
36610         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36611         
36612         this.value = v;
36613         
36614         if(this.rendered){
36615             
36616             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36617             
36618             this.inputEl().dom.value = (v == '') ? '' :
36619                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36620             
36621             if(!this.allowZero && v === '0') {
36622                 this.hiddenEl().dom.value = '';
36623                 this.inputEl().dom.value = '';
36624             }
36625             
36626             this.validate();
36627         }
36628     },
36629
36630     decimalPrecisionFcn : function(v)
36631     {
36632         return Math.floor(v);
36633     },
36634
36635     beforeBlur : function()
36636     {
36637         var v = this.parseValue(this.getRawValue());
36638         
36639         if(v || v === 0 || v === ''){
36640             this.setValue(v);
36641         }
36642     },
36643     
36644     hiddenEl : function()
36645     {
36646         return this.el.select('input.hidden-number-input',true).first();
36647     }
36648     
36649 });
36650
36651  
36652
36653 /*
36654 * Licence: LGPL
36655 */
36656
36657 /**
36658  * @class Roo.bootstrap.DocumentSlider
36659  * @extends Roo.bootstrap.Component
36660  * Bootstrap DocumentSlider class
36661  * 
36662  * @constructor
36663  * Create a new DocumentViewer
36664  * @param {Object} config The config object
36665  */
36666
36667 Roo.bootstrap.DocumentSlider = function(config){
36668     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36669     
36670     this.files = [];
36671     
36672     this.addEvents({
36673         /**
36674          * @event initial
36675          * Fire after initEvent
36676          * @param {Roo.bootstrap.DocumentSlider} this
36677          */
36678         "initial" : true,
36679         /**
36680          * @event update
36681          * Fire after update
36682          * @param {Roo.bootstrap.DocumentSlider} this
36683          */
36684         "update" : true,
36685         /**
36686          * @event click
36687          * Fire after click
36688          * @param {Roo.bootstrap.DocumentSlider} this
36689          */
36690         "click" : true
36691     });
36692 };
36693
36694 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36695     
36696     files : false,
36697     
36698     indicator : 0,
36699     
36700     getAutoCreate : function()
36701     {
36702         var cfg = {
36703             tag : 'div',
36704             cls : 'roo-document-slider',
36705             cn : [
36706                 {
36707                     tag : 'div',
36708                     cls : 'roo-document-slider-header',
36709                     cn : [
36710                         {
36711                             tag : 'div',
36712                             cls : 'roo-document-slider-header-title'
36713                         }
36714                     ]
36715                 },
36716                 {
36717                     tag : 'div',
36718                     cls : 'roo-document-slider-body',
36719                     cn : [
36720                         {
36721                             tag : 'div',
36722                             cls : 'roo-document-slider-prev',
36723                             cn : [
36724                                 {
36725                                     tag : 'i',
36726                                     cls : 'fa fa-chevron-left'
36727                                 }
36728                             ]
36729                         },
36730                         {
36731                             tag : 'div',
36732                             cls : 'roo-document-slider-thumb',
36733                             cn : [
36734                                 {
36735                                     tag : 'img',
36736                                     cls : 'roo-document-slider-image'
36737                                 }
36738                             ]
36739                         },
36740                         {
36741                             tag : 'div',
36742                             cls : 'roo-document-slider-next',
36743                             cn : [
36744                                 {
36745                                     tag : 'i',
36746                                     cls : 'fa fa-chevron-right'
36747                                 }
36748                             ]
36749                         }
36750                     ]
36751                 }
36752             ]
36753         };
36754         
36755         return cfg;
36756     },
36757     
36758     initEvents : function()
36759     {
36760         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36761         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36762         
36763         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36764         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36765         
36766         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36767         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36768         
36769         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36770         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36771         
36772         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36773         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36774         
36775         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36776         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36777         
36778         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36779         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36780         
36781         this.thumbEl.on('click', this.onClick, this);
36782         
36783         this.prevIndicator.on('click', this.prev, this);
36784         
36785         this.nextIndicator.on('click', this.next, this);
36786         
36787     },
36788     
36789     initial : function()
36790     {
36791         if(this.files.length){
36792             this.indicator = 1;
36793             this.update()
36794         }
36795         
36796         this.fireEvent('initial', this);
36797     },
36798     
36799     update : function()
36800     {
36801         this.imageEl.attr('src', this.files[this.indicator - 1]);
36802         
36803         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36804         
36805         this.prevIndicator.show();
36806         
36807         if(this.indicator == 1){
36808             this.prevIndicator.hide();
36809         }
36810         
36811         this.nextIndicator.show();
36812         
36813         if(this.indicator == this.files.length){
36814             this.nextIndicator.hide();
36815         }
36816         
36817         this.thumbEl.scrollTo('top');
36818         
36819         this.fireEvent('update', this);
36820     },
36821     
36822     onClick : function(e)
36823     {
36824         e.preventDefault();
36825         
36826         this.fireEvent('click', this);
36827     },
36828     
36829     prev : function(e)
36830     {
36831         e.preventDefault();
36832         
36833         this.indicator = Math.max(1, this.indicator - 1);
36834         
36835         this.update();
36836     },
36837     
36838     next : function(e)
36839     {
36840         e.preventDefault();
36841         
36842         this.indicator = Math.min(this.files.length, this.indicator + 1);
36843         
36844         this.update();
36845     }
36846 });
36847 /*
36848  * - LGPL
36849  *
36850  * RadioSet
36851  *
36852  *
36853  */
36854
36855 /**
36856  * @class Roo.bootstrap.RadioSet
36857  * @extends Roo.bootstrap.Input
36858  * Bootstrap RadioSet class
36859  * @cfg {String} indicatorpos (left|right) default left
36860  * @cfg {Boolean} inline (true|false) inline the element (default true)
36861  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36862  * @constructor
36863  * Create a new RadioSet
36864  * @param {Object} config The config object
36865  */
36866
36867 Roo.bootstrap.RadioSet = function(config){
36868     
36869     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36870     
36871     this.radioes = [];
36872     
36873     Roo.bootstrap.RadioSet.register(this);
36874     
36875     this.addEvents({
36876         /**
36877         * @event check
36878         * Fires when the element is checked or unchecked.
36879         * @param {Roo.bootstrap.RadioSet} this This radio
36880         * @param {Roo.bootstrap.Radio} item The checked item
36881         */
36882        check : true,
36883        /**
36884         * @event click
36885         * Fires when the element is click.
36886         * @param {Roo.bootstrap.RadioSet} this This radio set
36887         * @param {Roo.bootstrap.Radio} item The checked item
36888         * @param {Roo.EventObject} e The event object
36889         */
36890        click : true
36891     });
36892     
36893 };
36894
36895 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36896
36897     radioes : false,
36898     
36899     inline : true,
36900     
36901     weight : '',
36902     
36903     indicatorpos : 'left',
36904     
36905     getAutoCreate : function()
36906     {
36907         var label = {
36908             tag : 'label',
36909             cls : 'roo-radio-set-label',
36910             cn : [
36911                 {
36912                     tag : 'span',
36913                     html : this.fieldLabel
36914                 }
36915             ]
36916         };
36917         if (Roo.bootstrap.version == 3) {
36918             
36919             
36920             if(this.indicatorpos == 'left'){
36921                 label.cn.unshift({
36922                     tag : 'i',
36923                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36924                     tooltip : 'This field is required'
36925                 });
36926             } else {
36927                 label.cn.push({
36928                     tag : 'i',
36929                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36930                     tooltip : 'This field is required'
36931                 });
36932             }
36933         }
36934         var items = {
36935             tag : 'div',
36936             cls : 'roo-radio-set-items'
36937         };
36938         
36939         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36940         
36941         if (align === 'left' && this.fieldLabel.length) {
36942             
36943             items = {
36944                 cls : "roo-radio-set-right", 
36945                 cn: [
36946                     items
36947                 ]
36948             };
36949             
36950             if(this.labelWidth > 12){
36951                 label.style = "width: " + this.labelWidth + 'px';
36952             }
36953             
36954             if(this.labelWidth < 13 && this.labelmd == 0){
36955                 this.labelmd = this.labelWidth;
36956             }
36957             
36958             if(this.labellg > 0){
36959                 label.cls += ' col-lg-' + this.labellg;
36960                 items.cls += ' col-lg-' + (12 - this.labellg);
36961             }
36962             
36963             if(this.labelmd > 0){
36964                 label.cls += ' col-md-' + this.labelmd;
36965                 items.cls += ' col-md-' + (12 - this.labelmd);
36966             }
36967             
36968             if(this.labelsm > 0){
36969                 label.cls += ' col-sm-' + this.labelsm;
36970                 items.cls += ' col-sm-' + (12 - this.labelsm);
36971             }
36972             
36973             if(this.labelxs > 0){
36974                 label.cls += ' col-xs-' + this.labelxs;
36975                 items.cls += ' col-xs-' + (12 - this.labelxs);
36976             }
36977         }
36978         
36979         var cfg = {
36980             tag : 'div',
36981             cls : 'roo-radio-set',
36982             cn : [
36983                 {
36984                     tag : 'input',
36985                     cls : 'roo-radio-set-input',
36986                     type : 'hidden',
36987                     name : this.name,
36988                     value : this.value ? this.value :  ''
36989                 },
36990                 label,
36991                 items
36992             ]
36993         };
36994         
36995         if(this.weight.length){
36996             cfg.cls += ' roo-radio-' + this.weight;
36997         }
36998         
36999         if(this.inline) {
37000             cfg.cls += ' roo-radio-set-inline';
37001         }
37002         
37003         var settings=this;
37004         ['xs','sm','md','lg'].map(function(size){
37005             if (settings[size]) {
37006                 cfg.cls += ' col-' + size + '-' + settings[size];
37007             }
37008         });
37009         
37010         return cfg;
37011         
37012     },
37013
37014     initEvents : function()
37015     {
37016         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
37017         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
37018         
37019         if(!this.fieldLabel.length){
37020             this.labelEl.hide();
37021         }
37022         
37023         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
37024         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
37025         
37026         this.indicator = this.indicatorEl();
37027         
37028         if(this.indicator){
37029             this.indicator.addClass('invisible');
37030         }
37031         
37032         this.originalValue = this.getValue();
37033         
37034     },
37035     
37036     inputEl: function ()
37037     {
37038         return this.el.select('.roo-radio-set-input', true).first();
37039     },
37040     
37041     getChildContainer : function()
37042     {
37043         return this.itemsEl;
37044     },
37045     
37046     register : function(item)
37047     {
37048         this.radioes.push(item);
37049         
37050     },
37051     
37052     validate : function()
37053     {   
37054         if(this.getVisibilityEl().hasClass('hidden')){
37055             return true;
37056         }
37057         
37058         var valid = false;
37059         
37060         Roo.each(this.radioes, function(i){
37061             if(!i.checked){
37062                 return;
37063             }
37064             
37065             valid = true;
37066             return false;
37067         });
37068         
37069         if(this.allowBlank) {
37070             return true;
37071         }
37072         
37073         if(this.disabled || valid){
37074             this.markValid();
37075             return true;
37076         }
37077         
37078         this.markInvalid();
37079         return false;
37080         
37081     },
37082     
37083     markValid : function()
37084     {
37085         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37086             this.indicatorEl().removeClass('visible');
37087             this.indicatorEl().addClass('invisible');
37088         }
37089         
37090         
37091         if (Roo.bootstrap.version == 3) {
37092             this.el.removeClass([this.invalidClass, this.validClass]);
37093             this.el.addClass(this.validClass);
37094         } else {
37095             this.el.removeClass(['is-invalid','is-valid']);
37096             this.el.addClass(['is-valid']);
37097         }
37098         this.fireEvent('valid', this);
37099     },
37100     
37101     markInvalid : function(msg)
37102     {
37103         if(this.allowBlank || this.disabled){
37104             return;
37105         }
37106         
37107         if(this.labelEl.isVisible(true) && this.indicatorEl()){
37108             this.indicatorEl().removeClass('invisible');
37109             this.indicatorEl().addClass('visible');
37110         }
37111         if (Roo.bootstrap.version == 3) {
37112             this.el.removeClass([this.invalidClass, this.validClass]);
37113             this.el.addClass(this.invalidClass);
37114         } else {
37115             this.el.removeClass(['is-invalid','is-valid']);
37116             this.el.addClass(['is-invalid']);
37117         }
37118         
37119         this.fireEvent('invalid', this, msg);
37120         
37121     },
37122     
37123     setValue : function(v, suppressEvent)
37124     {   
37125         if(this.value === v){
37126             return;
37127         }
37128         
37129         this.value = v;
37130         
37131         if(this.rendered){
37132             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
37133         }
37134         
37135         Roo.each(this.radioes, function(i){
37136             i.checked = false;
37137             i.el.removeClass('checked');
37138         });
37139         
37140         Roo.each(this.radioes, function(i){
37141             
37142             if(i.value === v || i.value.toString() === v.toString()){
37143                 i.checked = true;
37144                 i.el.addClass('checked');
37145                 
37146                 if(suppressEvent !== true){
37147                     this.fireEvent('check', this, i);
37148                 }
37149                 
37150                 return false;
37151             }
37152             
37153         }, this);
37154         
37155         this.validate();
37156     },
37157     
37158     clearInvalid : function(){
37159         
37160         if(!this.el || this.preventMark){
37161             return;
37162         }
37163         
37164         this.el.removeClass([this.invalidClass]);
37165         
37166         this.fireEvent('valid', this);
37167     }
37168     
37169 });
37170
37171 Roo.apply(Roo.bootstrap.RadioSet, {
37172     
37173     groups: {},
37174     
37175     register : function(set)
37176     {
37177         this.groups[set.name] = set;
37178     },
37179     
37180     get: function(name) 
37181     {
37182         if (typeof(this.groups[name]) == 'undefined') {
37183             return false;
37184         }
37185         
37186         return this.groups[name] ;
37187     }
37188     
37189 });
37190 /*
37191  * Based on:
37192  * Ext JS Library 1.1.1
37193  * Copyright(c) 2006-2007, Ext JS, LLC.
37194  *
37195  * Originally Released Under LGPL - original licence link has changed is not relivant.
37196  *
37197  * Fork - LGPL
37198  * <script type="text/javascript">
37199  */
37200
37201
37202 /**
37203  * @class Roo.bootstrap.SplitBar
37204  * @extends Roo.util.Observable
37205  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
37206  * <br><br>
37207  * Usage:
37208  * <pre><code>
37209 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
37210                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
37211 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
37212 split.minSize = 100;
37213 split.maxSize = 600;
37214 split.animate = true;
37215 split.on('moved', splitterMoved);
37216 </code></pre>
37217  * @constructor
37218  * Create a new SplitBar
37219  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
37220  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
37221  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37222  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
37223                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
37224                         position of the SplitBar).
37225  */
37226 Roo.bootstrap.SplitBar = function(cfg){
37227     
37228     /** @private */
37229     
37230     //{
37231     //  dragElement : elm
37232     //  resizingElement: el,
37233         // optional..
37234     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
37235     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
37236         // existingProxy ???
37237     //}
37238     
37239     this.el = Roo.get(cfg.dragElement, true);
37240     this.el.dom.unselectable = "on";
37241     /** @private */
37242     this.resizingEl = Roo.get(cfg.resizingElement, true);
37243
37244     /**
37245      * @private
37246      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
37247      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
37248      * @type Number
37249      */
37250     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
37251     
37252     /**
37253      * The minimum size of the resizing element. (Defaults to 0)
37254      * @type Number
37255      */
37256     this.minSize = 0;
37257     
37258     /**
37259      * The maximum size of the resizing element. (Defaults to 2000)
37260      * @type Number
37261      */
37262     this.maxSize = 2000;
37263     
37264     /**
37265      * Whether to animate the transition to the new size
37266      * @type Boolean
37267      */
37268     this.animate = false;
37269     
37270     /**
37271      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
37272      * @type Boolean
37273      */
37274     this.useShim = false;
37275     
37276     /** @private */
37277     this.shim = null;
37278     
37279     if(!cfg.existingProxy){
37280         /** @private */
37281         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
37282     }else{
37283         this.proxy = Roo.get(cfg.existingProxy).dom;
37284     }
37285     /** @private */
37286     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
37287     
37288     /** @private */
37289     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
37290     
37291     /** @private */
37292     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
37293     
37294     /** @private */
37295     this.dragSpecs = {};
37296     
37297     /**
37298      * @private The adapter to use to positon and resize elements
37299      */
37300     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37301     this.adapter.init(this);
37302     
37303     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37304         /** @private */
37305         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
37306         this.el.addClass("roo-splitbar-h");
37307     }else{
37308         /** @private */
37309         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
37310         this.el.addClass("roo-splitbar-v");
37311     }
37312     
37313     this.addEvents({
37314         /**
37315          * @event resize
37316          * Fires when the splitter is moved (alias for {@link #event-moved})
37317          * @param {Roo.bootstrap.SplitBar} this
37318          * @param {Number} newSize the new width or height
37319          */
37320         "resize" : true,
37321         /**
37322          * @event moved
37323          * Fires when the splitter is moved
37324          * @param {Roo.bootstrap.SplitBar} this
37325          * @param {Number} newSize the new width or height
37326          */
37327         "moved" : true,
37328         /**
37329          * @event beforeresize
37330          * Fires before the splitter is dragged
37331          * @param {Roo.bootstrap.SplitBar} this
37332          */
37333         "beforeresize" : true,
37334
37335         "beforeapply" : true
37336     });
37337
37338     Roo.util.Observable.call(this);
37339 };
37340
37341 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
37342     onStartProxyDrag : function(x, y){
37343         this.fireEvent("beforeresize", this);
37344         if(!this.overlay){
37345             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
37346             o.unselectable();
37347             o.enableDisplayMode("block");
37348             // all splitbars share the same overlay
37349             Roo.bootstrap.SplitBar.prototype.overlay = o;
37350         }
37351         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
37352         this.overlay.show();
37353         Roo.get(this.proxy).setDisplayed("block");
37354         var size = this.adapter.getElementSize(this);
37355         this.activeMinSize = this.getMinimumSize();;
37356         this.activeMaxSize = this.getMaximumSize();;
37357         var c1 = size - this.activeMinSize;
37358         var c2 = Math.max(this.activeMaxSize - size, 0);
37359         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37360             this.dd.resetConstraints();
37361             this.dd.setXConstraint(
37362                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
37363                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
37364             );
37365             this.dd.setYConstraint(0, 0);
37366         }else{
37367             this.dd.resetConstraints();
37368             this.dd.setXConstraint(0, 0);
37369             this.dd.setYConstraint(
37370                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37371                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37372             );
37373          }
37374         this.dragSpecs.startSize = size;
37375         this.dragSpecs.startPoint = [x, y];
37376         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37377     },
37378     
37379     /** 
37380      * @private Called after the drag operation by the DDProxy
37381      */
37382     onEndProxyDrag : function(e){
37383         Roo.get(this.proxy).setDisplayed(false);
37384         var endPoint = Roo.lib.Event.getXY(e);
37385         if(this.overlay){
37386             this.overlay.hide();
37387         }
37388         var newSize;
37389         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37390             newSize = this.dragSpecs.startSize + 
37391                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37392                     endPoint[0] - this.dragSpecs.startPoint[0] :
37393                     this.dragSpecs.startPoint[0] - endPoint[0]
37394                 );
37395         }else{
37396             newSize = this.dragSpecs.startSize + 
37397                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37398                     endPoint[1] - this.dragSpecs.startPoint[1] :
37399                     this.dragSpecs.startPoint[1] - endPoint[1]
37400                 );
37401         }
37402         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37403         if(newSize != this.dragSpecs.startSize){
37404             if(this.fireEvent('beforeapply', this, newSize) !== false){
37405                 this.adapter.setElementSize(this, newSize);
37406                 this.fireEvent("moved", this, newSize);
37407                 this.fireEvent("resize", this, newSize);
37408             }
37409         }
37410     },
37411     
37412     /**
37413      * Get the adapter this SplitBar uses
37414      * @return The adapter object
37415      */
37416     getAdapter : function(){
37417         return this.adapter;
37418     },
37419     
37420     /**
37421      * Set the adapter this SplitBar uses
37422      * @param {Object} adapter A SplitBar adapter object
37423      */
37424     setAdapter : function(adapter){
37425         this.adapter = adapter;
37426         this.adapter.init(this);
37427     },
37428     
37429     /**
37430      * Gets the minimum size for the resizing element
37431      * @return {Number} The minimum size
37432      */
37433     getMinimumSize : function(){
37434         return this.minSize;
37435     },
37436     
37437     /**
37438      * Sets the minimum size for the resizing element
37439      * @param {Number} minSize The minimum size
37440      */
37441     setMinimumSize : function(minSize){
37442         this.minSize = minSize;
37443     },
37444     
37445     /**
37446      * Gets the maximum size for the resizing element
37447      * @return {Number} The maximum size
37448      */
37449     getMaximumSize : function(){
37450         return this.maxSize;
37451     },
37452     
37453     /**
37454      * Sets the maximum size for the resizing element
37455      * @param {Number} maxSize The maximum size
37456      */
37457     setMaximumSize : function(maxSize){
37458         this.maxSize = maxSize;
37459     },
37460     
37461     /**
37462      * Sets the initialize size for the resizing element
37463      * @param {Number} size The initial size
37464      */
37465     setCurrentSize : function(size){
37466         var oldAnimate = this.animate;
37467         this.animate = false;
37468         this.adapter.setElementSize(this, size);
37469         this.animate = oldAnimate;
37470     },
37471     
37472     /**
37473      * Destroy this splitbar. 
37474      * @param {Boolean} removeEl True to remove the element
37475      */
37476     destroy : function(removeEl){
37477         if(this.shim){
37478             this.shim.remove();
37479         }
37480         this.dd.unreg();
37481         this.proxy.parentNode.removeChild(this.proxy);
37482         if(removeEl){
37483             this.el.remove();
37484         }
37485     }
37486 });
37487
37488 /**
37489  * @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.
37490  */
37491 Roo.bootstrap.SplitBar.createProxy = function(dir){
37492     var proxy = new Roo.Element(document.createElement("div"));
37493     proxy.unselectable();
37494     var cls = 'roo-splitbar-proxy';
37495     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37496     document.body.appendChild(proxy.dom);
37497     return proxy.dom;
37498 };
37499
37500 /** 
37501  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37502  * Default Adapter. It assumes the splitter and resizing element are not positioned
37503  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37504  */
37505 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37506 };
37507
37508 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37509     // do nothing for now
37510     init : function(s){
37511     
37512     },
37513     /**
37514      * Called before drag operations to get the current size of the resizing element. 
37515      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37516      */
37517      getElementSize : function(s){
37518         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37519             return s.resizingEl.getWidth();
37520         }else{
37521             return s.resizingEl.getHeight();
37522         }
37523     },
37524     
37525     /**
37526      * Called after drag operations to set the size of the resizing element.
37527      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37528      * @param {Number} newSize The new size to set
37529      * @param {Function} onComplete A function to be invoked when resizing is complete
37530      */
37531     setElementSize : function(s, newSize, onComplete){
37532         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37533             if(!s.animate){
37534                 s.resizingEl.setWidth(newSize);
37535                 if(onComplete){
37536                     onComplete(s, newSize);
37537                 }
37538             }else{
37539                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37540             }
37541         }else{
37542             
37543             if(!s.animate){
37544                 s.resizingEl.setHeight(newSize);
37545                 if(onComplete){
37546                     onComplete(s, newSize);
37547                 }
37548             }else{
37549                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37550             }
37551         }
37552     }
37553 };
37554
37555 /** 
37556  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37557  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37558  * Adapter that  moves the splitter element to align with the resized sizing element. 
37559  * Used with an absolute positioned SplitBar.
37560  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37561  * document.body, make sure you assign an id to the body element.
37562  */
37563 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37564     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37565     this.container = Roo.get(container);
37566 };
37567
37568 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37569     init : function(s){
37570         this.basic.init(s);
37571     },
37572     
37573     getElementSize : function(s){
37574         return this.basic.getElementSize(s);
37575     },
37576     
37577     setElementSize : function(s, newSize, onComplete){
37578         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37579     },
37580     
37581     moveSplitter : function(s){
37582         var yes = Roo.bootstrap.SplitBar;
37583         switch(s.placement){
37584             case yes.LEFT:
37585                 s.el.setX(s.resizingEl.getRight());
37586                 break;
37587             case yes.RIGHT:
37588                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37589                 break;
37590             case yes.TOP:
37591                 s.el.setY(s.resizingEl.getBottom());
37592                 break;
37593             case yes.BOTTOM:
37594                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37595                 break;
37596         }
37597     }
37598 };
37599
37600 /**
37601  * Orientation constant - Create a vertical SplitBar
37602  * @static
37603  * @type Number
37604  */
37605 Roo.bootstrap.SplitBar.VERTICAL = 1;
37606
37607 /**
37608  * Orientation constant - Create a horizontal SplitBar
37609  * @static
37610  * @type Number
37611  */
37612 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37613
37614 /**
37615  * Placement constant - The resizing element is to the left of the splitter element
37616  * @static
37617  * @type Number
37618  */
37619 Roo.bootstrap.SplitBar.LEFT = 1;
37620
37621 /**
37622  * Placement constant - The resizing element is to the right of the splitter element
37623  * @static
37624  * @type Number
37625  */
37626 Roo.bootstrap.SplitBar.RIGHT = 2;
37627
37628 /**
37629  * Placement constant - The resizing element is positioned above the splitter element
37630  * @static
37631  * @type Number
37632  */
37633 Roo.bootstrap.SplitBar.TOP = 3;
37634
37635 /**
37636  * Placement constant - The resizing element is positioned under splitter element
37637  * @static
37638  * @type Number
37639  */
37640 Roo.bootstrap.SplitBar.BOTTOM = 4;
37641 Roo.namespace("Roo.bootstrap.layout");/*
37642  * Based on:
37643  * Ext JS Library 1.1.1
37644  * Copyright(c) 2006-2007, Ext JS, LLC.
37645  *
37646  * Originally Released Under LGPL - original licence link has changed is not relivant.
37647  *
37648  * Fork - LGPL
37649  * <script type="text/javascript">
37650  */
37651
37652 /**
37653  * @class Roo.bootstrap.layout.Manager
37654  * @extends Roo.bootstrap.Component
37655  * Base class for layout managers.
37656  */
37657 Roo.bootstrap.layout.Manager = function(config)
37658 {
37659     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37660
37661
37662
37663
37664
37665     /** false to disable window resize monitoring @type Boolean */
37666     this.monitorWindowResize = true;
37667     this.regions = {};
37668     this.addEvents({
37669         /**
37670          * @event layout
37671          * Fires when a layout is performed.
37672          * @param {Roo.LayoutManager} this
37673          */
37674         "layout" : true,
37675         /**
37676          * @event regionresized
37677          * Fires when the user resizes a region.
37678          * @param {Roo.LayoutRegion} region The resized region
37679          * @param {Number} newSize The new size (width for east/west, height for north/south)
37680          */
37681         "regionresized" : true,
37682         /**
37683          * @event regioncollapsed
37684          * Fires when a region is collapsed.
37685          * @param {Roo.LayoutRegion} region The collapsed region
37686          */
37687         "regioncollapsed" : true,
37688         /**
37689          * @event regionexpanded
37690          * Fires when a region is expanded.
37691          * @param {Roo.LayoutRegion} region The expanded region
37692          */
37693         "regionexpanded" : true
37694     });
37695     this.updating = false;
37696
37697     if (config.el) {
37698         this.el = Roo.get(config.el);
37699         this.initEvents();
37700     }
37701
37702 };
37703
37704 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37705
37706
37707     regions : null,
37708
37709     monitorWindowResize : true,
37710
37711
37712     updating : false,
37713
37714
37715     onRender : function(ct, position)
37716     {
37717         if(!this.el){
37718             this.el = Roo.get(ct);
37719             this.initEvents();
37720         }
37721         //this.fireEvent('render',this);
37722     },
37723
37724
37725     initEvents: function()
37726     {
37727
37728
37729         // ie scrollbar fix
37730         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37731             document.body.scroll = "no";
37732         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37733             this.el.position('relative');
37734         }
37735         this.id = this.el.id;
37736         this.el.addClass("roo-layout-container");
37737         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37738         if(this.el.dom != document.body ) {
37739             this.el.on('resize', this.layout,this);
37740             this.el.on('show', this.layout,this);
37741         }
37742
37743     },
37744
37745     /**
37746      * Returns true if this layout is currently being updated
37747      * @return {Boolean}
37748      */
37749     isUpdating : function(){
37750         return this.updating;
37751     },
37752
37753     /**
37754      * Suspend the LayoutManager from doing auto-layouts while
37755      * making multiple add or remove calls
37756      */
37757     beginUpdate : function(){
37758         this.updating = true;
37759     },
37760
37761     /**
37762      * Restore auto-layouts and optionally disable the manager from performing a layout
37763      * @param {Boolean} noLayout true to disable a layout update
37764      */
37765     endUpdate : function(noLayout){
37766         this.updating = false;
37767         if(!noLayout){
37768             this.layout();
37769         }
37770     },
37771
37772     layout: function(){
37773         // abstract...
37774     },
37775
37776     onRegionResized : function(region, newSize){
37777         this.fireEvent("regionresized", region, newSize);
37778         this.layout();
37779     },
37780
37781     onRegionCollapsed : function(region){
37782         this.fireEvent("regioncollapsed", region);
37783     },
37784
37785     onRegionExpanded : function(region){
37786         this.fireEvent("regionexpanded", region);
37787     },
37788
37789     /**
37790      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37791      * performs box-model adjustments.
37792      * @return {Object} The size as an object {width: (the width), height: (the height)}
37793      */
37794     getViewSize : function()
37795     {
37796         var size;
37797         if(this.el.dom != document.body){
37798             size = this.el.getSize();
37799         }else{
37800             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37801         }
37802         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37803         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37804         return size;
37805     },
37806
37807     /**
37808      * Returns the Element this layout is bound to.
37809      * @return {Roo.Element}
37810      */
37811     getEl : function(){
37812         return this.el;
37813     },
37814
37815     /**
37816      * Returns the specified region.
37817      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37818      * @return {Roo.LayoutRegion}
37819      */
37820     getRegion : function(target){
37821         return this.regions[target.toLowerCase()];
37822     },
37823
37824     onWindowResize : function(){
37825         if(this.monitorWindowResize){
37826             this.layout();
37827         }
37828     }
37829 });
37830 /*
37831  * Based on:
37832  * Ext JS Library 1.1.1
37833  * Copyright(c) 2006-2007, Ext JS, LLC.
37834  *
37835  * Originally Released Under LGPL - original licence link has changed is not relivant.
37836  *
37837  * Fork - LGPL
37838  * <script type="text/javascript">
37839  */
37840 /**
37841  * @class Roo.bootstrap.layout.Border
37842  * @extends Roo.bootstrap.layout.Manager
37843  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37844  * please see: examples/bootstrap/nested.html<br><br>
37845  
37846 <b>The container the layout is rendered into can be either the body element or any other element.
37847 If it is not the body element, the container needs to either be an absolute positioned element,
37848 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37849 the container size if it is not the body element.</b>
37850
37851 * @constructor
37852 * Create a new Border
37853 * @param {Object} config Configuration options
37854  */
37855 Roo.bootstrap.layout.Border = function(config){
37856     config = config || {};
37857     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37858     
37859     
37860     
37861     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37862         if(config[region]){
37863             config[region].region = region;
37864             this.addRegion(config[region]);
37865         }
37866     },this);
37867     
37868 };
37869
37870 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37871
37872 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37873     
37874     parent : false, // this might point to a 'nest' or a ???
37875     
37876     /**
37877      * Creates and adds a new region if it doesn't already exist.
37878      * @param {String} target The target region key (north, south, east, west or center).
37879      * @param {Object} config The regions config object
37880      * @return {BorderLayoutRegion} The new region
37881      */
37882     addRegion : function(config)
37883     {
37884         if(!this.regions[config.region]){
37885             var r = this.factory(config);
37886             this.bindRegion(r);
37887         }
37888         return this.regions[config.region];
37889     },
37890
37891     // private (kinda)
37892     bindRegion : function(r){
37893         this.regions[r.config.region] = r;
37894         
37895         r.on("visibilitychange",    this.layout, this);
37896         r.on("paneladded",          this.layout, this);
37897         r.on("panelremoved",        this.layout, this);
37898         r.on("invalidated",         this.layout, this);
37899         r.on("resized",             this.onRegionResized, this);
37900         r.on("collapsed",           this.onRegionCollapsed, this);
37901         r.on("expanded",            this.onRegionExpanded, this);
37902     },
37903
37904     /**
37905      * Performs a layout update.
37906      */
37907     layout : function()
37908     {
37909         if(this.updating) {
37910             return;
37911         }
37912         
37913         // render all the rebions if they have not been done alreayd?
37914         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37915             if(this.regions[region] && !this.regions[region].bodyEl){
37916                 this.regions[region].onRender(this.el)
37917             }
37918         },this);
37919         
37920         var size = this.getViewSize();
37921         var w = size.width;
37922         var h = size.height;
37923         var centerW = w;
37924         var centerH = h;
37925         var centerY = 0;
37926         var centerX = 0;
37927         //var x = 0, y = 0;
37928
37929         var rs = this.regions;
37930         var north = rs["north"];
37931         var south = rs["south"]; 
37932         var west = rs["west"];
37933         var east = rs["east"];
37934         var center = rs["center"];
37935         //if(this.hideOnLayout){ // not supported anymore
37936             //c.el.setStyle("display", "none");
37937         //}
37938         if(north && north.isVisible()){
37939             var b = north.getBox();
37940             var m = north.getMargins();
37941             b.width = w - (m.left+m.right);
37942             b.x = m.left;
37943             b.y = m.top;
37944             centerY = b.height + b.y + m.bottom;
37945             centerH -= centerY;
37946             north.updateBox(this.safeBox(b));
37947         }
37948         if(south && south.isVisible()){
37949             var b = south.getBox();
37950             var m = south.getMargins();
37951             b.width = w - (m.left+m.right);
37952             b.x = m.left;
37953             var totalHeight = (b.height + m.top + m.bottom);
37954             b.y = h - totalHeight + m.top;
37955             centerH -= totalHeight;
37956             south.updateBox(this.safeBox(b));
37957         }
37958         if(west && west.isVisible()){
37959             var b = west.getBox();
37960             var m = west.getMargins();
37961             b.height = centerH - (m.top+m.bottom);
37962             b.x = m.left;
37963             b.y = centerY + m.top;
37964             var totalWidth = (b.width + m.left + m.right);
37965             centerX += totalWidth;
37966             centerW -= totalWidth;
37967             west.updateBox(this.safeBox(b));
37968         }
37969         if(east && east.isVisible()){
37970             var b = east.getBox();
37971             var m = east.getMargins();
37972             b.height = centerH - (m.top+m.bottom);
37973             var totalWidth = (b.width + m.left + m.right);
37974             b.x = w - totalWidth + m.left;
37975             b.y = centerY + m.top;
37976             centerW -= totalWidth;
37977             east.updateBox(this.safeBox(b));
37978         }
37979         if(center){
37980             var m = center.getMargins();
37981             var centerBox = {
37982                 x: centerX + m.left,
37983                 y: centerY + m.top,
37984                 width: centerW - (m.left+m.right),
37985                 height: centerH - (m.top+m.bottom)
37986             };
37987             //if(this.hideOnLayout){
37988                 //center.el.setStyle("display", "block");
37989             //}
37990             center.updateBox(this.safeBox(centerBox));
37991         }
37992         this.el.repaint();
37993         this.fireEvent("layout", this);
37994     },
37995
37996     // private
37997     safeBox : function(box){
37998         box.width = Math.max(0, box.width);
37999         box.height = Math.max(0, box.height);
38000         return box;
38001     },
38002
38003     /**
38004      * Adds a ContentPanel (or subclass) to this layout.
38005      * @param {String} target The target region key (north, south, east, west or center).
38006      * @param {Roo.ContentPanel} panel The panel to add
38007      * @return {Roo.ContentPanel} The added panel
38008      */
38009     add : function(target, panel){
38010          
38011         target = target.toLowerCase();
38012         return this.regions[target].add(panel);
38013     },
38014
38015     /**
38016      * Remove a ContentPanel (or subclass) to this layout.
38017      * @param {String} target The target region key (north, south, east, west or center).
38018      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
38019      * @return {Roo.ContentPanel} The removed panel
38020      */
38021     remove : function(target, panel){
38022         target = target.toLowerCase();
38023         return this.regions[target].remove(panel);
38024     },
38025
38026     /**
38027      * Searches all regions for a panel with the specified id
38028      * @param {String} panelId
38029      * @return {Roo.ContentPanel} The panel or null if it wasn't found
38030      */
38031     findPanel : function(panelId){
38032         var rs = this.regions;
38033         for(var target in rs){
38034             if(typeof rs[target] != "function"){
38035                 var p = rs[target].getPanel(panelId);
38036                 if(p){
38037                     return p;
38038                 }
38039             }
38040         }
38041         return null;
38042     },
38043
38044     /**
38045      * Searches all regions for a panel with the specified id and activates (shows) it.
38046      * @param {String/ContentPanel} panelId The panels id or the panel itself
38047      * @return {Roo.ContentPanel} The shown panel or null
38048      */
38049     showPanel : function(panelId) {
38050       var rs = this.regions;
38051       for(var target in rs){
38052          var r = rs[target];
38053          if(typeof r != "function"){
38054             if(r.hasPanel(panelId)){
38055                return r.showPanel(panelId);
38056             }
38057          }
38058       }
38059       return null;
38060    },
38061
38062    /**
38063      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
38064      * @param {Roo.state.Provider} provider (optional) An alternate state provider
38065      */
38066    /*
38067     restoreState : function(provider){
38068         if(!provider){
38069             provider = Roo.state.Manager;
38070         }
38071         var sm = new Roo.LayoutStateManager();
38072         sm.init(this, provider);
38073     },
38074 */
38075  
38076  
38077     /**
38078      * Adds a xtype elements to the layout.
38079      * <pre><code>
38080
38081 layout.addxtype({
38082        xtype : 'ContentPanel',
38083        region: 'west',
38084        items: [ .... ]
38085    }
38086 );
38087
38088 layout.addxtype({
38089         xtype : 'NestedLayoutPanel',
38090         region: 'west',
38091         layout: {
38092            center: { },
38093            west: { }   
38094         },
38095         items : [ ... list of content panels or nested layout panels.. ]
38096    }
38097 );
38098 </code></pre>
38099      * @param {Object} cfg Xtype definition of item to add.
38100      */
38101     addxtype : function(cfg)
38102     {
38103         // basically accepts a pannel...
38104         // can accept a layout region..!?!?
38105         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
38106         
38107         
38108         // theory?  children can only be panels??
38109         
38110         //if (!cfg.xtype.match(/Panel$/)) {
38111         //    return false;
38112         //}
38113         var ret = false;
38114         
38115         if (typeof(cfg.region) == 'undefined') {
38116             Roo.log("Failed to add Panel, region was not set");
38117             Roo.log(cfg);
38118             return false;
38119         }
38120         var region = cfg.region;
38121         delete cfg.region;
38122         
38123           
38124         var xitems = [];
38125         if (cfg.items) {
38126             xitems = cfg.items;
38127             delete cfg.items;
38128         }
38129         var nb = false;
38130         
38131         if ( region == 'center') {
38132             Roo.log("Center: " + cfg.title);
38133         }
38134         
38135         
38136         switch(cfg.xtype) 
38137         {
38138             case 'Content':  // ContentPanel (el, cfg)
38139             case 'Scroll':  // ContentPanel (el, cfg)
38140             case 'View': 
38141                 cfg.autoCreate = cfg.autoCreate || true;
38142                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38143                 //} else {
38144                 //    var el = this.el.createChild();
38145                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
38146                 //}
38147                 
38148                 this.add(region, ret);
38149                 break;
38150             
38151             /*
38152             case 'TreePanel': // our new panel!
38153                 cfg.el = this.el.createChild();
38154                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38155                 this.add(region, ret);
38156                 break;
38157             */
38158             
38159             case 'Nest': 
38160                 // create a new Layout (which is  a Border Layout...
38161                 
38162                 var clayout = cfg.layout;
38163                 clayout.el  = this.el.createChild();
38164                 clayout.items   = clayout.items  || [];
38165                 
38166                 delete cfg.layout;
38167                 
38168                 // replace this exitems with the clayout ones..
38169                 xitems = clayout.items;
38170                  
38171                 // force background off if it's in center...
38172                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
38173                     cfg.background = false;
38174                 }
38175                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
38176                 
38177                 
38178                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38179                 //console.log('adding nested layout panel '  + cfg.toSource());
38180                 this.add(region, ret);
38181                 nb = {}; /// find first...
38182                 break;
38183             
38184             case 'Grid':
38185                 
38186                 // needs grid and region
38187                 
38188                 //var el = this.getRegion(region).el.createChild();
38189                 /*
38190                  *var el = this.el.createChild();
38191                 // create the grid first...
38192                 cfg.grid.container = el;
38193                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
38194                 */
38195                 
38196                 if (region == 'center' && this.active ) {
38197                     cfg.background = false;
38198                 }
38199                 
38200                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
38201                 
38202                 this.add(region, ret);
38203                 /*
38204                 if (cfg.background) {
38205                     // render grid on panel activation (if panel background)
38206                     ret.on('activate', function(gp) {
38207                         if (!gp.grid.rendered) {
38208                     //        gp.grid.render(el);
38209                         }
38210                     });
38211                 } else {
38212                   //  cfg.grid.render(el);
38213                 }
38214                 */
38215                 break;
38216            
38217            
38218             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
38219                 // it was the old xcomponent building that caused this before.
38220                 // espeically if border is the top element in the tree.
38221                 ret = this;
38222                 break; 
38223                 
38224                     
38225                 
38226                 
38227                 
38228             default:
38229                 /*
38230                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
38231                     
38232                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
38233                     this.add(region, ret);
38234                 } else {
38235                 */
38236                     Roo.log(cfg);
38237                     throw "Can not add '" + cfg.xtype + "' to Border";
38238                     return null;
38239              
38240                                 
38241              
38242         }
38243         this.beginUpdate();
38244         // add children..
38245         var region = '';
38246         var abn = {};
38247         Roo.each(xitems, function(i)  {
38248             region = nb && i.region ? i.region : false;
38249             
38250             var add = ret.addxtype(i);
38251            
38252             if (region) {
38253                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
38254                 if (!i.background) {
38255                     abn[region] = nb[region] ;
38256                 }
38257             }
38258             
38259         });
38260         this.endUpdate();
38261
38262         // make the last non-background panel active..
38263         //if (nb) { Roo.log(abn); }
38264         if (nb) {
38265             
38266             for(var r in abn) {
38267                 region = this.getRegion(r);
38268                 if (region) {
38269                     // tried using nb[r], but it does not work..
38270                      
38271                     region.showPanel(abn[r]);
38272                    
38273                 }
38274             }
38275         }
38276         return ret;
38277         
38278     },
38279     
38280     
38281 // private
38282     factory : function(cfg)
38283     {
38284         
38285         var validRegions = Roo.bootstrap.layout.Border.regions;
38286
38287         var target = cfg.region;
38288         cfg.mgr = this;
38289         
38290         var r = Roo.bootstrap.layout;
38291         Roo.log(target);
38292         switch(target){
38293             case "north":
38294                 return new r.North(cfg);
38295             case "south":
38296                 return new r.South(cfg);
38297             case "east":
38298                 return new r.East(cfg);
38299             case "west":
38300                 return new r.West(cfg);
38301             case "center":
38302                 return new r.Center(cfg);
38303         }
38304         throw 'Layout region "'+target+'" not supported.';
38305     }
38306     
38307     
38308 });
38309  /*
38310  * Based on:
38311  * Ext JS Library 1.1.1
38312  * Copyright(c) 2006-2007, Ext JS, LLC.
38313  *
38314  * Originally Released Under LGPL - original licence link has changed is not relivant.
38315  *
38316  * Fork - LGPL
38317  * <script type="text/javascript">
38318  */
38319  
38320 /**
38321  * @class Roo.bootstrap.layout.Basic
38322  * @extends Roo.util.Observable
38323  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
38324  * and does not have a titlebar, tabs or any other features. All it does is size and position 
38325  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
38326  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38327  * @cfg {string}   region  the region that it inhabits..
38328  * @cfg {bool}   skipConfig skip config?
38329  * 
38330
38331  */
38332 Roo.bootstrap.layout.Basic = function(config){
38333     
38334     this.mgr = config.mgr;
38335     
38336     this.position = config.region;
38337     
38338     var skipConfig = config.skipConfig;
38339     
38340     this.events = {
38341         /**
38342          * @scope Roo.BasicLayoutRegion
38343          */
38344         
38345         /**
38346          * @event beforeremove
38347          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
38348          * @param {Roo.LayoutRegion} this
38349          * @param {Roo.ContentPanel} panel The panel
38350          * @param {Object} e The cancel event object
38351          */
38352         "beforeremove" : true,
38353         /**
38354          * @event invalidated
38355          * Fires when the layout for this region is changed.
38356          * @param {Roo.LayoutRegion} this
38357          */
38358         "invalidated" : true,
38359         /**
38360          * @event visibilitychange
38361          * Fires when this region is shown or hidden 
38362          * @param {Roo.LayoutRegion} this
38363          * @param {Boolean} visibility true or false
38364          */
38365         "visibilitychange" : true,
38366         /**
38367          * @event paneladded
38368          * Fires when a panel is added. 
38369          * @param {Roo.LayoutRegion} this
38370          * @param {Roo.ContentPanel} panel The panel
38371          */
38372         "paneladded" : true,
38373         /**
38374          * @event panelremoved
38375          * Fires when a panel is removed. 
38376          * @param {Roo.LayoutRegion} this
38377          * @param {Roo.ContentPanel} panel The panel
38378          */
38379         "panelremoved" : true,
38380         /**
38381          * @event beforecollapse
38382          * Fires when this region before collapse.
38383          * @param {Roo.LayoutRegion} this
38384          */
38385         "beforecollapse" : true,
38386         /**
38387          * @event collapsed
38388          * Fires when this region is collapsed.
38389          * @param {Roo.LayoutRegion} this
38390          */
38391         "collapsed" : true,
38392         /**
38393          * @event expanded
38394          * Fires when this region is expanded.
38395          * @param {Roo.LayoutRegion} this
38396          */
38397         "expanded" : true,
38398         /**
38399          * @event slideshow
38400          * Fires when this region is slid into view.
38401          * @param {Roo.LayoutRegion} this
38402          */
38403         "slideshow" : true,
38404         /**
38405          * @event slidehide
38406          * Fires when this region slides out of view. 
38407          * @param {Roo.LayoutRegion} this
38408          */
38409         "slidehide" : true,
38410         /**
38411          * @event panelactivated
38412          * Fires when a panel is activated. 
38413          * @param {Roo.LayoutRegion} this
38414          * @param {Roo.ContentPanel} panel The activated panel
38415          */
38416         "panelactivated" : true,
38417         /**
38418          * @event resized
38419          * Fires when the user resizes this region. 
38420          * @param {Roo.LayoutRegion} this
38421          * @param {Number} newSize The new size (width for east/west, height for north/south)
38422          */
38423         "resized" : true
38424     };
38425     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38426     this.panels = new Roo.util.MixedCollection();
38427     this.panels.getKey = this.getPanelId.createDelegate(this);
38428     this.box = null;
38429     this.activePanel = null;
38430     // ensure listeners are added...
38431     
38432     if (config.listeners || config.events) {
38433         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38434             listeners : config.listeners || {},
38435             events : config.events || {}
38436         });
38437     }
38438     
38439     if(skipConfig !== true){
38440         this.applyConfig(config);
38441     }
38442 };
38443
38444 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38445 {
38446     getPanelId : function(p){
38447         return p.getId();
38448     },
38449     
38450     applyConfig : function(config){
38451         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38452         this.config = config;
38453         
38454     },
38455     
38456     /**
38457      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38458      * the width, for horizontal (north, south) the height.
38459      * @param {Number} newSize The new width or height
38460      */
38461     resizeTo : function(newSize){
38462         var el = this.el ? this.el :
38463                  (this.activePanel ? this.activePanel.getEl() : null);
38464         if(el){
38465             switch(this.position){
38466                 case "east":
38467                 case "west":
38468                     el.setWidth(newSize);
38469                     this.fireEvent("resized", this, newSize);
38470                 break;
38471                 case "north":
38472                 case "south":
38473                     el.setHeight(newSize);
38474                     this.fireEvent("resized", this, newSize);
38475                 break;                
38476             }
38477         }
38478     },
38479     
38480     getBox : function(){
38481         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38482     },
38483     
38484     getMargins : function(){
38485         return this.margins;
38486     },
38487     
38488     updateBox : function(box){
38489         this.box = box;
38490         var el = this.activePanel.getEl();
38491         el.dom.style.left = box.x + "px";
38492         el.dom.style.top = box.y + "px";
38493         this.activePanel.setSize(box.width, box.height);
38494     },
38495     
38496     /**
38497      * Returns the container element for this region.
38498      * @return {Roo.Element}
38499      */
38500     getEl : function(){
38501         return this.activePanel;
38502     },
38503     
38504     /**
38505      * Returns true if this region is currently visible.
38506      * @return {Boolean}
38507      */
38508     isVisible : function(){
38509         return this.activePanel ? true : false;
38510     },
38511     
38512     setActivePanel : function(panel){
38513         panel = this.getPanel(panel);
38514         if(this.activePanel && this.activePanel != panel){
38515             this.activePanel.setActiveState(false);
38516             this.activePanel.getEl().setLeftTop(-10000,-10000);
38517         }
38518         this.activePanel = panel;
38519         panel.setActiveState(true);
38520         if(this.box){
38521             panel.setSize(this.box.width, this.box.height);
38522         }
38523         this.fireEvent("panelactivated", this, panel);
38524         this.fireEvent("invalidated");
38525     },
38526     
38527     /**
38528      * Show the specified panel.
38529      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38530      * @return {Roo.ContentPanel} The shown panel or null
38531      */
38532     showPanel : function(panel){
38533         panel = this.getPanel(panel);
38534         if(panel){
38535             this.setActivePanel(panel);
38536         }
38537         return panel;
38538     },
38539     
38540     /**
38541      * Get the active panel for this region.
38542      * @return {Roo.ContentPanel} The active panel or null
38543      */
38544     getActivePanel : function(){
38545         return this.activePanel;
38546     },
38547     
38548     /**
38549      * Add the passed ContentPanel(s)
38550      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38551      * @return {Roo.ContentPanel} The panel added (if only one was added)
38552      */
38553     add : function(panel){
38554         if(arguments.length > 1){
38555             for(var i = 0, len = arguments.length; i < len; i++) {
38556                 this.add(arguments[i]);
38557             }
38558             return null;
38559         }
38560         if(this.hasPanel(panel)){
38561             this.showPanel(panel);
38562             return panel;
38563         }
38564         var el = panel.getEl();
38565         if(el.dom.parentNode != this.mgr.el.dom){
38566             this.mgr.el.dom.appendChild(el.dom);
38567         }
38568         if(panel.setRegion){
38569             panel.setRegion(this);
38570         }
38571         this.panels.add(panel);
38572         el.setStyle("position", "absolute");
38573         if(!panel.background){
38574             this.setActivePanel(panel);
38575             if(this.config.initialSize && this.panels.getCount()==1){
38576                 this.resizeTo(this.config.initialSize);
38577             }
38578         }
38579         this.fireEvent("paneladded", this, panel);
38580         return panel;
38581     },
38582     
38583     /**
38584      * Returns true if the panel is in this region.
38585      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38586      * @return {Boolean}
38587      */
38588     hasPanel : function(panel){
38589         if(typeof panel == "object"){ // must be panel obj
38590             panel = panel.getId();
38591         }
38592         return this.getPanel(panel) ? true : false;
38593     },
38594     
38595     /**
38596      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38597      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38598      * @param {Boolean} preservePanel Overrides the config preservePanel option
38599      * @return {Roo.ContentPanel} The panel that was removed
38600      */
38601     remove : function(panel, preservePanel){
38602         panel = this.getPanel(panel);
38603         if(!panel){
38604             return null;
38605         }
38606         var e = {};
38607         this.fireEvent("beforeremove", this, panel, e);
38608         if(e.cancel === true){
38609             return null;
38610         }
38611         var panelId = panel.getId();
38612         this.panels.removeKey(panelId);
38613         return panel;
38614     },
38615     
38616     /**
38617      * Returns the panel specified or null if it's not in this region.
38618      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38619      * @return {Roo.ContentPanel}
38620      */
38621     getPanel : function(id){
38622         if(typeof id == "object"){ // must be panel obj
38623             return id;
38624         }
38625         return this.panels.get(id);
38626     },
38627     
38628     /**
38629      * Returns this regions position (north/south/east/west/center).
38630      * @return {String} 
38631      */
38632     getPosition: function(){
38633         return this.position;    
38634     }
38635 });/*
38636  * Based on:
38637  * Ext JS Library 1.1.1
38638  * Copyright(c) 2006-2007, Ext JS, LLC.
38639  *
38640  * Originally Released Under LGPL - original licence link has changed is not relivant.
38641  *
38642  * Fork - LGPL
38643  * <script type="text/javascript">
38644  */
38645  
38646 /**
38647  * @class Roo.bootstrap.layout.Region
38648  * @extends Roo.bootstrap.layout.Basic
38649  * This class represents a region in a layout manager.
38650  
38651  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38652  * @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})
38653  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38654  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38655  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38656  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38657  * @cfg {String}    title           The title for the region (overrides panel titles)
38658  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38659  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38660  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38661  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38662  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38663  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38664  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38665  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38666  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38667  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38668
38669  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38670  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38671  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38672  * @cfg {Number}    width           For East/West panels
38673  * @cfg {Number}    height          For North/South panels
38674  * @cfg {Boolean}   split           To show the splitter
38675  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38676  * 
38677  * @cfg {string}   cls             Extra CSS classes to add to region
38678  * 
38679  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38680  * @cfg {string}   region  the region that it inhabits..
38681  *
38682
38683  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38684  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38685
38686  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38687  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38688  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38689  */
38690 Roo.bootstrap.layout.Region = function(config)
38691 {
38692     this.applyConfig(config);
38693
38694     var mgr = config.mgr;
38695     var pos = config.region;
38696     config.skipConfig = true;
38697     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38698     
38699     if (mgr.el) {
38700         this.onRender(mgr.el);   
38701     }
38702      
38703     this.visible = true;
38704     this.collapsed = false;
38705     this.unrendered_panels = [];
38706 };
38707
38708 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38709
38710     position: '', // set by wrapper (eg. north/south etc..)
38711     unrendered_panels : null,  // unrendered panels.
38712     
38713     tabPosition : false,
38714     
38715     mgr: false, // points to 'Border'
38716     
38717     
38718     createBody : function(){
38719         /** This region's body element 
38720         * @type Roo.Element */
38721         this.bodyEl = this.el.createChild({
38722                 tag: "div",
38723                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38724         });
38725     },
38726
38727     onRender: function(ctr, pos)
38728     {
38729         var dh = Roo.DomHelper;
38730         /** This region's container element 
38731         * @type Roo.Element */
38732         this.el = dh.append(ctr.dom, {
38733                 tag: "div",
38734                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38735             }, true);
38736         /** This region's title element 
38737         * @type Roo.Element */
38738     
38739         this.titleEl = dh.append(this.el.dom,  {
38740                 tag: "div",
38741                 unselectable: "on",
38742                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38743                 children:[
38744                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38745                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38746                 ]
38747             }, true);
38748         
38749         this.titleEl.enableDisplayMode();
38750         /** This region's title text element 
38751         * @type HTMLElement */
38752         this.titleTextEl = this.titleEl.dom.firstChild;
38753         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38754         /*
38755         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38756         this.closeBtn.enableDisplayMode();
38757         this.closeBtn.on("click", this.closeClicked, this);
38758         this.closeBtn.hide();
38759     */
38760         this.createBody(this.config);
38761         if(this.config.hideWhenEmpty){
38762             this.hide();
38763             this.on("paneladded", this.validateVisibility, this);
38764             this.on("panelremoved", this.validateVisibility, this);
38765         }
38766         if(this.autoScroll){
38767             this.bodyEl.setStyle("overflow", "auto");
38768         }else{
38769             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38770         }
38771         //if(c.titlebar !== false){
38772             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38773                 this.titleEl.hide();
38774             }else{
38775                 this.titleEl.show();
38776                 if(this.config.title){
38777                     this.titleTextEl.innerHTML = this.config.title;
38778                 }
38779             }
38780         //}
38781         if(this.config.collapsed){
38782             this.collapse(true);
38783         }
38784         if(this.config.hidden){
38785             this.hide();
38786         }
38787         
38788         if (this.unrendered_panels && this.unrendered_panels.length) {
38789             for (var i =0;i< this.unrendered_panels.length; i++) {
38790                 this.add(this.unrendered_panels[i]);
38791             }
38792             this.unrendered_panels = null;
38793             
38794         }
38795         
38796     },
38797     
38798     applyConfig : function(c)
38799     {
38800         /*
38801          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38802             var dh = Roo.DomHelper;
38803             if(c.titlebar !== false){
38804                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38805                 this.collapseBtn.on("click", this.collapse, this);
38806                 this.collapseBtn.enableDisplayMode();
38807                 /*
38808                 if(c.showPin === true || this.showPin){
38809                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38810                     this.stickBtn.enableDisplayMode();
38811                     this.stickBtn.on("click", this.expand, this);
38812                     this.stickBtn.hide();
38813                 }
38814                 
38815             }
38816             */
38817             /** This region's collapsed element
38818             * @type Roo.Element */
38819             /*
38820              *
38821             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38822                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38823             ]}, true);
38824             
38825             if(c.floatable !== false){
38826                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38827                this.collapsedEl.on("click", this.collapseClick, this);
38828             }
38829
38830             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38831                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38832                    id: "message", unselectable: "on", style:{"float":"left"}});
38833                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38834              }
38835             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38836             this.expandBtn.on("click", this.expand, this);
38837             
38838         }
38839         
38840         if(this.collapseBtn){
38841             this.collapseBtn.setVisible(c.collapsible == true);
38842         }
38843         
38844         this.cmargins = c.cmargins || this.cmargins ||
38845                          (this.position == "west" || this.position == "east" ?
38846                              {top: 0, left: 2, right:2, bottom: 0} :
38847                              {top: 2, left: 0, right:0, bottom: 2});
38848         */
38849         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38850         
38851         
38852         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38853         
38854         this.autoScroll = c.autoScroll || false;
38855         
38856         
38857        
38858         
38859         this.duration = c.duration || .30;
38860         this.slideDuration = c.slideDuration || .45;
38861         this.config = c;
38862        
38863     },
38864     /**
38865      * Returns true if this region is currently visible.
38866      * @return {Boolean}
38867      */
38868     isVisible : function(){
38869         return this.visible;
38870     },
38871
38872     /**
38873      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38874      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38875      */
38876     //setCollapsedTitle : function(title){
38877     //    title = title || "&#160;";
38878      //   if(this.collapsedTitleTextEl){
38879       //      this.collapsedTitleTextEl.innerHTML = title;
38880        // }
38881     //},
38882
38883     getBox : function(){
38884         var b;
38885       //  if(!this.collapsed){
38886             b = this.el.getBox(false, true);
38887        // }else{
38888           //  b = this.collapsedEl.getBox(false, true);
38889         //}
38890         return b;
38891     },
38892
38893     getMargins : function(){
38894         return this.margins;
38895         //return this.collapsed ? this.cmargins : this.margins;
38896     },
38897 /*
38898     highlight : function(){
38899         this.el.addClass("x-layout-panel-dragover");
38900     },
38901
38902     unhighlight : function(){
38903         this.el.removeClass("x-layout-panel-dragover");
38904     },
38905 */
38906     updateBox : function(box)
38907     {
38908         if (!this.bodyEl) {
38909             return; // not rendered yet..
38910         }
38911         
38912         this.box = box;
38913         if(!this.collapsed){
38914             this.el.dom.style.left = box.x + "px";
38915             this.el.dom.style.top = box.y + "px";
38916             this.updateBody(box.width, box.height);
38917         }else{
38918             this.collapsedEl.dom.style.left = box.x + "px";
38919             this.collapsedEl.dom.style.top = box.y + "px";
38920             this.collapsedEl.setSize(box.width, box.height);
38921         }
38922         if(this.tabs){
38923             this.tabs.autoSizeTabs();
38924         }
38925     },
38926
38927     updateBody : function(w, h)
38928     {
38929         if(w !== null){
38930             this.el.setWidth(w);
38931             w -= this.el.getBorderWidth("rl");
38932             if(this.config.adjustments){
38933                 w += this.config.adjustments[0];
38934             }
38935         }
38936         if(h !== null && h > 0){
38937             this.el.setHeight(h);
38938             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38939             h -= this.el.getBorderWidth("tb");
38940             if(this.config.adjustments){
38941                 h += this.config.adjustments[1];
38942             }
38943             this.bodyEl.setHeight(h);
38944             if(this.tabs){
38945                 h = this.tabs.syncHeight(h);
38946             }
38947         }
38948         if(this.panelSize){
38949             w = w !== null ? w : this.panelSize.width;
38950             h = h !== null ? h : this.panelSize.height;
38951         }
38952         if(this.activePanel){
38953             var el = this.activePanel.getEl();
38954             w = w !== null ? w : el.getWidth();
38955             h = h !== null ? h : el.getHeight();
38956             this.panelSize = {width: w, height: h};
38957             this.activePanel.setSize(w, h);
38958         }
38959         if(Roo.isIE && this.tabs){
38960             this.tabs.el.repaint();
38961         }
38962     },
38963
38964     /**
38965      * Returns the container element for this region.
38966      * @return {Roo.Element}
38967      */
38968     getEl : function(){
38969         return this.el;
38970     },
38971
38972     /**
38973      * Hides this region.
38974      */
38975     hide : function(){
38976         //if(!this.collapsed){
38977             this.el.dom.style.left = "-2000px";
38978             this.el.hide();
38979         //}else{
38980          //   this.collapsedEl.dom.style.left = "-2000px";
38981          //   this.collapsedEl.hide();
38982        // }
38983         this.visible = false;
38984         this.fireEvent("visibilitychange", this, false);
38985     },
38986
38987     /**
38988      * Shows this region if it was previously hidden.
38989      */
38990     show : function(){
38991         //if(!this.collapsed){
38992             this.el.show();
38993         //}else{
38994         //    this.collapsedEl.show();
38995        // }
38996         this.visible = true;
38997         this.fireEvent("visibilitychange", this, true);
38998     },
38999 /*
39000     closeClicked : function(){
39001         if(this.activePanel){
39002             this.remove(this.activePanel);
39003         }
39004     },
39005
39006     collapseClick : function(e){
39007         if(this.isSlid){
39008            e.stopPropagation();
39009            this.slideIn();
39010         }else{
39011            e.stopPropagation();
39012            this.slideOut();
39013         }
39014     },
39015 */
39016     /**
39017      * Collapses this region.
39018      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
39019      */
39020     /*
39021     collapse : function(skipAnim, skipCheck = false){
39022         if(this.collapsed) {
39023             return;
39024         }
39025         
39026         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
39027             
39028             this.collapsed = true;
39029             if(this.split){
39030                 this.split.el.hide();
39031             }
39032             if(this.config.animate && skipAnim !== true){
39033                 this.fireEvent("invalidated", this);
39034                 this.animateCollapse();
39035             }else{
39036                 this.el.setLocation(-20000,-20000);
39037                 this.el.hide();
39038                 this.collapsedEl.show();
39039                 this.fireEvent("collapsed", this);
39040                 this.fireEvent("invalidated", this);
39041             }
39042         }
39043         
39044     },
39045 */
39046     animateCollapse : function(){
39047         // overridden
39048     },
39049
39050     /**
39051      * Expands this region if it was previously collapsed.
39052      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
39053      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
39054      */
39055     /*
39056     expand : function(e, skipAnim){
39057         if(e) {
39058             e.stopPropagation();
39059         }
39060         if(!this.collapsed || this.el.hasActiveFx()) {
39061             return;
39062         }
39063         if(this.isSlid){
39064             this.afterSlideIn();
39065             skipAnim = true;
39066         }
39067         this.collapsed = false;
39068         if(this.config.animate && skipAnim !== true){
39069             this.animateExpand();
39070         }else{
39071             this.el.show();
39072             if(this.split){
39073                 this.split.el.show();
39074             }
39075             this.collapsedEl.setLocation(-2000,-2000);
39076             this.collapsedEl.hide();
39077             this.fireEvent("invalidated", this);
39078             this.fireEvent("expanded", this);
39079         }
39080     },
39081 */
39082     animateExpand : function(){
39083         // overridden
39084     },
39085
39086     initTabs : function()
39087     {
39088         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
39089         
39090         var ts = new Roo.bootstrap.panel.Tabs({
39091             el: this.bodyEl.dom,
39092             region : this,
39093             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
39094             disableTooltips: this.config.disableTabTips,
39095             toolbar : this.config.toolbar
39096         });
39097         
39098         if(this.config.hideTabs){
39099             ts.stripWrap.setDisplayed(false);
39100         }
39101         this.tabs = ts;
39102         ts.resizeTabs = this.config.resizeTabs === true;
39103         ts.minTabWidth = this.config.minTabWidth || 40;
39104         ts.maxTabWidth = this.config.maxTabWidth || 250;
39105         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
39106         ts.monitorResize = false;
39107         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
39108         ts.bodyEl.addClass('roo-layout-tabs-body');
39109         this.panels.each(this.initPanelAsTab, this);
39110     },
39111
39112     initPanelAsTab : function(panel){
39113         var ti = this.tabs.addTab(
39114             panel.getEl().id,
39115             panel.getTitle(),
39116             null,
39117             this.config.closeOnTab && panel.isClosable(),
39118             panel.tpl
39119         );
39120         if(panel.tabTip !== undefined){
39121             ti.setTooltip(panel.tabTip);
39122         }
39123         ti.on("activate", function(){
39124               this.setActivePanel(panel);
39125         }, this);
39126         
39127         if(this.config.closeOnTab){
39128             ti.on("beforeclose", function(t, e){
39129                 e.cancel = true;
39130                 this.remove(panel);
39131             }, this);
39132         }
39133         
39134         panel.tabItem = ti;
39135         
39136         return ti;
39137     },
39138
39139     updatePanelTitle : function(panel, title)
39140     {
39141         if(this.activePanel == panel){
39142             this.updateTitle(title);
39143         }
39144         if(this.tabs){
39145             var ti = this.tabs.getTab(panel.getEl().id);
39146             ti.setText(title);
39147             if(panel.tabTip !== undefined){
39148                 ti.setTooltip(panel.tabTip);
39149             }
39150         }
39151     },
39152
39153     updateTitle : function(title){
39154         if(this.titleTextEl && !this.config.title){
39155             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
39156         }
39157     },
39158
39159     setActivePanel : function(panel)
39160     {
39161         panel = this.getPanel(panel);
39162         if(this.activePanel && this.activePanel != panel){
39163             if(this.activePanel.setActiveState(false) === false){
39164                 return;
39165             }
39166         }
39167         this.activePanel = panel;
39168         panel.setActiveState(true);
39169         if(this.panelSize){
39170             panel.setSize(this.panelSize.width, this.panelSize.height);
39171         }
39172         if(this.closeBtn){
39173             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
39174         }
39175         this.updateTitle(panel.getTitle());
39176         if(this.tabs){
39177             this.fireEvent("invalidated", this);
39178         }
39179         this.fireEvent("panelactivated", this, panel);
39180     },
39181
39182     /**
39183      * Shows the specified panel.
39184      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
39185      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
39186      */
39187     showPanel : function(panel)
39188     {
39189         panel = this.getPanel(panel);
39190         if(panel){
39191             if(this.tabs){
39192                 var tab = this.tabs.getTab(panel.getEl().id);
39193                 if(tab.isHidden()){
39194                     this.tabs.unhideTab(tab.id);
39195                 }
39196                 tab.activate();
39197             }else{
39198                 this.setActivePanel(panel);
39199             }
39200         }
39201         return panel;
39202     },
39203
39204     /**
39205      * Get the active panel for this region.
39206      * @return {Roo.ContentPanel} The active panel or null
39207      */
39208     getActivePanel : function(){
39209         return this.activePanel;
39210     },
39211
39212     validateVisibility : function(){
39213         if(this.panels.getCount() < 1){
39214             this.updateTitle("&#160;");
39215             this.closeBtn.hide();
39216             this.hide();
39217         }else{
39218             if(!this.isVisible()){
39219                 this.show();
39220             }
39221         }
39222     },
39223
39224     /**
39225      * Adds the passed ContentPanel(s) to this region.
39226      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
39227      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
39228      */
39229     add : function(panel)
39230     {
39231         if(arguments.length > 1){
39232             for(var i = 0, len = arguments.length; i < len; i++) {
39233                 this.add(arguments[i]);
39234             }
39235             return null;
39236         }
39237         
39238         // if we have not been rendered yet, then we can not really do much of this..
39239         if (!this.bodyEl) {
39240             this.unrendered_panels.push(panel);
39241             return panel;
39242         }
39243         
39244         
39245         
39246         
39247         if(this.hasPanel(panel)){
39248             this.showPanel(panel);
39249             return panel;
39250         }
39251         panel.setRegion(this);
39252         this.panels.add(panel);
39253        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
39254             // sinle panel - no tab...?? would it not be better to render it with the tabs,
39255             // and hide them... ???
39256             this.bodyEl.dom.appendChild(panel.getEl().dom);
39257             if(panel.background !== true){
39258                 this.setActivePanel(panel);
39259             }
39260             this.fireEvent("paneladded", this, panel);
39261             return panel;
39262         }
39263         */
39264         if(!this.tabs){
39265             this.initTabs();
39266         }else{
39267             this.initPanelAsTab(panel);
39268         }
39269         
39270         
39271         if(panel.background !== true){
39272             this.tabs.activate(panel.getEl().id);
39273         }
39274         this.fireEvent("paneladded", this, panel);
39275         return panel;
39276     },
39277
39278     /**
39279      * Hides the tab for the specified panel.
39280      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39281      */
39282     hidePanel : function(panel){
39283         if(this.tabs && (panel = this.getPanel(panel))){
39284             this.tabs.hideTab(panel.getEl().id);
39285         }
39286     },
39287
39288     /**
39289      * Unhides the tab for a previously hidden panel.
39290      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39291      */
39292     unhidePanel : function(panel){
39293         if(this.tabs && (panel = this.getPanel(panel))){
39294             this.tabs.unhideTab(panel.getEl().id);
39295         }
39296     },
39297
39298     clearPanels : function(){
39299         while(this.panels.getCount() > 0){
39300              this.remove(this.panels.first());
39301         }
39302     },
39303
39304     /**
39305      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
39306      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
39307      * @param {Boolean} preservePanel Overrides the config preservePanel option
39308      * @return {Roo.ContentPanel} The panel that was removed
39309      */
39310     remove : function(panel, preservePanel)
39311     {
39312         panel = this.getPanel(panel);
39313         if(!panel){
39314             return null;
39315         }
39316         var e = {};
39317         this.fireEvent("beforeremove", this, panel, e);
39318         if(e.cancel === true){
39319             return null;
39320         }
39321         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
39322         var panelId = panel.getId();
39323         this.panels.removeKey(panelId);
39324         if(preservePanel){
39325             document.body.appendChild(panel.getEl().dom);
39326         }
39327         if(this.tabs){
39328             this.tabs.removeTab(panel.getEl().id);
39329         }else if (!preservePanel){
39330             this.bodyEl.dom.removeChild(panel.getEl().dom);
39331         }
39332         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
39333             var p = this.panels.first();
39334             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
39335             tempEl.appendChild(p.getEl().dom);
39336             this.bodyEl.update("");
39337             this.bodyEl.dom.appendChild(p.getEl().dom);
39338             tempEl = null;
39339             this.updateTitle(p.getTitle());
39340             this.tabs = null;
39341             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
39342             this.setActivePanel(p);
39343         }
39344         panel.setRegion(null);
39345         if(this.activePanel == panel){
39346             this.activePanel = null;
39347         }
39348         if(this.config.autoDestroy !== false && preservePanel !== true){
39349             try{panel.destroy();}catch(e){}
39350         }
39351         this.fireEvent("panelremoved", this, panel);
39352         return panel;
39353     },
39354
39355     /**
39356      * Returns the TabPanel component used by this region
39357      * @return {Roo.TabPanel}
39358      */
39359     getTabs : function(){
39360         return this.tabs;
39361     },
39362
39363     createTool : function(parentEl, className){
39364         var btn = Roo.DomHelper.append(parentEl, {
39365             tag: "div",
39366             cls: "x-layout-tools-button",
39367             children: [ {
39368                 tag: "div",
39369                 cls: "roo-layout-tools-button-inner " + className,
39370                 html: "&#160;"
39371             }]
39372         }, true);
39373         btn.addClassOnOver("roo-layout-tools-button-over");
39374         return btn;
39375     }
39376 });/*
39377  * Based on:
39378  * Ext JS Library 1.1.1
39379  * Copyright(c) 2006-2007, Ext JS, LLC.
39380  *
39381  * Originally Released Under LGPL - original licence link has changed is not relivant.
39382  *
39383  * Fork - LGPL
39384  * <script type="text/javascript">
39385  */
39386  
39387
39388
39389 /**
39390  * @class Roo.SplitLayoutRegion
39391  * @extends Roo.LayoutRegion
39392  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39393  */
39394 Roo.bootstrap.layout.Split = function(config){
39395     this.cursor = config.cursor;
39396     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39397 };
39398
39399 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39400 {
39401     splitTip : "Drag to resize.",
39402     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39403     useSplitTips : false,
39404
39405     applyConfig : function(config){
39406         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39407     },
39408     
39409     onRender : function(ctr,pos) {
39410         
39411         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39412         if(!this.config.split){
39413             return;
39414         }
39415         if(!this.split){
39416             
39417             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39418                             tag: "div",
39419                             id: this.el.id + "-split",
39420                             cls: "roo-layout-split roo-layout-split-"+this.position,
39421                             html: "&#160;"
39422             });
39423             /** The SplitBar for this region 
39424             * @type Roo.SplitBar */
39425             // does not exist yet...
39426             Roo.log([this.position, this.orientation]);
39427             
39428             this.split = new Roo.bootstrap.SplitBar({
39429                 dragElement : splitEl,
39430                 resizingElement: this.el,
39431                 orientation : this.orientation
39432             });
39433             
39434             this.split.on("moved", this.onSplitMove, this);
39435             this.split.useShim = this.config.useShim === true;
39436             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39437             if(this.useSplitTips){
39438                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39439             }
39440             //if(config.collapsible){
39441             //    this.split.el.on("dblclick", this.collapse,  this);
39442             //}
39443         }
39444         if(typeof this.config.minSize != "undefined"){
39445             this.split.minSize = this.config.minSize;
39446         }
39447         if(typeof this.config.maxSize != "undefined"){
39448             this.split.maxSize = this.config.maxSize;
39449         }
39450         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39451             this.hideSplitter();
39452         }
39453         
39454     },
39455
39456     getHMaxSize : function(){
39457          var cmax = this.config.maxSize || 10000;
39458          var center = this.mgr.getRegion("center");
39459          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39460     },
39461
39462     getVMaxSize : function(){
39463          var cmax = this.config.maxSize || 10000;
39464          var center = this.mgr.getRegion("center");
39465          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39466     },
39467
39468     onSplitMove : function(split, newSize){
39469         this.fireEvent("resized", this, newSize);
39470     },
39471     
39472     /** 
39473      * Returns the {@link Roo.SplitBar} for this region.
39474      * @return {Roo.SplitBar}
39475      */
39476     getSplitBar : function(){
39477         return this.split;
39478     },
39479     
39480     hide : function(){
39481         this.hideSplitter();
39482         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39483     },
39484
39485     hideSplitter : function(){
39486         if(this.split){
39487             this.split.el.setLocation(-2000,-2000);
39488             this.split.el.hide();
39489         }
39490     },
39491
39492     show : function(){
39493         if(this.split){
39494             this.split.el.show();
39495         }
39496         Roo.bootstrap.layout.Split.superclass.show.call(this);
39497     },
39498     
39499     beforeSlide: function(){
39500         if(Roo.isGecko){// firefox overflow auto bug workaround
39501             this.bodyEl.clip();
39502             if(this.tabs) {
39503                 this.tabs.bodyEl.clip();
39504             }
39505             if(this.activePanel){
39506                 this.activePanel.getEl().clip();
39507                 
39508                 if(this.activePanel.beforeSlide){
39509                     this.activePanel.beforeSlide();
39510                 }
39511             }
39512         }
39513     },
39514     
39515     afterSlide : function(){
39516         if(Roo.isGecko){// firefox overflow auto bug workaround
39517             this.bodyEl.unclip();
39518             if(this.tabs) {
39519                 this.tabs.bodyEl.unclip();
39520             }
39521             if(this.activePanel){
39522                 this.activePanel.getEl().unclip();
39523                 if(this.activePanel.afterSlide){
39524                     this.activePanel.afterSlide();
39525                 }
39526             }
39527         }
39528     },
39529
39530     initAutoHide : function(){
39531         if(this.autoHide !== false){
39532             if(!this.autoHideHd){
39533                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39534                 this.autoHideHd = {
39535                     "mouseout": function(e){
39536                         if(!e.within(this.el, true)){
39537                             st.delay(500);
39538                         }
39539                     },
39540                     "mouseover" : function(e){
39541                         st.cancel();
39542                     },
39543                     scope : this
39544                 };
39545             }
39546             this.el.on(this.autoHideHd);
39547         }
39548     },
39549
39550     clearAutoHide : function(){
39551         if(this.autoHide !== false){
39552             this.el.un("mouseout", this.autoHideHd.mouseout);
39553             this.el.un("mouseover", this.autoHideHd.mouseover);
39554         }
39555     },
39556
39557     clearMonitor : function(){
39558         Roo.get(document).un("click", this.slideInIf, this);
39559     },
39560
39561     // these names are backwards but not changed for compat
39562     slideOut : function(){
39563         if(this.isSlid || this.el.hasActiveFx()){
39564             return;
39565         }
39566         this.isSlid = true;
39567         if(this.collapseBtn){
39568             this.collapseBtn.hide();
39569         }
39570         this.closeBtnState = this.closeBtn.getStyle('display');
39571         this.closeBtn.hide();
39572         if(this.stickBtn){
39573             this.stickBtn.show();
39574         }
39575         this.el.show();
39576         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39577         this.beforeSlide();
39578         this.el.setStyle("z-index", 10001);
39579         this.el.slideIn(this.getSlideAnchor(), {
39580             callback: function(){
39581                 this.afterSlide();
39582                 this.initAutoHide();
39583                 Roo.get(document).on("click", this.slideInIf, this);
39584                 this.fireEvent("slideshow", this);
39585             },
39586             scope: this,
39587             block: true
39588         });
39589     },
39590
39591     afterSlideIn : function(){
39592         this.clearAutoHide();
39593         this.isSlid = false;
39594         this.clearMonitor();
39595         this.el.setStyle("z-index", "");
39596         if(this.collapseBtn){
39597             this.collapseBtn.show();
39598         }
39599         this.closeBtn.setStyle('display', this.closeBtnState);
39600         if(this.stickBtn){
39601             this.stickBtn.hide();
39602         }
39603         this.fireEvent("slidehide", this);
39604     },
39605
39606     slideIn : function(cb){
39607         if(!this.isSlid || this.el.hasActiveFx()){
39608             Roo.callback(cb);
39609             return;
39610         }
39611         this.isSlid = false;
39612         this.beforeSlide();
39613         this.el.slideOut(this.getSlideAnchor(), {
39614             callback: function(){
39615                 this.el.setLeftTop(-10000, -10000);
39616                 this.afterSlide();
39617                 this.afterSlideIn();
39618                 Roo.callback(cb);
39619             },
39620             scope: this,
39621             block: true
39622         });
39623     },
39624     
39625     slideInIf : function(e){
39626         if(!e.within(this.el)){
39627             this.slideIn();
39628         }
39629     },
39630
39631     animateCollapse : function(){
39632         this.beforeSlide();
39633         this.el.setStyle("z-index", 20000);
39634         var anchor = this.getSlideAnchor();
39635         this.el.slideOut(anchor, {
39636             callback : function(){
39637                 this.el.setStyle("z-index", "");
39638                 this.collapsedEl.slideIn(anchor, {duration:.3});
39639                 this.afterSlide();
39640                 this.el.setLocation(-10000,-10000);
39641                 this.el.hide();
39642                 this.fireEvent("collapsed", this);
39643             },
39644             scope: this,
39645             block: true
39646         });
39647     },
39648
39649     animateExpand : function(){
39650         this.beforeSlide();
39651         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39652         this.el.setStyle("z-index", 20000);
39653         this.collapsedEl.hide({
39654             duration:.1
39655         });
39656         this.el.slideIn(this.getSlideAnchor(), {
39657             callback : function(){
39658                 this.el.setStyle("z-index", "");
39659                 this.afterSlide();
39660                 if(this.split){
39661                     this.split.el.show();
39662                 }
39663                 this.fireEvent("invalidated", this);
39664                 this.fireEvent("expanded", this);
39665             },
39666             scope: this,
39667             block: true
39668         });
39669     },
39670
39671     anchors : {
39672         "west" : "left",
39673         "east" : "right",
39674         "north" : "top",
39675         "south" : "bottom"
39676     },
39677
39678     sanchors : {
39679         "west" : "l",
39680         "east" : "r",
39681         "north" : "t",
39682         "south" : "b"
39683     },
39684
39685     canchors : {
39686         "west" : "tl-tr",
39687         "east" : "tr-tl",
39688         "north" : "tl-bl",
39689         "south" : "bl-tl"
39690     },
39691
39692     getAnchor : function(){
39693         return this.anchors[this.position];
39694     },
39695
39696     getCollapseAnchor : function(){
39697         return this.canchors[this.position];
39698     },
39699
39700     getSlideAnchor : function(){
39701         return this.sanchors[this.position];
39702     },
39703
39704     getAlignAdj : function(){
39705         var cm = this.cmargins;
39706         switch(this.position){
39707             case "west":
39708                 return [0, 0];
39709             break;
39710             case "east":
39711                 return [0, 0];
39712             break;
39713             case "north":
39714                 return [0, 0];
39715             break;
39716             case "south":
39717                 return [0, 0];
39718             break;
39719         }
39720     },
39721
39722     getExpandAdj : function(){
39723         var c = this.collapsedEl, cm = this.cmargins;
39724         switch(this.position){
39725             case "west":
39726                 return [-(cm.right+c.getWidth()+cm.left), 0];
39727             break;
39728             case "east":
39729                 return [cm.right+c.getWidth()+cm.left, 0];
39730             break;
39731             case "north":
39732                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39733             break;
39734             case "south":
39735                 return [0, cm.top+cm.bottom+c.getHeight()];
39736             break;
39737         }
39738     }
39739 });/*
39740  * Based on:
39741  * Ext JS Library 1.1.1
39742  * Copyright(c) 2006-2007, Ext JS, LLC.
39743  *
39744  * Originally Released Under LGPL - original licence link has changed is not relivant.
39745  *
39746  * Fork - LGPL
39747  * <script type="text/javascript">
39748  */
39749 /*
39750  * These classes are private internal classes
39751  */
39752 Roo.bootstrap.layout.Center = function(config){
39753     config.region = "center";
39754     Roo.bootstrap.layout.Region.call(this, config);
39755     this.visible = true;
39756     this.minWidth = config.minWidth || 20;
39757     this.minHeight = config.minHeight || 20;
39758 };
39759
39760 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39761     hide : function(){
39762         // center panel can't be hidden
39763     },
39764     
39765     show : function(){
39766         // center panel can't be hidden
39767     },
39768     
39769     getMinWidth: function(){
39770         return this.minWidth;
39771     },
39772     
39773     getMinHeight: function(){
39774         return this.minHeight;
39775     }
39776 });
39777
39778
39779
39780
39781  
39782
39783
39784
39785
39786
39787
39788 Roo.bootstrap.layout.North = function(config)
39789 {
39790     config.region = 'north';
39791     config.cursor = 'n-resize';
39792     
39793     Roo.bootstrap.layout.Split.call(this, config);
39794     
39795     
39796     if(this.split){
39797         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39798         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39799         this.split.el.addClass("roo-layout-split-v");
39800     }
39801     //var size = config.initialSize || config.height;
39802     //if(this.el && typeof size != "undefined"){
39803     //    this.el.setHeight(size);
39804     //}
39805 };
39806 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39807 {
39808     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39809      
39810      
39811     onRender : function(ctr, pos)
39812     {
39813         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39814         var size = this.config.initialSize || this.config.height;
39815         if(this.el && typeof size != "undefined"){
39816             this.el.setHeight(size);
39817         }
39818     
39819     },
39820     
39821     getBox : function(){
39822         if(this.collapsed){
39823             return this.collapsedEl.getBox();
39824         }
39825         var box = this.el.getBox();
39826         if(this.split){
39827             box.height += this.split.el.getHeight();
39828         }
39829         return box;
39830     },
39831     
39832     updateBox : function(box){
39833         if(this.split && !this.collapsed){
39834             box.height -= this.split.el.getHeight();
39835             this.split.el.setLeft(box.x);
39836             this.split.el.setTop(box.y+box.height);
39837             this.split.el.setWidth(box.width);
39838         }
39839         if(this.collapsed){
39840             this.updateBody(box.width, null);
39841         }
39842         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39843     }
39844 });
39845
39846
39847
39848
39849
39850 Roo.bootstrap.layout.South = function(config){
39851     config.region = 'south';
39852     config.cursor = 's-resize';
39853     Roo.bootstrap.layout.Split.call(this, config);
39854     if(this.split){
39855         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39856         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39857         this.split.el.addClass("roo-layout-split-v");
39858     }
39859     
39860 };
39861
39862 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39863     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39864     
39865     onRender : function(ctr, pos)
39866     {
39867         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39868         var size = this.config.initialSize || this.config.height;
39869         if(this.el && typeof size != "undefined"){
39870             this.el.setHeight(size);
39871         }
39872     
39873     },
39874     
39875     getBox : function(){
39876         if(this.collapsed){
39877             return this.collapsedEl.getBox();
39878         }
39879         var box = this.el.getBox();
39880         if(this.split){
39881             var sh = this.split.el.getHeight();
39882             box.height += sh;
39883             box.y -= sh;
39884         }
39885         return box;
39886     },
39887     
39888     updateBox : function(box){
39889         if(this.split && !this.collapsed){
39890             var sh = this.split.el.getHeight();
39891             box.height -= sh;
39892             box.y += sh;
39893             this.split.el.setLeft(box.x);
39894             this.split.el.setTop(box.y-sh);
39895             this.split.el.setWidth(box.width);
39896         }
39897         if(this.collapsed){
39898             this.updateBody(box.width, null);
39899         }
39900         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39901     }
39902 });
39903
39904 Roo.bootstrap.layout.East = function(config){
39905     config.region = "east";
39906     config.cursor = "e-resize";
39907     Roo.bootstrap.layout.Split.call(this, config);
39908     if(this.split){
39909         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39910         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39911         this.split.el.addClass("roo-layout-split-h");
39912     }
39913     
39914 };
39915 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39916     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39917     
39918     onRender : function(ctr, pos)
39919     {
39920         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39921         var size = this.config.initialSize || this.config.width;
39922         if(this.el && typeof size != "undefined"){
39923             this.el.setWidth(size);
39924         }
39925     
39926     },
39927     
39928     getBox : function(){
39929         if(this.collapsed){
39930             return this.collapsedEl.getBox();
39931         }
39932         var box = this.el.getBox();
39933         if(this.split){
39934             var sw = this.split.el.getWidth();
39935             box.width += sw;
39936             box.x -= sw;
39937         }
39938         return box;
39939     },
39940
39941     updateBox : function(box){
39942         if(this.split && !this.collapsed){
39943             var sw = this.split.el.getWidth();
39944             box.width -= sw;
39945             this.split.el.setLeft(box.x);
39946             this.split.el.setTop(box.y);
39947             this.split.el.setHeight(box.height);
39948             box.x += sw;
39949         }
39950         if(this.collapsed){
39951             this.updateBody(null, box.height);
39952         }
39953         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39954     }
39955 });
39956
39957 Roo.bootstrap.layout.West = function(config){
39958     config.region = "west";
39959     config.cursor = "w-resize";
39960     
39961     Roo.bootstrap.layout.Split.call(this, config);
39962     if(this.split){
39963         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39964         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39965         this.split.el.addClass("roo-layout-split-h");
39966     }
39967     
39968 };
39969 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39970     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39971     
39972     onRender: function(ctr, pos)
39973     {
39974         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39975         var size = this.config.initialSize || this.config.width;
39976         if(typeof size != "undefined"){
39977             this.el.setWidth(size);
39978         }
39979     },
39980     
39981     getBox : function(){
39982         if(this.collapsed){
39983             return this.collapsedEl.getBox();
39984         }
39985         var box = this.el.getBox();
39986         if (box.width == 0) {
39987             box.width = this.config.width; // kludge?
39988         }
39989         if(this.split){
39990             box.width += this.split.el.getWidth();
39991         }
39992         return box;
39993     },
39994     
39995     updateBox : function(box){
39996         if(this.split && !this.collapsed){
39997             var sw = this.split.el.getWidth();
39998             box.width -= sw;
39999             this.split.el.setLeft(box.x+box.width);
40000             this.split.el.setTop(box.y);
40001             this.split.el.setHeight(box.height);
40002         }
40003         if(this.collapsed){
40004             this.updateBody(null, box.height);
40005         }
40006         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
40007     }
40008 });Roo.namespace("Roo.bootstrap.panel");/*
40009  * Based on:
40010  * Ext JS Library 1.1.1
40011  * Copyright(c) 2006-2007, Ext JS, LLC.
40012  *
40013  * Originally Released Under LGPL - original licence link has changed is not relivant.
40014  *
40015  * Fork - LGPL
40016  * <script type="text/javascript">
40017  */
40018 /**
40019  * @class Roo.ContentPanel
40020  * @extends Roo.util.Observable
40021  * A basic ContentPanel element.
40022  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
40023  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
40024  * @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
40025  * @cfg {Boolean}   closable      True if the panel can be closed/removed
40026  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
40027  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
40028  * @cfg {Toolbar}   toolbar       A toolbar for this panel
40029  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
40030  * @cfg {String} title          The title for this panel
40031  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
40032  * @cfg {String} url            Calls {@link #setUrl} with this value
40033  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
40034  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
40035  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
40036  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
40037  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
40038  * @cfg {Boolean} badges render the badges
40039  * @cfg {String} cls  extra classes to use  
40040  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
40041
40042  * @constructor
40043  * Create a new ContentPanel.
40044  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
40045  * @param {String/Object} config A string to set only the title or a config object
40046  * @param {String} content (optional) Set the HTML content for this panel
40047  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
40048  */
40049 Roo.bootstrap.panel.Content = function( config){
40050     
40051     this.tpl = config.tpl || false;
40052     
40053     var el = config.el;
40054     var content = config.content;
40055
40056     if(config.autoCreate){ // xtype is available if this is called from factory
40057         el = Roo.id();
40058     }
40059     this.el = Roo.get(el);
40060     if(!this.el && config && config.autoCreate){
40061         if(typeof config.autoCreate == "object"){
40062             if(!config.autoCreate.id){
40063                 config.autoCreate.id = config.id||el;
40064             }
40065             this.el = Roo.DomHelper.append(document.body,
40066                         config.autoCreate, true);
40067         }else{
40068             var elcfg =  {
40069                 tag: "div",
40070                 cls: (config.cls || '') +
40071                     (config.background ? ' bg-' + config.background : '') +
40072                     " roo-layout-inactive-content",
40073                 id: config.id||el
40074             };
40075             if (config.iframe) {
40076                 elcfg.cn = [
40077                     {
40078                         tag : 'iframe',
40079                         style : 'border: 0px',
40080                         src : 'about:blank'
40081                     }
40082                 ];
40083             }
40084               
40085             if (config.html) {
40086                 elcfg.html = config.html;
40087                 
40088             }
40089                         
40090             this.el = Roo.DomHelper.append(document.body, elcfg , true);
40091             if (config.iframe) {
40092                 this.iframeEl = this.el.select('iframe',true).first();
40093             }
40094             
40095         }
40096     } 
40097     this.closable = false;
40098     this.loaded = false;
40099     this.active = false;
40100    
40101       
40102     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
40103         
40104         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
40105         
40106         this.wrapEl = this.el; //this.el.wrap();
40107         var ti = [];
40108         if (config.toolbar.items) {
40109             ti = config.toolbar.items ;
40110             delete config.toolbar.items ;
40111         }
40112         
40113         var nitems = [];
40114         this.toolbar.render(this.wrapEl, 'before');
40115         for(var i =0;i < ti.length;i++) {
40116           //  Roo.log(['add child', items[i]]);
40117             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40118         }
40119         this.toolbar.items = nitems;
40120         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
40121         delete config.toolbar;
40122         
40123     }
40124     /*
40125     // xtype created footer. - not sure if will work as we normally have to render first..
40126     if (this.footer && !this.footer.el && this.footer.xtype) {
40127         if (!this.wrapEl) {
40128             this.wrapEl = this.el.wrap();
40129         }
40130     
40131         this.footer.container = this.wrapEl.createChild();
40132          
40133         this.footer = Roo.factory(this.footer, Roo);
40134         
40135     }
40136     */
40137     
40138      if(typeof config == "string"){
40139         this.title = config;
40140     }else{
40141         Roo.apply(this, config);
40142     }
40143     
40144     if(this.resizeEl){
40145         this.resizeEl = Roo.get(this.resizeEl, true);
40146     }else{
40147         this.resizeEl = this.el;
40148     }
40149     // handle view.xtype
40150     
40151  
40152     
40153     
40154     this.addEvents({
40155         /**
40156          * @event activate
40157          * Fires when this panel is activated. 
40158          * @param {Roo.ContentPanel} this
40159          */
40160         "activate" : true,
40161         /**
40162          * @event deactivate
40163          * Fires when this panel is activated. 
40164          * @param {Roo.ContentPanel} this
40165          */
40166         "deactivate" : true,
40167
40168         /**
40169          * @event resize
40170          * Fires when this panel is resized if fitToFrame is true.
40171          * @param {Roo.ContentPanel} this
40172          * @param {Number} width The width after any component adjustments
40173          * @param {Number} height The height after any component adjustments
40174          */
40175         "resize" : true,
40176         
40177          /**
40178          * @event render
40179          * Fires when this tab is created
40180          * @param {Roo.ContentPanel} this
40181          */
40182         "render" : true
40183         
40184         
40185         
40186     });
40187     
40188
40189     
40190     
40191     if(this.autoScroll && !this.iframe){
40192         this.resizeEl.setStyle("overflow", "auto");
40193     } else {
40194         // fix randome scrolling
40195         //this.el.on('scroll', function() {
40196         //    Roo.log('fix random scolling');
40197         //    this.scrollTo('top',0); 
40198         //});
40199     }
40200     content = content || this.content;
40201     if(content){
40202         this.setContent(content);
40203     }
40204     if(config && config.url){
40205         this.setUrl(this.url, this.params, this.loadOnce);
40206     }
40207     
40208     
40209     
40210     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
40211     
40212     if (this.view && typeof(this.view.xtype) != 'undefined') {
40213         this.view.el = this.el.appendChild(document.createElement("div"));
40214         this.view = Roo.factory(this.view); 
40215         this.view.render  &&  this.view.render(false, '');  
40216     }
40217     
40218     
40219     this.fireEvent('render', this);
40220 };
40221
40222 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
40223     
40224     cls : '',
40225     background : '',
40226     
40227     tabTip : '',
40228     
40229     iframe : false,
40230     iframeEl : false,
40231     
40232     setRegion : function(region){
40233         this.region = region;
40234         this.setActiveClass(region && !this.background);
40235     },
40236     
40237     
40238     setActiveClass: function(state)
40239     {
40240         if(state){
40241            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
40242            this.el.setStyle('position','relative');
40243         }else{
40244            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
40245            this.el.setStyle('position', 'absolute');
40246         } 
40247     },
40248     
40249     /**
40250      * Returns the toolbar for this Panel if one was configured. 
40251      * @return {Roo.Toolbar} 
40252      */
40253     getToolbar : function(){
40254         return this.toolbar;
40255     },
40256     
40257     setActiveState : function(active)
40258     {
40259         this.active = active;
40260         this.setActiveClass(active);
40261         if(!active){
40262             if(this.fireEvent("deactivate", this) === false){
40263                 return false;
40264             }
40265             return true;
40266         }
40267         this.fireEvent("activate", this);
40268         return true;
40269     },
40270     /**
40271      * Updates this panel's element (not for iframe)
40272      * @param {String} content The new content
40273      * @param {Boolean} loadScripts (optional) true to look for and process scripts
40274     */
40275     setContent : function(content, loadScripts){
40276         if (this.iframe) {
40277             return;
40278         }
40279         
40280         this.el.update(content, loadScripts);
40281     },
40282
40283     ignoreResize : function(w, h){
40284         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
40285             return true;
40286         }else{
40287             this.lastSize = {width: w, height: h};
40288             return false;
40289         }
40290     },
40291     /**
40292      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
40293      * @return {Roo.UpdateManager} The UpdateManager
40294      */
40295     getUpdateManager : function(){
40296         if (this.iframe) {
40297             return false;
40298         }
40299         return this.el.getUpdateManager();
40300     },
40301      /**
40302      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
40303      * Does not work with IFRAME contents
40304      * @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:
40305 <pre><code>
40306 panel.load({
40307     url: "your-url.php",
40308     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
40309     callback: yourFunction,
40310     scope: yourObject, //(optional scope)
40311     discardUrl: false,
40312     nocache: false,
40313     text: "Loading...",
40314     timeout: 30,
40315     scripts: false
40316 });
40317 </code></pre>
40318      
40319      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
40320      * 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.
40321      * @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}
40322      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
40323      * @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.
40324      * @return {Roo.ContentPanel} this
40325      */
40326     load : function(){
40327         
40328         if (this.iframe) {
40329             return this;
40330         }
40331         
40332         var um = this.el.getUpdateManager();
40333         um.update.apply(um, arguments);
40334         return this;
40335     },
40336
40337
40338     /**
40339      * 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.
40340      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
40341      * @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)
40342      * @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)
40343      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
40344      */
40345     setUrl : function(url, params, loadOnce){
40346         if (this.iframe) {
40347             this.iframeEl.dom.src = url;
40348             return false;
40349         }
40350         
40351         if(this.refreshDelegate){
40352             this.removeListener("activate", this.refreshDelegate);
40353         }
40354         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
40355         this.on("activate", this.refreshDelegate);
40356         return this.el.getUpdateManager();
40357     },
40358     
40359     _handleRefresh : function(url, params, loadOnce){
40360         if(!loadOnce || !this.loaded){
40361             var updater = this.el.getUpdateManager();
40362             updater.update(url, params, this._setLoaded.createDelegate(this));
40363         }
40364     },
40365     
40366     _setLoaded : function(){
40367         this.loaded = true;
40368     }, 
40369     
40370     /**
40371      * Returns this panel's id
40372      * @return {String} 
40373      */
40374     getId : function(){
40375         return this.el.id;
40376     },
40377     
40378     /** 
40379      * Returns this panel's element - used by regiosn to add.
40380      * @return {Roo.Element} 
40381      */
40382     getEl : function(){
40383         return this.wrapEl || this.el;
40384     },
40385     
40386    
40387     
40388     adjustForComponents : function(width, height)
40389     {
40390         //Roo.log('adjustForComponents ');
40391         if(this.resizeEl != this.el){
40392             width -= this.el.getFrameWidth('lr');
40393             height -= this.el.getFrameWidth('tb');
40394         }
40395         if(this.toolbar){
40396             var te = this.toolbar.getEl();
40397             te.setWidth(width);
40398             height -= te.getHeight();
40399         }
40400         if(this.footer){
40401             var te = this.footer.getEl();
40402             te.setWidth(width);
40403             height -= te.getHeight();
40404         }
40405         
40406         
40407         if(this.adjustments){
40408             width += this.adjustments[0];
40409             height += this.adjustments[1];
40410         }
40411         return {"width": width, "height": height};
40412     },
40413     
40414     setSize : function(width, height){
40415         if(this.fitToFrame && !this.ignoreResize(width, height)){
40416             if(this.fitContainer && this.resizeEl != this.el){
40417                 this.el.setSize(width, height);
40418             }
40419             var size = this.adjustForComponents(width, height);
40420             if (this.iframe) {
40421                 this.iframeEl.setSize(width,height);
40422             }
40423             
40424             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40425             this.fireEvent('resize', this, size.width, size.height);
40426             
40427             
40428         }
40429     },
40430     
40431     /**
40432      * Returns this panel's title
40433      * @return {String} 
40434      */
40435     getTitle : function(){
40436         
40437         if (typeof(this.title) != 'object') {
40438             return this.title;
40439         }
40440         
40441         var t = '';
40442         for (var k in this.title) {
40443             if (!this.title.hasOwnProperty(k)) {
40444                 continue;
40445             }
40446             
40447             if (k.indexOf('-') >= 0) {
40448                 var s = k.split('-');
40449                 for (var i = 0; i<s.length; i++) {
40450                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40451                 }
40452             } else {
40453                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40454             }
40455         }
40456         return t;
40457     },
40458     
40459     /**
40460      * Set this panel's title
40461      * @param {String} title
40462      */
40463     setTitle : function(title){
40464         this.title = title;
40465         if(this.region){
40466             this.region.updatePanelTitle(this, title);
40467         }
40468     },
40469     
40470     /**
40471      * Returns true is this panel was configured to be closable
40472      * @return {Boolean} 
40473      */
40474     isClosable : function(){
40475         return this.closable;
40476     },
40477     
40478     beforeSlide : function(){
40479         this.el.clip();
40480         this.resizeEl.clip();
40481     },
40482     
40483     afterSlide : function(){
40484         this.el.unclip();
40485         this.resizeEl.unclip();
40486     },
40487     
40488     /**
40489      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40490      *   Will fail silently if the {@link #setUrl} method has not been called.
40491      *   This does not activate the panel, just updates its content.
40492      */
40493     refresh : function(){
40494         if(this.refreshDelegate){
40495            this.loaded = false;
40496            this.refreshDelegate();
40497         }
40498     },
40499     
40500     /**
40501      * Destroys this panel
40502      */
40503     destroy : function(){
40504         this.el.removeAllListeners();
40505         var tempEl = document.createElement("span");
40506         tempEl.appendChild(this.el.dom);
40507         tempEl.innerHTML = "";
40508         this.el.remove();
40509         this.el = null;
40510     },
40511     
40512     /**
40513      * form - if the content panel contains a form - this is a reference to it.
40514      * @type {Roo.form.Form}
40515      */
40516     form : false,
40517     /**
40518      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40519      *    This contains a reference to it.
40520      * @type {Roo.View}
40521      */
40522     view : false,
40523     
40524       /**
40525      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40526      * <pre><code>
40527
40528 layout.addxtype({
40529        xtype : 'Form',
40530        items: [ .... ]
40531    }
40532 );
40533
40534 </code></pre>
40535      * @param {Object} cfg Xtype definition of item to add.
40536      */
40537     
40538     
40539     getChildContainer: function () {
40540         return this.getEl();
40541     }
40542     
40543     
40544     /*
40545         var  ret = new Roo.factory(cfg);
40546         return ret;
40547         
40548         
40549         // add form..
40550         if (cfg.xtype.match(/^Form$/)) {
40551             
40552             var el;
40553             //if (this.footer) {
40554             //    el = this.footer.container.insertSibling(false, 'before');
40555             //} else {
40556                 el = this.el.createChild();
40557             //}
40558
40559             this.form = new  Roo.form.Form(cfg);
40560             
40561             
40562             if ( this.form.allItems.length) {
40563                 this.form.render(el.dom);
40564             }
40565             return this.form;
40566         }
40567         // should only have one of theses..
40568         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40569             // views.. should not be just added - used named prop 'view''
40570             
40571             cfg.el = this.el.appendChild(document.createElement("div"));
40572             // factory?
40573             
40574             var ret = new Roo.factory(cfg);
40575              
40576              ret.render && ret.render(false, ''); // render blank..
40577             this.view = ret;
40578             return ret;
40579         }
40580         return false;
40581     }
40582     \*/
40583 });
40584  
40585 /**
40586  * @class Roo.bootstrap.panel.Grid
40587  * @extends Roo.bootstrap.panel.Content
40588  * @constructor
40589  * Create a new GridPanel.
40590  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40591  * @param {Object} config A the config object
40592   
40593  */
40594
40595
40596
40597 Roo.bootstrap.panel.Grid = function(config)
40598 {
40599     
40600       
40601     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40602         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40603
40604     config.el = this.wrapper;
40605     //this.el = this.wrapper;
40606     
40607       if (config.container) {
40608         // ctor'ed from a Border/panel.grid
40609         
40610         
40611         this.wrapper.setStyle("overflow", "hidden");
40612         this.wrapper.addClass('roo-grid-container');
40613
40614     }
40615     
40616     
40617     if(config.toolbar){
40618         var tool_el = this.wrapper.createChild();    
40619         this.toolbar = Roo.factory(config.toolbar);
40620         var ti = [];
40621         if (config.toolbar.items) {
40622             ti = config.toolbar.items ;
40623             delete config.toolbar.items ;
40624         }
40625         
40626         var nitems = [];
40627         this.toolbar.render(tool_el);
40628         for(var i =0;i < ti.length;i++) {
40629           //  Roo.log(['add child', items[i]]);
40630             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40631         }
40632         this.toolbar.items = nitems;
40633         
40634         delete config.toolbar;
40635     }
40636     
40637     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40638     config.grid.scrollBody = true;;
40639     config.grid.monitorWindowResize = false; // turn off autosizing
40640     config.grid.autoHeight = false;
40641     config.grid.autoWidth = false;
40642     
40643     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40644     
40645     if (config.background) {
40646         // render grid on panel activation (if panel background)
40647         this.on('activate', function(gp) {
40648             if (!gp.grid.rendered) {
40649                 gp.grid.render(this.wrapper);
40650                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40651             }
40652         });
40653             
40654     } else {
40655         this.grid.render(this.wrapper);
40656         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40657
40658     }
40659     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40660     // ??? needed ??? config.el = this.wrapper;
40661     
40662     
40663     
40664   
40665     // xtype created footer. - not sure if will work as we normally have to render first..
40666     if (this.footer && !this.footer.el && this.footer.xtype) {
40667         
40668         var ctr = this.grid.getView().getFooterPanel(true);
40669         this.footer.dataSource = this.grid.dataSource;
40670         this.footer = Roo.factory(this.footer, Roo);
40671         this.footer.render(ctr);
40672         
40673     }
40674     
40675     
40676     
40677     
40678      
40679 };
40680
40681 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40682     getId : function(){
40683         return this.grid.id;
40684     },
40685     
40686     /**
40687      * Returns the grid for this panel
40688      * @return {Roo.bootstrap.Table} 
40689      */
40690     getGrid : function(){
40691         return this.grid;    
40692     },
40693     
40694     setSize : function(width, height){
40695         if(!this.ignoreResize(width, height)){
40696             var grid = this.grid;
40697             var size = this.adjustForComponents(width, height);
40698             // tfoot is not a footer?
40699           
40700             
40701             var gridel = grid.getGridEl();
40702             gridel.setSize(size.width, size.height);
40703             
40704             var tbd = grid.getGridEl().select('tbody', true).first();
40705             var thd = grid.getGridEl().select('thead',true).first();
40706             var tbf= grid.getGridEl().select('tfoot', true).first();
40707
40708             if (tbf) {
40709                 size.height -= tbf.getHeight();
40710             }
40711             if (thd) {
40712                 size.height -= thd.getHeight();
40713             }
40714             
40715             tbd.setSize(size.width, size.height );
40716             // this is for the account management tab -seems to work there.
40717             var thd = grid.getGridEl().select('thead',true).first();
40718             //if (tbd) {
40719             //    tbd.setSize(size.width, size.height - thd.getHeight());
40720             //}
40721              
40722             grid.autoSize();
40723         }
40724     },
40725      
40726     
40727     
40728     beforeSlide : function(){
40729         this.grid.getView().scroller.clip();
40730     },
40731     
40732     afterSlide : function(){
40733         this.grid.getView().scroller.unclip();
40734     },
40735     
40736     destroy : function(){
40737         this.grid.destroy();
40738         delete this.grid;
40739         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40740     }
40741 });
40742
40743 /**
40744  * @class Roo.bootstrap.panel.Nest
40745  * @extends Roo.bootstrap.panel.Content
40746  * @constructor
40747  * Create a new Panel, that can contain a layout.Border.
40748  * 
40749  * 
40750  * @param {Roo.BorderLayout} layout The layout for this panel
40751  * @param {String/Object} config A string to set only the title or a config object
40752  */
40753 Roo.bootstrap.panel.Nest = function(config)
40754 {
40755     // construct with only one argument..
40756     /* FIXME - implement nicer consturctors
40757     if (layout.layout) {
40758         config = layout;
40759         layout = config.layout;
40760         delete config.layout;
40761     }
40762     if (layout.xtype && !layout.getEl) {
40763         // then layout needs constructing..
40764         layout = Roo.factory(layout, Roo);
40765     }
40766     */
40767     
40768     config.el =  config.layout.getEl();
40769     
40770     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40771     
40772     config.layout.monitorWindowResize = false; // turn off autosizing
40773     this.layout = config.layout;
40774     this.layout.getEl().addClass("roo-layout-nested-layout");
40775     this.layout.parent = this;
40776     
40777     
40778     
40779     
40780 };
40781
40782 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40783
40784     setSize : function(width, height){
40785         if(!this.ignoreResize(width, height)){
40786             var size = this.adjustForComponents(width, height);
40787             var el = this.layout.getEl();
40788             if (size.height < 1) {
40789                 el.setWidth(size.width);   
40790             } else {
40791                 el.setSize(size.width, size.height);
40792             }
40793             var touch = el.dom.offsetWidth;
40794             this.layout.layout();
40795             // ie requires a double layout on the first pass
40796             if(Roo.isIE && !this.initialized){
40797                 this.initialized = true;
40798                 this.layout.layout();
40799             }
40800         }
40801     },
40802     
40803     // activate all subpanels if not currently active..
40804     
40805     setActiveState : function(active){
40806         this.active = active;
40807         this.setActiveClass(active);
40808         
40809         if(!active){
40810             this.fireEvent("deactivate", this);
40811             return;
40812         }
40813         
40814         this.fireEvent("activate", this);
40815         // not sure if this should happen before or after..
40816         if (!this.layout) {
40817             return; // should not happen..
40818         }
40819         var reg = false;
40820         for (var r in this.layout.regions) {
40821             reg = this.layout.getRegion(r);
40822             if (reg.getActivePanel()) {
40823                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40824                 reg.setActivePanel(reg.getActivePanel());
40825                 continue;
40826             }
40827             if (!reg.panels.length) {
40828                 continue;
40829             }
40830             reg.showPanel(reg.getPanel(0));
40831         }
40832         
40833         
40834         
40835         
40836     },
40837     
40838     /**
40839      * Returns the nested BorderLayout for this panel
40840      * @return {Roo.BorderLayout} 
40841      */
40842     getLayout : function(){
40843         return this.layout;
40844     },
40845     
40846      /**
40847      * Adds a xtype elements to the layout of the nested panel
40848      * <pre><code>
40849
40850 panel.addxtype({
40851        xtype : 'ContentPanel',
40852        region: 'west',
40853        items: [ .... ]
40854    }
40855 );
40856
40857 panel.addxtype({
40858         xtype : 'NestedLayoutPanel',
40859         region: 'west',
40860         layout: {
40861            center: { },
40862            west: { }   
40863         },
40864         items : [ ... list of content panels or nested layout panels.. ]
40865    }
40866 );
40867 </code></pre>
40868      * @param {Object} cfg Xtype definition of item to add.
40869      */
40870     addxtype : function(cfg) {
40871         return this.layout.addxtype(cfg);
40872     
40873     }
40874 });/*
40875  * Based on:
40876  * Ext JS Library 1.1.1
40877  * Copyright(c) 2006-2007, Ext JS, LLC.
40878  *
40879  * Originally Released Under LGPL - original licence link has changed is not relivant.
40880  *
40881  * Fork - LGPL
40882  * <script type="text/javascript">
40883  */
40884 /**
40885  * @class Roo.TabPanel
40886  * @extends Roo.util.Observable
40887  * A lightweight tab container.
40888  * <br><br>
40889  * Usage:
40890  * <pre><code>
40891 // basic tabs 1, built from existing content
40892 var tabs = new Roo.TabPanel("tabs1");
40893 tabs.addTab("script", "View Script");
40894 tabs.addTab("markup", "View Markup");
40895 tabs.activate("script");
40896
40897 // more advanced tabs, built from javascript
40898 var jtabs = new Roo.TabPanel("jtabs");
40899 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40900
40901 // set up the UpdateManager
40902 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40903 var updater = tab2.getUpdateManager();
40904 updater.setDefaultUrl("ajax1.htm");
40905 tab2.on('activate', updater.refresh, updater, true);
40906
40907 // Use setUrl for Ajax loading
40908 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40909 tab3.setUrl("ajax2.htm", null, true);
40910
40911 // Disabled tab
40912 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40913 tab4.disable();
40914
40915 jtabs.activate("jtabs-1");
40916  * </code></pre>
40917  * @constructor
40918  * Create a new TabPanel.
40919  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40920  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40921  */
40922 Roo.bootstrap.panel.Tabs = function(config){
40923     /**
40924     * The container element for this TabPanel.
40925     * @type Roo.Element
40926     */
40927     this.el = Roo.get(config.el);
40928     delete config.el;
40929     if(config){
40930         if(typeof config == "boolean"){
40931             this.tabPosition = config ? "bottom" : "top";
40932         }else{
40933             Roo.apply(this, config);
40934         }
40935     }
40936     
40937     if(this.tabPosition == "bottom"){
40938         // if tabs are at the bottom = create the body first.
40939         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40940         this.el.addClass("roo-tabs-bottom");
40941     }
40942     // next create the tabs holders
40943     
40944     if (this.tabPosition == "west"){
40945         
40946         var reg = this.region; // fake it..
40947         while (reg) {
40948             if (!reg.mgr.parent) {
40949                 break;
40950             }
40951             reg = reg.mgr.parent.region;
40952         }
40953         Roo.log("got nest?");
40954         Roo.log(reg);
40955         if (reg.mgr.getRegion('west')) {
40956             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40957             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40958             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40959             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40960             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40961         
40962             
40963         }
40964         
40965         
40966     } else {
40967      
40968         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40969         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40970         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40971         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40972     }
40973     
40974     
40975     if(Roo.isIE){
40976         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40977     }
40978     
40979     // finally - if tabs are at the top, then create the body last..
40980     if(this.tabPosition != "bottom"){
40981         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40982          * @type Roo.Element
40983          */
40984         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40985         this.el.addClass("roo-tabs-top");
40986     }
40987     this.items = [];
40988
40989     this.bodyEl.setStyle("position", "relative");
40990
40991     this.active = null;
40992     this.activateDelegate = this.activate.createDelegate(this);
40993
40994     this.addEvents({
40995         /**
40996          * @event tabchange
40997          * Fires when the active tab changes
40998          * @param {Roo.TabPanel} this
40999          * @param {Roo.TabPanelItem} activePanel The new active tab
41000          */
41001         "tabchange": true,
41002         /**
41003          * @event beforetabchange
41004          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
41005          * @param {Roo.TabPanel} this
41006          * @param {Object} e Set cancel to true on this object to cancel the tab change
41007          * @param {Roo.TabPanelItem} tab The tab being changed to
41008          */
41009         "beforetabchange" : true
41010     });
41011
41012     Roo.EventManager.onWindowResize(this.onResize, this);
41013     this.cpad = this.el.getPadding("lr");
41014     this.hiddenCount = 0;
41015
41016
41017     // toolbar on the tabbar support...
41018     if (this.toolbar) {
41019         alert("no toolbar support yet");
41020         this.toolbar  = false;
41021         /*
41022         var tcfg = this.toolbar;
41023         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
41024         this.toolbar = new Roo.Toolbar(tcfg);
41025         if (Roo.isSafari) {
41026             var tbl = tcfg.container.child('table', true);
41027             tbl.setAttribute('width', '100%');
41028         }
41029         */
41030         
41031     }
41032    
41033
41034
41035     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
41036 };
41037
41038 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
41039     /*
41040      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
41041      */
41042     tabPosition : "top",
41043     /*
41044      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
41045      */
41046     currentTabWidth : 0,
41047     /*
41048      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
41049      */
41050     minTabWidth : 40,
41051     /*
41052      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
41053      */
41054     maxTabWidth : 250,
41055     /*
41056      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
41057      */
41058     preferredTabWidth : 175,
41059     /*
41060      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
41061      */
41062     resizeTabs : false,
41063     /*
41064      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
41065      */
41066     monitorResize : true,
41067     /*
41068      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
41069      */
41070     toolbar : false,  // set by caller..
41071     
41072     region : false, /// set by caller
41073     
41074     disableTooltips : true, // not used yet...
41075
41076     /**
41077      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
41078      * @param {String} id The id of the div to use <b>or create</b>
41079      * @param {String} text The text for the tab
41080      * @param {String} content (optional) Content to put in the TabPanelItem body
41081      * @param {Boolean} closable (optional) True to create a close icon on the tab
41082      * @return {Roo.TabPanelItem} The created TabPanelItem
41083      */
41084     addTab : function(id, text, content, closable, tpl)
41085     {
41086         var item = new Roo.bootstrap.panel.TabItem({
41087             panel: this,
41088             id : id,
41089             text : text,
41090             closable : closable,
41091             tpl : tpl
41092         });
41093         this.addTabItem(item);
41094         if(content){
41095             item.setContent(content);
41096         }
41097         return item;
41098     },
41099
41100     /**
41101      * Returns the {@link Roo.TabPanelItem} with the specified id/index
41102      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
41103      * @return {Roo.TabPanelItem}
41104      */
41105     getTab : function(id){
41106         return this.items[id];
41107     },
41108
41109     /**
41110      * Hides the {@link Roo.TabPanelItem} with the specified id/index
41111      * @param {String/Number} id The id or index of the TabPanelItem to hide.
41112      */
41113     hideTab : function(id){
41114         var t = this.items[id];
41115         if(!t.isHidden()){
41116            t.setHidden(true);
41117            this.hiddenCount++;
41118            this.autoSizeTabs();
41119         }
41120     },
41121
41122     /**
41123      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
41124      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
41125      */
41126     unhideTab : function(id){
41127         var t = this.items[id];
41128         if(t.isHidden()){
41129            t.setHidden(false);
41130            this.hiddenCount--;
41131            this.autoSizeTabs();
41132         }
41133     },
41134
41135     /**
41136      * Adds an existing {@link Roo.TabPanelItem}.
41137      * @param {Roo.TabPanelItem} item The TabPanelItem to add
41138      */
41139     addTabItem : function(item)
41140     {
41141         this.items[item.id] = item;
41142         this.items.push(item);
41143         this.autoSizeTabs();
41144       //  if(this.resizeTabs){
41145     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
41146   //         this.autoSizeTabs();
41147 //        }else{
41148 //            item.autoSize();
41149        // }
41150     },
41151
41152     /**
41153      * Removes a {@link Roo.TabPanelItem}.
41154      * @param {String/Number} id The id or index of the TabPanelItem to remove.
41155      */
41156     removeTab : function(id){
41157         var items = this.items;
41158         var tab = items[id];
41159         if(!tab) { return; }
41160         var index = items.indexOf(tab);
41161         if(this.active == tab && items.length > 1){
41162             var newTab = this.getNextAvailable(index);
41163             if(newTab) {
41164                 newTab.activate();
41165             }
41166         }
41167         this.stripEl.dom.removeChild(tab.pnode.dom);
41168         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
41169             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
41170         }
41171         items.splice(index, 1);
41172         delete this.items[tab.id];
41173         tab.fireEvent("close", tab);
41174         tab.purgeListeners();
41175         this.autoSizeTabs();
41176     },
41177
41178     getNextAvailable : function(start){
41179         var items = this.items;
41180         var index = start;
41181         // look for a next tab that will slide over to
41182         // replace the one being removed
41183         while(index < items.length){
41184             var item = items[++index];
41185             if(item && !item.isHidden()){
41186                 return item;
41187             }
41188         }
41189         // if one isn't found select the previous tab (on the left)
41190         index = start;
41191         while(index >= 0){
41192             var item = items[--index];
41193             if(item && !item.isHidden()){
41194                 return item;
41195             }
41196         }
41197         return null;
41198     },
41199
41200     /**
41201      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
41202      * @param {String/Number} id The id or index of the TabPanelItem to disable.
41203      */
41204     disableTab : function(id){
41205         var tab = this.items[id];
41206         if(tab && this.active != tab){
41207             tab.disable();
41208         }
41209     },
41210
41211     /**
41212      * Enables a {@link Roo.TabPanelItem} that is disabled.
41213      * @param {String/Number} id The id or index of the TabPanelItem to enable.
41214      */
41215     enableTab : function(id){
41216         var tab = this.items[id];
41217         tab.enable();
41218     },
41219
41220     /**
41221      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
41222      * @param {String/Number} id The id or index of the TabPanelItem to activate.
41223      * @return {Roo.TabPanelItem} The TabPanelItem.
41224      */
41225     activate : function(id)
41226     {
41227         //Roo.log('activite:'  + id);
41228         
41229         var tab = this.items[id];
41230         if(!tab){
41231             return null;
41232         }
41233         if(tab == this.active || tab.disabled){
41234             return tab;
41235         }
41236         var e = {};
41237         this.fireEvent("beforetabchange", this, e, tab);
41238         if(e.cancel !== true && !tab.disabled){
41239             if(this.active){
41240                 this.active.hide();
41241             }
41242             this.active = this.items[id];
41243             this.active.show();
41244             this.fireEvent("tabchange", this, this.active);
41245         }
41246         return tab;
41247     },
41248
41249     /**
41250      * Gets the active {@link Roo.TabPanelItem}.
41251      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
41252      */
41253     getActiveTab : function(){
41254         return this.active;
41255     },
41256
41257     /**
41258      * Updates the tab body element to fit the height of the container element
41259      * for overflow scrolling
41260      * @param {Number} targetHeight (optional) Override the starting height from the elements height
41261      */
41262     syncHeight : function(targetHeight){
41263         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
41264         var bm = this.bodyEl.getMargins();
41265         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
41266         this.bodyEl.setHeight(newHeight);
41267         return newHeight;
41268     },
41269
41270     onResize : function(){
41271         if(this.monitorResize){
41272             this.autoSizeTabs();
41273         }
41274     },
41275
41276     /**
41277      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
41278      */
41279     beginUpdate : function(){
41280         this.updating = true;
41281     },
41282
41283     /**
41284      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
41285      */
41286     endUpdate : function(){
41287         this.updating = false;
41288         this.autoSizeTabs();
41289     },
41290
41291     /**
41292      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
41293      */
41294     autoSizeTabs : function()
41295     {
41296         var count = this.items.length;
41297         var vcount = count - this.hiddenCount;
41298         
41299         if (vcount < 2) {
41300             this.stripEl.hide();
41301         } else {
41302             this.stripEl.show();
41303         }
41304         
41305         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
41306             return;
41307         }
41308         
41309         
41310         var w = Math.max(this.el.getWidth() - this.cpad, 10);
41311         var availWidth = Math.floor(w / vcount);
41312         var b = this.stripBody;
41313         if(b.getWidth() > w){
41314             var tabs = this.items;
41315             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
41316             if(availWidth < this.minTabWidth){
41317                 /*if(!this.sleft){    // incomplete scrolling code
41318                     this.createScrollButtons();
41319                 }
41320                 this.showScroll();
41321                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
41322             }
41323         }else{
41324             if(this.currentTabWidth < this.preferredTabWidth){
41325                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
41326             }
41327         }
41328     },
41329
41330     /**
41331      * Returns the number of tabs in this TabPanel.
41332      * @return {Number}
41333      */
41334      getCount : function(){
41335          return this.items.length;
41336      },
41337
41338     /**
41339      * Resizes all the tabs to the passed width
41340      * @param {Number} The new width
41341      */
41342     setTabWidth : function(width){
41343         this.currentTabWidth = width;
41344         for(var i = 0, len = this.items.length; i < len; i++) {
41345                 if(!this.items[i].isHidden()) {
41346                 this.items[i].setWidth(width);
41347             }
41348         }
41349     },
41350
41351     /**
41352      * Destroys this TabPanel
41353      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
41354      */
41355     destroy : function(removeEl){
41356         Roo.EventManager.removeResizeListener(this.onResize, this);
41357         for(var i = 0, len = this.items.length; i < len; i++){
41358             this.items[i].purgeListeners();
41359         }
41360         if(removeEl === true){
41361             this.el.update("");
41362             this.el.remove();
41363         }
41364     },
41365     
41366     createStrip : function(container)
41367     {
41368         var strip = document.createElement("nav");
41369         strip.className = Roo.bootstrap.version == 4 ?
41370             "navbar-light bg-light" : 
41371             "navbar navbar-default"; //"x-tabs-wrap";
41372         container.appendChild(strip);
41373         return strip;
41374     },
41375     
41376     createStripList : function(strip)
41377     {
41378         // div wrapper for retard IE
41379         // returns the "tr" element.
41380         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41381         //'<div class="x-tabs-strip-wrap">'+
41382           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41383           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41384         return strip.firstChild; //.firstChild.firstChild.firstChild;
41385     },
41386     createBody : function(container)
41387     {
41388         var body = document.createElement("div");
41389         Roo.id(body, "tab-body");
41390         //Roo.fly(body).addClass("x-tabs-body");
41391         Roo.fly(body).addClass("tab-content");
41392         container.appendChild(body);
41393         return body;
41394     },
41395     createItemBody :function(bodyEl, id){
41396         var body = Roo.getDom(id);
41397         if(!body){
41398             body = document.createElement("div");
41399             body.id = id;
41400         }
41401         //Roo.fly(body).addClass("x-tabs-item-body");
41402         Roo.fly(body).addClass("tab-pane");
41403          bodyEl.insertBefore(body, bodyEl.firstChild);
41404         return body;
41405     },
41406     /** @private */
41407     createStripElements :  function(stripEl, text, closable, tpl)
41408     {
41409         var td = document.createElement("li"); // was td..
41410         td.className = 'nav-item';
41411         
41412         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41413         
41414         
41415         stripEl.appendChild(td);
41416         /*if(closable){
41417             td.className = "x-tabs-closable";
41418             if(!this.closeTpl){
41419                 this.closeTpl = new Roo.Template(
41420                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41421                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41422                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41423                 );
41424             }
41425             var el = this.closeTpl.overwrite(td, {"text": text});
41426             var close = el.getElementsByTagName("div")[0];
41427             var inner = el.getElementsByTagName("em")[0];
41428             return {"el": el, "close": close, "inner": inner};
41429         } else {
41430         */
41431         // not sure what this is..
41432 //            if(!this.tabTpl){
41433                 //this.tabTpl = new Roo.Template(
41434                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41435                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41436                 //);
41437 //                this.tabTpl = new Roo.Template(
41438 //                   '<a href="#">' +
41439 //                   '<span unselectable="on"' +
41440 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41441 //                            ' >{text}</span></a>'
41442 //                );
41443 //                
41444 //            }
41445
41446
41447             var template = tpl || this.tabTpl || false;
41448             
41449             if(!template){
41450                 template =  new Roo.Template(
41451                         Roo.bootstrap.version == 4 ? 
41452                             (
41453                                 '<a class="nav-link" href="#" unselectable="on"' +
41454                                      (this.disableTooltips ? '' : ' title="{text}"') +
41455                                      ' >{text}</a>'
41456                             ) : (
41457                                 '<a class="nav-link" href="#">' +
41458                                 '<span unselectable="on"' +
41459                                          (this.disableTooltips ? '' : ' title="{text}"') +
41460                                     ' >{text}</span></a>'
41461                             )
41462                 );
41463             }
41464             
41465             switch (typeof(template)) {
41466                 case 'object' :
41467                     break;
41468                 case 'string' :
41469                     template = new Roo.Template(template);
41470                     break;
41471                 default :
41472                     break;
41473             }
41474             
41475             var el = template.overwrite(td, {"text": text});
41476             
41477             var inner = el.getElementsByTagName("span")[0];
41478             
41479             return {"el": el, "inner": inner};
41480             
41481     }
41482         
41483     
41484 });
41485
41486 /**
41487  * @class Roo.TabPanelItem
41488  * @extends Roo.util.Observable
41489  * Represents an individual item (tab plus body) in a TabPanel.
41490  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41491  * @param {String} id The id of this TabPanelItem
41492  * @param {String} text The text for the tab of this TabPanelItem
41493  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41494  */
41495 Roo.bootstrap.panel.TabItem = function(config){
41496     /**
41497      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41498      * @type Roo.TabPanel
41499      */
41500     this.tabPanel = config.panel;
41501     /**
41502      * The id for this TabPanelItem
41503      * @type String
41504      */
41505     this.id = config.id;
41506     /** @private */
41507     this.disabled = false;
41508     /** @private */
41509     this.text = config.text;
41510     /** @private */
41511     this.loaded = false;
41512     this.closable = config.closable;
41513
41514     /**
41515      * The body element for this TabPanelItem.
41516      * @type Roo.Element
41517      */
41518     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41519     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41520     this.bodyEl.setStyle("display", "block");
41521     this.bodyEl.setStyle("zoom", "1");
41522     //this.hideAction();
41523
41524     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41525     /** @private */
41526     this.el = Roo.get(els.el);
41527     this.inner = Roo.get(els.inner, true);
41528      this.textEl = Roo.bootstrap.version == 4 ?
41529         this.el : Roo.get(this.el.dom.firstChild, true);
41530
41531     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41532     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41533
41534     
41535 //    this.el.on("mousedown", this.onTabMouseDown, this);
41536     this.el.on("click", this.onTabClick, this);
41537     /** @private */
41538     if(config.closable){
41539         var c = Roo.get(els.close, true);
41540         c.dom.title = this.closeText;
41541         c.addClassOnOver("close-over");
41542         c.on("click", this.closeClick, this);
41543      }
41544
41545     this.addEvents({
41546          /**
41547          * @event activate
41548          * Fires when this tab becomes the active tab.
41549          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41550          * @param {Roo.TabPanelItem} this
41551          */
41552         "activate": true,
41553         /**
41554          * @event beforeclose
41555          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41556          * @param {Roo.TabPanelItem} this
41557          * @param {Object} e Set cancel to true on this object to cancel the close.
41558          */
41559         "beforeclose": true,
41560         /**
41561          * @event close
41562          * Fires when this tab is closed.
41563          * @param {Roo.TabPanelItem} this
41564          */
41565          "close": true,
41566         /**
41567          * @event deactivate
41568          * Fires when this tab is no longer the active tab.
41569          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41570          * @param {Roo.TabPanelItem} this
41571          */
41572          "deactivate" : true
41573     });
41574     this.hidden = false;
41575
41576     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41577 };
41578
41579 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41580            {
41581     purgeListeners : function(){
41582        Roo.util.Observable.prototype.purgeListeners.call(this);
41583        this.el.removeAllListeners();
41584     },
41585     /**
41586      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41587      */
41588     show : function(){
41589         this.status_node.addClass("active");
41590         this.showAction();
41591         if(Roo.isOpera){
41592             this.tabPanel.stripWrap.repaint();
41593         }
41594         this.fireEvent("activate", this.tabPanel, this);
41595     },
41596
41597     /**
41598      * Returns true if this tab is the active tab.
41599      * @return {Boolean}
41600      */
41601     isActive : function(){
41602         return this.tabPanel.getActiveTab() == this;
41603     },
41604
41605     /**
41606      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41607      */
41608     hide : function(){
41609         this.status_node.removeClass("active");
41610         this.hideAction();
41611         this.fireEvent("deactivate", this.tabPanel, this);
41612     },
41613
41614     hideAction : function(){
41615         this.bodyEl.hide();
41616         this.bodyEl.setStyle("position", "absolute");
41617         this.bodyEl.setLeft("-20000px");
41618         this.bodyEl.setTop("-20000px");
41619     },
41620
41621     showAction : function(){
41622         this.bodyEl.setStyle("position", "relative");
41623         this.bodyEl.setTop("");
41624         this.bodyEl.setLeft("");
41625         this.bodyEl.show();
41626     },
41627
41628     /**
41629      * Set the tooltip for the tab.
41630      * @param {String} tooltip The tab's tooltip
41631      */
41632     setTooltip : function(text){
41633         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41634             this.textEl.dom.qtip = text;
41635             this.textEl.dom.removeAttribute('title');
41636         }else{
41637             this.textEl.dom.title = text;
41638         }
41639     },
41640
41641     onTabClick : function(e){
41642         e.preventDefault();
41643         this.tabPanel.activate(this.id);
41644     },
41645
41646     onTabMouseDown : function(e){
41647         e.preventDefault();
41648         this.tabPanel.activate(this.id);
41649     },
41650 /*
41651     getWidth : function(){
41652         return this.inner.getWidth();
41653     },
41654
41655     setWidth : function(width){
41656         var iwidth = width - this.linode.getPadding("lr");
41657         this.inner.setWidth(iwidth);
41658         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41659         this.linode.setWidth(width);
41660     },
41661 */
41662     /**
41663      * Show or hide the tab
41664      * @param {Boolean} hidden True to hide or false to show.
41665      */
41666     setHidden : function(hidden){
41667         this.hidden = hidden;
41668         this.linode.setStyle("display", hidden ? "none" : "");
41669     },
41670
41671     /**
41672      * Returns true if this tab is "hidden"
41673      * @return {Boolean}
41674      */
41675     isHidden : function(){
41676         return this.hidden;
41677     },
41678
41679     /**
41680      * Returns the text for this tab
41681      * @return {String}
41682      */
41683     getText : function(){
41684         return this.text;
41685     },
41686     /*
41687     autoSize : function(){
41688         //this.el.beginMeasure();
41689         this.textEl.setWidth(1);
41690         /*
41691          *  #2804 [new] Tabs in Roojs
41692          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41693          */
41694         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41695         //this.el.endMeasure();
41696     //},
41697
41698     /**
41699      * Sets the text for the tab (Note: this also sets the tooltip text)
41700      * @param {String} text The tab's text and tooltip
41701      */
41702     setText : function(text){
41703         this.text = text;
41704         this.textEl.update(text);
41705         this.setTooltip(text);
41706         //if(!this.tabPanel.resizeTabs){
41707         //    this.autoSize();
41708         //}
41709     },
41710     /**
41711      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41712      */
41713     activate : function(){
41714         this.tabPanel.activate(this.id);
41715     },
41716
41717     /**
41718      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41719      */
41720     disable : function(){
41721         if(this.tabPanel.active != this){
41722             this.disabled = true;
41723             this.status_node.addClass("disabled");
41724         }
41725     },
41726
41727     /**
41728      * Enables this TabPanelItem if it was previously disabled.
41729      */
41730     enable : function(){
41731         this.disabled = false;
41732         this.status_node.removeClass("disabled");
41733     },
41734
41735     /**
41736      * Sets the content for this TabPanelItem.
41737      * @param {String} content The content
41738      * @param {Boolean} loadScripts true to look for and load scripts
41739      */
41740     setContent : function(content, loadScripts){
41741         this.bodyEl.update(content, loadScripts);
41742     },
41743
41744     /**
41745      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41746      * @return {Roo.UpdateManager} The UpdateManager
41747      */
41748     getUpdateManager : function(){
41749         return this.bodyEl.getUpdateManager();
41750     },
41751
41752     /**
41753      * Set a URL to be used to load the content for this TabPanelItem.
41754      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41755      * @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)
41756      * @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)
41757      * @return {Roo.UpdateManager} The UpdateManager
41758      */
41759     setUrl : function(url, params, loadOnce){
41760         if(this.refreshDelegate){
41761             this.un('activate', this.refreshDelegate);
41762         }
41763         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41764         this.on("activate", this.refreshDelegate);
41765         return this.bodyEl.getUpdateManager();
41766     },
41767
41768     /** @private */
41769     _handleRefresh : function(url, params, loadOnce){
41770         if(!loadOnce || !this.loaded){
41771             var updater = this.bodyEl.getUpdateManager();
41772             updater.update(url, params, this._setLoaded.createDelegate(this));
41773         }
41774     },
41775
41776     /**
41777      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41778      *   Will fail silently if the setUrl method has not been called.
41779      *   This does not activate the panel, just updates its content.
41780      */
41781     refresh : function(){
41782         if(this.refreshDelegate){
41783            this.loaded = false;
41784            this.refreshDelegate();
41785         }
41786     },
41787
41788     /** @private */
41789     _setLoaded : function(){
41790         this.loaded = true;
41791     },
41792
41793     /** @private */
41794     closeClick : function(e){
41795         var o = {};
41796         e.stopEvent();
41797         this.fireEvent("beforeclose", this, o);
41798         if(o.cancel !== true){
41799             this.tabPanel.removeTab(this.id);
41800         }
41801     },
41802     /**
41803      * The text displayed in the tooltip for the close icon.
41804      * @type String
41805      */
41806     closeText : "Close this tab"
41807 });
41808 /**
41809 *    This script refer to:
41810 *    Title: International Telephone Input
41811 *    Author: Jack O'Connor
41812 *    Code version:  v12.1.12
41813 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41814 **/
41815
41816 Roo.bootstrap.PhoneInputData = function() {
41817     var d = [
41818       [
41819         "Afghanistan (‫افغانستان‬‎)",
41820         "af",
41821         "93"
41822       ],
41823       [
41824         "Albania (Shqipëri)",
41825         "al",
41826         "355"
41827       ],
41828       [
41829         "Algeria (‫الجزائر‬‎)",
41830         "dz",
41831         "213"
41832       ],
41833       [
41834         "American Samoa",
41835         "as",
41836         "1684"
41837       ],
41838       [
41839         "Andorra",
41840         "ad",
41841         "376"
41842       ],
41843       [
41844         "Angola",
41845         "ao",
41846         "244"
41847       ],
41848       [
41849         "Anguilla",
41850         "ai",
41851         "1264"
41852       ],
41853       [
41854         "Antigua and Barbuda",
41855         "ag",
41856         "1268"
41857       ],
41858       [
41859         "Argentina",
41860         "ar",
41861         "54"
41862       ],
41863       [
41864         "Armenia (Հայաստան)",
41865         "am",
41866         "374"
41867       ],
41868       [
41869         "Aruba",
41870         "aw",
41871         "297"
41872       ],
41873       [
41874         "Australia",
41875         "au",
41876         "61",
41877         0
41878       ],
41879       [
41880         "Austria (Österreich)",
41881         "at",
41882         "43"
41883       ],
41884       [
41885         "Azerbaijan (Azərbaycan)",
41886         "az",
41887         "994"
41888       ],
41889       [
41890         "Bahamas",
41891         "bs",
41892         "1242"
41893       ],
41894       [
41895         "Bahrain (‫البحرين‬‎)",
41896         "bh",
41897         "973"
41898       ],
41899       [
41900         "Bangladesh (বাংলাদেশ)",
41901         "bd",
41902         "880"
41903       ],
41904       [
41905         "Barbados",
41906         "bb",
41907         "1246"
41908       ],
41909       [
41910         "Belarus (Беларусь)",
41911         "by",
41912         "375"
41913       ],
41914       [
41915         "Belgium (België)",
41916         "be",
41917         "32"
41918       ],
41919       [
41920         "Belize",
41921         "bz",
41922         "501"
41923       ],
41924       [
41925         "Benin (Bénin)",
41926         "bj",
41927         "229"
41928       ],
41929       [
41930         "Bermuda",
41931         "bm",
41932         "1441"
41933       ],
41934       [
41935         "Bhutan (འབྲུག)",
41936         "bt",
41937         "975"
41938       ],
41939       [
41940         "Bolivia",
41941         "bo",
41942         "591"
41943       ],
41944       [
41945         "Bosnia and Herzegovina (Босна и Херцеговина)",
41946         "ba",
41947         "387"
41948       ],
41949       [
41950         "Botswana",
41951         "bw",
41952         "267"
41953       ],
41954       [
41955         "Brazil (Brasil)",
41956         "br",
41957         "55"
41958       ],
41959       [
41960         "British Indian Ocean Territory",
41961         "io",
41962         "246"
41963       ],
41964       [
41965         "British Virgin Islands",
41966         "vg",
41967         "1284"
41968       ],
41969       [
41970         "Brunei",
41971         "bn",
41972         "673"
41973       ],
41974       [
41975         "Bulgaria (България)",
41976         "bg",
41977         "359"
41978       ],
41979       [
41980         "Burkina Faso",
41981         "bf",
41982         "226"
41983       ],
41984       [
41985         "Burundi (Uburundi)",
41986         "bi",
41987         "257"
41988       ],
41989       [
41990         "Cambodia (កម្ពុជា)",
41991         "kh",
41992         "855"
41993       ],
41994       [
41995         "Cameroon (Cameroun)",
41996         "cm",
41997         "237"
41998       ],
41999       [
42000         "Canada",
42001         "ca",
42002         "1",
42003         1,
42004         ["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"]
42005       ],
42006       [
42007         "Cape Verde (Kabu Verdi)",
42008         "cv",
42009         "238"
42010       ],
42011       [
42012         "Caribbean Netherlands",
42013         "bq",
42014         "599",
42015         1
42016       ],
42017       [
42018         "Cayman Islands",
42019         "ky",
42020         "1345"
42021       ],
42022       [
42023         "Central African Republic (République centrafricaine)",
42024         "cf",
42025         "236"
42026       ],
42027       [
42028         "Chad (Tchad)",
42029         "td",
42030         "235"
42031       ],
42032       [
42033         "Chile",
42034         "cl",
42035         "56"
42036       ],
42037       [
42038         "China (中国)",
42039         "cn",
42040         "86"
42041       ],
42042       [
42043         "Christmas Island",
42044         "cx",
42045         "61",
42046         2
42047       ],
42048       [
42049         "Cocos (Keeling) Islands",
42050         "cc",
42051         "61",
42052         1
42053       ],
42054       [
42055         "Colombia",
42056         "co",
42057         "57"
42058       ],
42059       [
42060         "Comoros (‫جزر القمر‬‎)",
42061         "km",
42062         "269"
42063       ],
42064       [
42065         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
42066         "cd",
42067         "243"
42068       ],
42069       [
42070         "Congo (Republic) (Congo-Brazzaville)",
42071         "cg",
42072         "242"
42073       ],
42074       [
42075         "Cook Islands",
42076         "ck",
42077         "682"
42078       ],
42079       [
42080         "Costa Rica",
42081         "cr",
42082         "506"
42083       ],
42084       [
42085         "Côte d’Ivoire",
42086         "ci",
42087         "225"
42088       ],
42089       [
42090         "Croatia (Hrvatska)",
42091         "hr",
42092         "385"
42093       ],
42094       [
42095         "Cuba",
42096         "cu",
42097         "53"
42098       ],
42099       [
42100         "Curaçao",
42101         "cw",
42102         "599",
42103         0
42104       ],
42105       [
42106         "Cyprus (Κύπρος)",
42107         "cy",
42108         "357"
42109       ],
42110       [
42111         "Czech Republic (Česká republika)",
42112         "cz",
42113         "420"
42114       ],
42115       [
42116         "Denmark (Danmark)",
42117         "dk",
42118         "45"
42119       ],
42120       [
42121         "Djibouti",
42122         "dj",
42123         "253"
42124       ],
42125       [
42126         "Dominica",
42127         "dm",
42128         "1767"
42129       ],
42130       [
42131         "Dominican Republic (República Dominicana)",
42132         "do",
42133         "1",
42134         2,
42135         ["809", "829", "849"]
42136       ],
42137       [
42138         "Ecuador",
42139         "ec",
42140         "593"
42141       ],
42142       [
42143         "Egypt (‫مصر‬‎)",
42144         "eg",
42145         "20"
42146       ],
42147       [
42148         "El Salvador",
42149         "sv",
42150         "503"
42151       ],
42152       [
42153         "Equatorial Guinea (Guinea Ecuatorial)",
42154         "gq",
42155         "240"
42156       ],
42157       [
42158         "Eritrea",
42159         "er",
42160         "291"
42161       ],
42162       [
42163         "Estonia (Eesti)",
42164         "ee",
42165         "372"
42166       ],
42167       [
42168         "Ethiopia",
42169         "et",
42170         "251"
42171       ],
42172       [
42173         "Falkland Islands (Islas Malvinas)",
42174         "fk",
42175         "500"
42176       ],
42177       [
42178         "Faroe Islands (Føroyar)",
42179         "fo",
42180         "298"
42181       ],
42182       [
42183         "Fiji",
42184         "fj",
42185         "679"
42186       ],
42187       [
42188         "Finland (Suomi)",
42189         "fi",
42190         "358",
42191         0
42192       ],
42193       [
42194         "France",
42195         "fr",
42196         "33"
42197       ],
42198       [
42199         "French Guiana (Guyane française)",
42200         "gf",
42201         "594"
42202       ],
42203       [
42204         "French Polynesia (Polynésie française)",
42205         "pf",
42206         "689"
42207       ],
42208       [
42209         "Gabon",
42210         "ga",
42211         "241"
42212       ],
42213       [
42214         "Gambia",
42215         "gm",
42216         "220"
42217       ],
42218       [
42219         "Georgia (საქართველო)",
42220         "ge",
42221         "995"
42222       ],
42223       [
42224         "Germany (Deutschland)",
42225         "de",
42226         "49"
42227       ],
42228       [
42229         "Ghana (Gaana)",
42230         "gh",
42231         "233"
42232       ],
42233       [
42234         "Gibraltar",
42235         "gi",
42236         "350"
42237       ],
42238       [
42239         "Greece (Ελλάδα)",
42240         "gr",
42241         "30"
42242       ],
42243       [
42244         "Greenland (Kalaallit Nunaat)",
42245         "gl",
42246         "299"
42247       ],
42248       [
42249         "Grenada",
42250         "gd",
42251         "1473"
42252       ],
42253       [
42254         "Guadeloupe",
42255         "gp",
42256         "590",
42257         0
42258       ],
42259       [
42260         "Guam",
42261         "gu",
42262         "1671"
42263       ],
42264       [
42265         "Guatemala",
42266         "gt",
42267         "502"
42268       ],
42269       [
42270         "Guernsey",
42271         "gg",
42272         "44",
42273         1
42274       ],
42275       [
42276         "Guinea (Guinée)",
42277         "gn",
42278         "224"
42279       ],
42280       [
42281         "Guinea-Bissau (Guiné Bissau)",
42282         "gw",
42283         "245"
42284       ],
42285       [
42286         "Guyana",
42287         "gy",
42288         "592"
42289       ],
42290       [
42291         "Haiti",
42292         "ht",
42293         "509"
42294       ],
42295       [
42296         "Honduras",
42297         "hn",
42298         "504"
42299       ],
42300       [
42301         "Hong Kong (香港)",
42302         "hk",
42303         "852"
42304       ],
42305       [
42306         "Hungary (Magyarország)",
42307         "hu",
42308         "36"
42309       ],
42310       [
42311         "Iceland (Ísland)",
42312         "is",
42313         "354"
42314       ],
42315       [
42316         "India (भारत)",
42317         "in",
42318         "91"
42319       ],
42320       [
42321         "Indonesia",
42322         "id",
42323         "62"
42324       ],
42325       [
42326         "Iran (‫ایران‬‎)",
42327         "ir",
42328         "98"
42329       ],
42330       [
42331         "Iraq (‫العراق‬‎)",
42332         "iq",
42333         "964"
42334       ],
42335       [
42336         "Ireland",
42337         "ie",
42338         "353"
42339       ],
42340       [
42341         "Isle of Man",
42342         "im",
42343         "44",
42344         2
42345       ],
42346       [
42347         "Israel (‫ישראל‬‎)",
42348         "il",
42349         "972"
42350       ],
42351       [
42352         "Italy (Italia)",
42353         "it",
42354         "39",
42355         0
42356       ],
42357       [
42358         "Jamaica",
42359         "jm",
42360         "1876"
42361       ],
42362       [
42363         "Japan (日本)",
42364         "jp",
42365         "81"
42366       ],
42367       [
42368         "Jersey",
42369         "je",
42370         "44",
42371         3
42372       ],
42373       [
42374         "Jordan (‫الأردن‬‎)",
42375         "jo",
42376         "962"
42377       ],
42378       [
42379         "Kazakhstan (Казахстан)",
42380         "kz",
42381         "7",
42382         1
42383       ],
42384       [
42385         "Kenya",
42386         "ke",
42387         "254"
42388       ],
42389       [
42390         "Kiribati",
42391         "ki",
42392         "686"
42393       ],
42394       [
42395         "Kosovo",
42396         "xk",
42397         "383"
42398       ],
42399       [
42400         "Kuwait (‫الكويت‬‎)",
42401         "kw",
42402         "965"
42403       ],
42404       [
42405         "Kyrgyzstan (Кыргызстан)",
42406         "kg",
42407         "996"
42408       ],
42409       [
42410         "Laos (ລາວ)",
42411         "la",
42412         "856"
42413       ],
42414       [
42415         "Latvia (Latvija)",
42416         "lv",
42417         "371"
42418       ],
42419       [
42420         "Lebanon (‫لبنان‬‎)",
42421         "lb",
42422         "961"
42423       ],
42424       [
42425         "Lesotho",
42426         "ls",
42427         "266"
42428       ],
42429       [
42430         "Liberia",
42431         "lr",
42432         "231"
42433       ],
42434       [
42435         "Libya (‫ليبيا‬‎)",
42436         "ly",
42437         "218"
42438       ],
42439       [
42440         "Liechtenstein",
42441         "li",
42442         "423"
42443       ],
42444       [
42445         "Lithuania (Lietuva)",
42446         "lt",
42447         "370"
42448       ],
42449       [
42450         "Luxembourg",
42451         "lu",
42452         "352"
42453       ],
42454       [
42455         "Macau (澳門)",
42456         "mo",
42457         "853"
42458       ],
42459       [
42460         "Macedonia (FYROM) (Македонија)",
42461         "mk",
42462         "389"
42463       ],
42464       [
42465         "Madagascar (Madagasikara)",
42466         "mg",
42467         "261"
42468       ],
42469       [
42470         "Malawi",
42471         "mw",
42472         "265"
42473       ],
42474       [
42475         "Malaysia",
42476         "my",
42477         "60"
42478       ],
42479       [
42480         "Maldives",
42481         "mv",
42482         "960"
42483       ],
42484       [
42485         "Mali",
42486         "ml",
42487         "223"
42488       ],
42489       [
42490         "Malta",
42491         "mt",
42492         "356"
42493       ],
42494       [
42495         "Marshall Islands",
42496         "mh",
42497         "692"
42498       ],
42499       [
42500         "Martinique",
42501         "mq",
42502         "596"
42503       ],
42504       [
42505         "Mauritania (‫موريتانيا‬‎)",
42506         "mr",
42507         "222"
42508       ],
42509       [
42510         "Mauritius (Moris)",
42511         "mu",
42512         "230"
42513       ],
42514       [
42515         "Mayotte",
42516         "yt",
42517         "262",
42518         1
42519       ],
42520       [
42521         "Mexico (México)",
42522         "mx",
42523         "52"
42524       ],
42525       [
42526         "Micronesia",
42527         "fm",
42528         "691"
42529       ],
42530       [
42531         "Moldova (Republica Moldova)",
42532         "md",
42533         "373"
42534       ],
42535       [
42536         "Monaco",
42537         "mc",
42538         "377"
42539       ],
42540       [
42541         "Mongolia (Монгол)",
42542         "mn",
42543         "976"
42544       ],
42545       [
42546         "Montenegro (Crna Gora)",
42547         "me",
42548         "382"
42549       ],
42550       [
42551         "Montserrat",
42552         "ms",
42553         "1664"
42554       ],
42555       [
42556         "Morocco (‫المغرب‬‎)",
42557         "ma",
42558         "212",
42559         0
42560       ],
42561       [
42562         "Mozambique (Moçambique)",
42563         "mz",
42564         "258"
42565       ],
42566       [
42567         "Myanmar (Burma) (မြန်မာ)",
42568         "mm",
42569         "95"
42570       ],
42571       [
42572         "Namibia (Namibië)",
42573         "na",
42574         "264"
42575       ],
42576       [
42577         "Nauru",
42578         "nr",
42579         "674"
42580       ],
42581       [
42582         "Nepal (नेपाल)",
42583         "np",
42584         "977"
42585       ],
42586       [
42587         "Netherlands (Nederland)",
42588         "nl",
42589         "31"
42590       ],
42591       [
42592         "New Caledonia (Nouvelle-Calédonie)",
42593         "nc",
42594         "687"
42595       ],
42596       [
42597         "New Zealand",
42598         "nz",
42599         "64"
42600       ],
42601       [
42602         "Nicaragua",
42603         "ni",
42604         "505"
42605       ],
42606       [
42607         "Niger (Nijar)",
42608         "ne",
42609         "227"
42610       ],
42611       [
42612         "Nigeria",
42613         "ng",
42614         "234"
42615       ],
42616       [
42617         "Niue",
42618         "nu",
42619         "683"
42620       ],
42621       [
42622         "Norfolk Island",
42623         "nf",
42624         "672"
42625       ],
42626       [
42627         "North Korea (조선 민주주의 인민 공화국)",
42628         "kp",
42629         "850"
42630       ],
42631       [
42632         "Northern Mariana Islands",
42633         "mp",
42634         "1670"
42635       ],
42636       [
42637         "Norway (Norge)",
42638         "no",
42639         "47",
42640         0
42641       ],
42642       [
42643         "Oman (‫عُمان‬‎)",
42644         "om",
42645         "968"
42646       ],
42647       [
42648         "Pakistan (‫پاکستان‬‎)",
42649         "pk",
42650         "92"
42651       ],
42652       [
42653         "Palau",
42654         "pw",
42655         "680"
42656       ],
42657       [
42658         "Palestine (‫فلسطين‬‎)",
42659         "ps",
42660         "970"
42661       ],
42662       [
42663         "Panama (Panamá)",
42664         "pa",
42665         "507"
42666       ],
42667       [
42668         "Papua New Guinea",
42669         "pg",
42670         "675"
42671       ],
42672       [
42673         "Paraguay",
42674         "py",
42675         "595"
42676       ],
42677       [
42678         "Peru (Perú)",
42679         "pe",
42680         "51"
42681       ],
42682       [
42683         "Philippines",
42684         "ph",
42685         "63"
42686       ],
42687       [
42688         "Poland (Polska)",
42689         "pl",
42690         "48"
42691       ],
42692       [
42693         "Portugal",
42694         "pt",
42695         "351"
42696       ],
42697       [
42698         "Puerto Rico",
42699         "pr",
42700         "1",
42701         3,
42702         ["787", "939"]
42703       ],
42704       [
42705         "Qatar (‫قطر‬‎)",
42706         "qa",
42707         "974"
42708       ],
42709       [
42710         "Réunion (La Réunion)",
42711         "re",
42712         "262",
42713         0
42714       ],
42715       [
42716         "Romania (România)",
42717         "ro",
42718         "40"
42719       ],
42720       [
42721         "Russia (Россия)",
42722         "ru",
42723         "7",
42724         0
42725       ],
42726       [
42727         "Rwanda",
42728         "rw",
42729         "250"
42730       ],
42731       [
42732         "Saint Barthélemy",
42733         "bl",
42734         "590",
42735         1
42736       ],
42737       [
42738         "Saint Helena",
42739         "sh",
42740         "290"
42741       ],
42742       [
42743         "Saint Kitts and Nevis",
42744         "kn",
42745         "1869"
42746       ],
42747       [
42748         "Saint Lucia",
42749         "lc",
42750         "1758"
42751       ],
42752       [
42753         "Saint Martin (Saint-Martin (partie française))",
42754         "mf",
42755         "590",
42756         2
42757       ],
42758       [
42759         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42760         "pm",
42761         "508"
42762       ],
42763       [
42764         "Saint Vincent and the Grenadines",
42765         "vc",
42766         "1784"
42767       ],
42768       [
42769         "Samoa",
42770         "ws",
42771         "685"
42772       ],
42773       [
42774         "San Marino",
42775         "sm",
42776         "378"
42777       ],
42778       [
42779         "São Tomé and Príncipe (São Tomé e Príncipe)",
42780         "st",
42781         "239"
42782       ],
42783       [
42784         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42785         "sa",
42786         "966"
42787       ],
42788       [
42789         "Senegal (Sénégal)",
42790         "sn",
42791         "221"
42792       ],
42793       [
42794         "Serbia (Србија)",
42795         "rs",
42796         "381"
42797       ],
42798       [
42799         "Seychelles",
42800         "sc",
42801         "248"
42802       ],
42803       [
42804         "Sierra Leone",
42805         "sl",
42806         "232"
42807       ],
42808       [
42809         "Singapore",
42810         "sg",
42811         "65"
42812       ],
42813       [
42814         "Sint Maarten",
42815         "sx",
42816         "1721"
42817       ],
42818       [
42819         "Slovakia (Slovensko)",
42820         "sk",
42821         "421"
42822       ],
42823       [
42824         "Slovenia (Slovenija)",
42825         "si",
42826         "386"
42827       ],
42828       [
42829         "Solomon Islands",
42830         "sb",
42831         "677"
42832       ],
42833       [
42834         "Somalia (Soomaaliya)",
42835         "so",
42836         "252"
42837       ],
42838       [
42839         "South Africa",
42840         "za",
42841         "27"
42842       ],
42843       [
42844         "South Korea (대한민국)",
42845         "kr",
42846         "82"
42847       ],
42848       [
42849         "South Sudan (‫جنوب السودان‬‎)",
42850         "ss",
42851         "211"
42852       ],
42853       [
42854         "Spain (España)",
42855         "es",
42856         "34"
42857       ],
42858       [
42859         "Sri Lanka (ශ්‍රී ලංකාව)",
42860         "lk",
42861         "94"
42862       ],
42863       [
42864         "Sudan (‫السودان‬‎)",
42865         "sd",
42866         "249"
42867       ],
42868       [
42869         "Suriname",
42870         "sr",
42871         "597"
42872       ],
42873       [
42874         "Svalbard and Jan Mayen",
42875         "sj",
42876         "47",
42877         1
42878       ],
42879       [
42880         "Swaziland",
42881         "sz",
42882         "268"
42883       ],
42884       [
42885         "Sweden (Sverige)",
42886         "se",
42887         "46"
42888       ],
42889       [
42890         "Switzerland (Schweiz)",
42891         "ch",
42892         "41"
42893       ],
42894       [
42895         "Syria (‫سوريا‬‎)",
42896         "sy",
42897         "963"
42898       ],
42899       [
42900         "Taiwan (台灣)",
42901         "tw",
42902         "886"
42903       ],
42904       [
42905         "Tajikistan",
42906         "tj",
42907         "992"
42908       ],
42909       [
42910         "Tanzania",
42911         "tz",
42912         "255"
42913       ],
42914       [
42915         "Thailand (ไทย)",
42916         "th",
42917         "66"
42918       ],
42919       [
42920         "Timor-Leste",
42921         "tl",
42922         "670"
42923       ],
42924       [
42925         "Togo",
42926         "tg",
42927         "228"
42928       ],
42929       [
42930         "Tokelau",
42931         "tk",
42932         "690"
42933       ],
42934       [
42935         "Tonga",
42936         "to",
42937         "676"
42938       ],
42939       [
42940         "Trinidad and Tobago",
42941         "tt",
42942         "1868"
42943       ],
42944       [
42945         "Tunisia (‫تونس‬‎)",
42946         "tn",
42947         "216"
42948       ],
42949       [
42950         "Turkey (Türkiye)",
42951         "tr",
42952         "90"
42953       ],
42954       [
42955         "Turkmenistan",
42956         "tm",
42957         "993"
42958       ],
42959       [
42960         "Turks and Caicos Islands",
42961         "tc",
42962         "1649"
42963       ],
42964       [
42965         "Tuvalu",
42966         "tv",
42967         "688"
42968       ],
42969       [
42970         "U.S. Virgin Islands",
42971         "vi",
42972         "1340"
42973       ],
42974       [
42975         "Uganda",
42976         "ug",
42977         "256"
42978       ],
42979       [
42980         "Ukraine (Україна)",
42981         "ua",
42982         "380"
42983       ],
42984       [
42985         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42986         "ae",
42987         "971"
42988       ],
42989       [
42990         "United Kingdom",
42991         "gb",
42992         "44",
42993         0
42994       ],
42995       [
42996         "United States",
42997         "us",
42998         "1",
42999         0
43000       ],
43001       [
43002         "Uruguay",
43003         "uy",
43004         "598"
43005       ],
43006       [
43007         "Uzbekistan (Oʻzbekiston)",
43008         "uz",
43009         "998"
43010       ],
43011       [
43012         "Vanuatu",
43013         "vu",
43014         "678"
43015       ],
43016       [
43017         "Vatican City (Città del Vaticano)",
43018         "va",
43019         "39",
43020         1
43021       ],
43022       [
43023         "Venezuela",
43024         "ve",
43025         "58"
43026       ],
43027       [
43028         "Vietnam (Việt Nam)",
43029         "vn",
43030         "84"
43031       ],
43032       [
43033         "Wallis and Futuna (Wallis-et-Futuna)",
43034         "wf",
43035         "681"
43036       ],
43037       [
43038         "Western Sahara (‫الصحراء الغربية‬‎)",
43039         "eh",
43040         "212",
43041         1
43042       ],
43043       [
43044         "Yemen (‫اليمن‬‎)",
43045         "ye",
43046         "967"
43047       ],
43048       [
43049         "Zambia",
43050         "zm",
43051         "260"
43052       ],
43053       [
43054         "Zimbabwe",
43055         "zw",
43056         "263"
43057       ],
43058       [
43059         "Åland Islands",
43060         "ax",
43061         "358",
43062         1
43063       ]
43064   ];
43065   
43066   return d;
43067 }/**
43068 *    This script refer to:
43069 *    Title: International Telephone Input
43070 *    Author: Jack O'Connor
43071 *    Code version:  v12.1.12
43072 *    Availability: https://github.com/jackocnr/intl-tel-input.git
43073 **/
43074
43075 /**
43076  * @class Roo.bootstrap.PhoneInput
43077  * @extends Roo.bootstrap.TriggerField
43078  * An input with International dial-code selection
43079  
43080  * @cfg {String} defaultDialCode default '+852'
43081  * @cfg {Array} preferedCountries default []
43082   
43083  * @constructor
43084  * Create a new PhoneInput.
43085  * @param {Object} config Configuration options
43086  */
43087
43088 Roo.bootstrap.PhoneInput = function(config) {
43089     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
43090 };
43091
43092 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
43093         
43094         listWidth: undefined,
43095         
43096         selectedClass: 'active',
43097         
43098         invalidClass : "has-warning",
43099         
43100         validClass: 'has-success',
43101         
43102         allowed: '0123456789',
43103         
43104         max_length: 15,
43105         
43106         /**
43107          * @cfg {String} defaultDialCode The default dial code when initializing the input
43108          */
43109         defaultDialCode: '+852',
43110         
43111         /**
43112          * @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
43113          */
43114         preferedCountries: false,
43115         
43116         getAutoCreate : function()
43117         {
43118             var data = Roo.bootstrap.PhoneInputData();
43119             var align = this.labelAlign || this.parentLabelAlign();
43120             var id = Roo.id();
43121             
43122             this.allCountries = [];
43123             this.dialCodeMapping = [];
43124             
43125             for (var i = 0; i < data.length; i++) {
43126               var c = data[i];
43127               this.allCountries[i] = {
43128                 name: c[0],
43129                 iso2: c[1],
43130                 dialCode: c[2],
43131                 priority: c[3] || 0,
43132                 areaCodes: c[4] || null
43133               };
43134               this.dialCodeMapping[c[2]] = {
43135                   name: c[0],
43136                   iso2: c[1],
43137                   priority: c[3] || 0,
43138                   areaCodes: c[4] || null
43139               };
43140             }
43141             
43142             var cfg = {
43143                 cls: 'form-group',
43144                 cn: []
43145             };
43146             
43147             var input =  {
43148                 tag: 'input',
43149                 id : id,
43150                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
43151                 maxlength: this.max_length,
43152                 cls : 'form-control tel-input',
43153                 autocomplete: 'new-password'
43154             };
43155             
43156             var hiddenInput = {
43157                 tag: 'input',
43158                 type: 'hidden',
43159                 cls: 'hidden-tel-input'
43160             };
43161             
43162             if (this.name) {
43163                 hiddenInput.name = this.name;
43164             }
43165             
43166             if (this.disabled) {
43167                 input.disabled = true;
43168             }
43169             
43170             var flag_container = {
43171                 tag: 'div',
43172                 cls: 'flag-box',
43173                 cn: [
43174                     {
43175                         tag: 'div',
43176                         cls: 'flag'
43177                     },
43178                     {
43179                         tag: 'div',
43180                         cls: 'caret'
43181                     }
43182                 ]
43183             };
43184             
43185             var box = {
43186                 tag: 'div',
43187                 cls: this.hasFeedback ? 'has-feedback' : '',
43188                 cn: [
43189                     hiddenInput,
43190                     input,
43191                     {
43192                         tag: 'input',
43193                         cls: 'dial-code-holder',
43194                         disabled: true
43195                     }
43196                 ]
43197             };
43198             
43199             var container = {
43200                 cls: 'roo-select2-container input-group',
43201                 cn: [
43202                     flag_container,
43203                     box
43204                 ]
43205             };
43206             
43207             if (this.fieldLabel.length) {
43208                 var indicator = {
43209                     tag: 'i',
43210                     tooltip: 'This field is required'
43211                 };
43212                 
43213                 var label = {
43214                     tag: 'label',
43215                     'for':  id,
43216                     cls: 'control-label',
43217                     cn: []
43218                 };
43219                 
43220                 var label_text = {
43221                     tag: 'span',
43222                     html: this.fieldLabel
43223                 };
43224                 
43225                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43226                 label.cn = [
43227                     indicator,
43228                     label_text
43229                 ];
43230                 
43231                 if(this.indicatorpos == 'right') {
43232                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43233                     label.cn = [
43234                         label_text,
43235                         indicator
43236                     ];
43237                 }
43238                 
43239                 if(align == 'left') {
43240                     container = {
43241                         tag: 'div',
43242                         cn: [
43243                             container
43244                         ]
43245                     };
43246                     
43247                     if(this.labelWidth > 12){
43248                         label.style = "width: " + this.labelWidth + 'px';
43249                     }
43250                     if(this.labelWidth < 13 && this.labelmd == 0){
43251                         this.labelmd = this.labelWidth;
43252                     }
43253                     if(this.labellg > 0){
43254                         label.cls += ' col-lg-' + this.labellg;
43255                         input.cls += ' col-lg-' + (12 - this.labellg);
43256                     }
43257                     if(this.labelmd > 0){
43258                         label.cls += ' col-md-' + this.labelmd;
43259                         container.cls += ' col-md-' + (12 - this.labelmd);
43260                     }
43261                     if(this.labelsm > 0){
43262                         label.cls += ' col-sm-' + this.labelsm;
43263                         container.cls += ' col-sm-' + (12 - this.labelsm);
43264                     }
43265                     if(this.labelxs > 0){
43266                         label.cls += ' col-xs-' + this.labelxs;
43267                         container.cls += ' col-xs-' + (12 - this.labelxs);
43268                     }
43269                 }
43270             }
43271             
43272             cfg.cn = [
43273                 label,
43274                 container
43275             ];
43276             
43277             var settings = this;
43278             
43279             ['xs','sm','md','lg'].map(function(size){
43280                 if (settings[size]) {
43281                     cfg.cls += ' col-' + size + '-' + settings[size];
43282                 }
43283             });
43284             
43285             this.store = new Roo.data.Store({
43286                 proxy : new Roo.data.MemoryProxy({}),
43287                 reader : new Roo.data.JsonReader({
43288                     fields : [
43289                         {
43290                             'name' : 'name',
43291                             'type' : 'string'
43292                         },
43293                         {
43294                             'name' : 'iso2',
43295                             'type' : 'string'
43296                         },
43297                         {
43298                             'name' : 'dialCode',
43299                             'type' : 'string'
43300                         },
43301                         {
43302                             'name' : 'priority',
43303                             'type' : 'string'
43304                         },
43305                         {
43306                             'name' : 'areaCodes',
43307                             'type' : 'string'
43308                         }
43309                     ]
43310                 })
43311             });
43312             
43313             if(!this.preferedCountries) {
43314                 this.preferedCountries = [
43315                     'hk',
43316                     'gb',
43317                     'us'
43318                 ];
43319             }
43320             
43321             var p = this.preferedCountries.reverse();
43322             
43323             if(p) {
43324                 for (var i = 0; i < p.length; i++) {
43325                     for (var j = 0; j < this.allCountries.length; j++) {
43326                         if(this.allCountries[j].iso2 == p[i]) {
43327                             var t = this.allCountries[j];
43328                             this.allCountries.splice(j,1);
43329                             this.allCountries.unshift(t);
43330                         }
43331                     } 
43332                 }
43333             }
43334             
43335             this.store.proxy.data = {
43336                 success: true,
43337                 data: this.allCountries
43338             };
43339             
43340             return cfg;
43341         },
43342         
43343         initEvents : function()
43344         {
43345             this.createList();
43346             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
43347             
43348             this.indicator = this.indicatorEl();
43349             this.flag = this.flagEl();
43350             this.dialCodeHolder = this.dialCodeHolderEl();
43351             
43352             this.trigger = this.el.select('div.flag-box',true).first();
43353             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
43354             
43355             var _this = this;
43356             
43357             (function(){
43358                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43359                 _this.list.setWidth(lw);
43360             }).defer(100);
43361             
43362             this.list.on('mouseover', this.onViewOver, this);
43363             this.list.on('mousemove', this.onViewMove, this);
43364             this.inputEl().on("keyup", this.onKeyUp, this);
43365             this.inputEl().on("keypress", this.onKeyPress, this);
43366             
43367             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43368
43369             this.view = new Roo.View(this.list, this.tpl, {
43370                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43371             });
43372             
43373             this.view.on('click', this.onViewClick, this);
43374             this.setValue(this.defaultDialCode);
43375         },
43376         
43377         onTriggerClick : function(e)
43378         {
43379             Roo.log('trigger click');
43380             if(this.disabled){
43381                 return;
43382             }
43383             
43384             if(this.isExpanded()){
43385                 this.collapse();
43386                 this.hasFocus = false;
43387             }else {
43388                 this.store.load({});
43389                 this.hasFocus = true;
43390                 this.expand();
43391             }
43392         },
43393         
43394         isExpanded : function()
43395         {
43396             return this.list.isVisible();
43397         },
43398         
43399         collapse : function()
43400         {
43401             if(!this.isExpanded()){
43402                 return;
43403             }
43404             this.list.hide();
43405             Roo.get(document).un('mousedown', this.collapseIf, this);
43406             Roo.get(document).un('mousewheel', this.collapseIf, this);
43407             this.fireEvent('collapse', this);
43408             this.validate();
43409         },
43410         
43411         expand : function()
43412         {
43413             Roo.log('expand');
43414
43415             if(this.isExpanded() || !this.hasFocus){
43416                 return;
43417             }
43418             
43419             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43420             this.list.setWidth(lw);
43421             
43422             this.list.show();
43423             this.restrictHeight();
43424             
43425             Roo.get(document).on('mousedown', this.collapseIf, this);
43426             Roo.get(document).on('mousewheel', this.collapseIf, this);
43427             
43428             this.fireEvent('expand', this);
43429         },
43430         
43431         restrictHeight : function()
43432         {
43433             this.list.alignTo(this.inputEl(), this.listAlign);
43434             this.list.alignTo(this.inputEl(), this.listAlign);
43435         },
43436         
43437         onViewOver : function(e, t)
43438         {
43439             if(this.inKeyMode){
43440                 return;
43441             }
43442             var item = this.view.findItemFromChild(t);
43443             
43444             if(item){
43445                 var index = this.view.indexOf(item);
43446                 this.select(index, false);
43447             }
43448         },
43449
43450         // private
43451         onViewClick : function(view, doFocus, el, e)
43452         {
43453             var index = this.view.getSelectedIndexes()[0];
43454             
43455             var r = this.store.getAt(index);
43456             
43457             if(r){
43458                 this.onSelect(r, index);
43459             }
43460             if(doFocus !== false && !this.blockFocus){
43461                 this.inputEl().focus();
43462             }
43463         },
43464         
43465         onViewMove : function(e, t)
43466         {
43467             this.inKeyMode = false;
43468         },
43469         
43470         select : function(index, scrollIntoView)
43471         {
43472             this.selectedIndex = index;
43473             this.view.select(index);
43474             if(scrollIntoView !== false){
43475                 var el = this.view.getNode(index);
43476                 if(el){
43477                     this.list.scrollChildIntoView(el, false);
43478                 }
43479             }
43480         },
43481         
43482         createList : function()
43483         {
43484             this.list = Roo.get(document.body).createChild({
43485                 tag: 'ul',
43486                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43487                 style: 'display:none'
43488             });
43489             
43490             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43491         },
43492         
43493         collapseIf : function(e)
43494         {
43495             var in_combo  = e.within(this.el);
43496             var in_list =  e.within(this.list);
43497             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43498             
43499             if (in_combo || in_list || is_list) {
43500                 return;
43501             }
43502             this.collapse();
43503         },
43504         
43505         onSelect : function(record, index)
43506         {
43507             if(this.fireEvent('beforeselect', this, record, index) !== false){
43508                 
43509                 this.setFlagClass(record.data.iso2);
43510                 this.setDialCode(record.data.dialCode);
43511                 this.hasFocus = false;
43512                 this.collapse();
43513                 this.fireEvent('select', this, record, index);
43514             }
43515         },
43516         
43517         flagEl : function()
43518         {
43519             var flag = this.el.select('div.flag',true).first();
43520             if(!flag){
43521                 return false;
43522             }
43523             return flag;
43524         },
43525         
43526         dialCodeHolderEl : function()
43527         {
43528             var d = this.el.select('input.dial-code-holder',true).first();
43529             if(!d){
43530                 return false;
43531             }
43532             return d;
43533         },
43534         
43535         setDialCode : function(v)
43536         {
43537             this.dialCodeHolder.dom.value = '+'+v;
43538         },
43539         
43540         setFlagClass : function(n)
43541         {
43542             this.flag.dom.className = 'flag '+n;
43543         },
43544         
43545         getValue : function()
43546         {
43547             var v = this.inputEl().getValue();
43548             if(this.dialCodeHolder) {
43549                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43550             }
43551             return v;
43552         },
43553         
43554         setValue : function(v)
43555         {
43556             var d = this.getDialCode(v);
43557             
43558             //invalid dial code
43559             if(v.length == 0 || !d || d.length == 0) {
43560                 if(this.rendered){
43561                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43562                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43563                 }
43564                 return;
43565             }
43566             
43567             //valid dial code
43568             this.setFlagClass(this.dialCodeMapping[d].iso2);
43569             this.setDialCode(d);
43570             this.inputEl().dom.value = v.replace('+'+d,'');
43571             this.hiddenEl().dom.value = this.getValue();
43572             
43573             this.validate();
43574         },
43575         
43576         getDialCode : function(v)
43577         {
43578             v = v ||  '';
43579             
43580             if (v.length == 0) {
43581                 return this.dialCodeHolder.dom.value;
43582             }
43583             
43584             var dialCode = "";
43585             if (v.charAt(0) != "+") {
43586                 return false;
43587             }
43588             var numericChars = "";
43589             for (var i = 1; i < v.length; i++) {
43590               var c = v.charAt(i);
43591               if (!isNaN(c)) {
43592                 numericChars += c;
43593                 if (this.dialCodeMapping[numericChars]) {
43594                   dialCode = v.substr(1, i);
43595                 }
43596                 if (numericChars.length == 4) {
43597                   break;
43598                 }
43599               }
43600             }
43601             return dialCode;
43602         },
43603         
43604         reset : function()
43605         {
43606             this.setValue(this.defaultDialCode);
43607             this.validate();
43608         },
43609         
43610         hiddenEl : function()
43611         {
43612             return this.el.select('input.hidden-tel-input',true).first();
43613         },
43614         
43615         // after setting val
43616         onKeyUp : function(e){
43617             this.setValue(this.getValue());
43618         },
43619         
43620         onKeyPress : function(e){
43621             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43622                 e.stopEvent();
43623             }
43624         }
43625         
43626 });
43627 /**
43628  * @class Roo.bootstrap.MoneyField
43629  * @extends Roo.bootstrap.ComboBox
43630  * Bootstrap MoneyField class
43631  * 
43632  * @constructor
43633  * Create a new MoneyField.
43634  * @param {Object} config Configuration options
43635  */
43636
43637 Roo.bootstrap.MoneyField = function(config) {
43638     
43639     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43640     
43641 };
43642
43643 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43644     
43645     /**
43646      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43647      */
43648     allowDecimals : true,
43649     /**
43650      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43651      */
43652     decimalSeparator : ".",
43653     /**
43654      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43655      */
43656     decimalPrecision : 0,
43657     /**
43658      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43659      */
43660     allowNegative : true,
43661     /**
43662      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43663      */
43664     allowZero: true,
43665     /**
43666      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43667      */
43668     minValue : Number.NEGATIVE_INFINITY,
43669     /**
43670      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43671      */
43672     maxValue : Number.MAX_VALUE,
43673     /**
43674      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43675      */
43676     minText : "The minimum value for this field is {0}",
43677     /**
43678      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43679      */
43680     maxText : "The maximum value for this field is {0}",
43681     /**
43682      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43683      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43684      */
43685     nanText : "{0} is not a valid number",
43686     /**
43687      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43688      */
43689     castInt : true,
43690     /**
43691      * @cfg {String} defaults currency of the MoneyField
43692      * value should be in lkey
43693      */
43694     defaultCurrency : false,
43695     /**
43696      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43697      */
43698     thousandsDelimiter : false,
43699     /**
43700      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43701      */
43702     max_length: false,
43703     
43704     inputlg : 9,
43705     inputmd : 9,
43706     inputsm : 9,
43707     inputxs : 6,
43708     
43709     store : false,
43710     
43711     getAutoCreate : function()
43712     {
43713         var align = this.labelAlign || this.parentLabelAlign();
43714         
43715         var id = Roo.id();
43716
43717         var cfg = {
43718             cls: 'form-group',
43719             cn: []
43720         };
43721
43722         var input =  {
43723             tag: 'input',
43724             id : id,
43725             cls : 'form-control roo-money-amount-input',
43726             autocomplete: 'new-password'
43727         };
43728         
43729         var hiddenInput = {
43730             tag: 'input',
43731             type: 'hidden',
43732             id: Roo.id(),
43733             cls: 'hidden-number-input'
43734         };
43735         
43736         if(this.max_length) {
43737             input.maxlength = this.max_length; 
43738         }
43739         
43740         if (this.name) {
43741             hiddenInput.name = this.name;
43742         }
43743
43744         if (this.disabled) {
43745             input.disabled = true;
43746         }
43747
43748         var clg = 12 - this.inputlg;
43749         var cmd = 12 - this.inputmd;
43750         var csm = 12 - this.inputsm;
43751         var cxs = 12 - this.inputxs;
43752         
43753         var container = {
43754             tag : 'div',
43755             cls : 'row roo-money-field',
43756             cn : [
43757                 {
43758                     tag : 'div',
43759                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43760                     cn : [
43761                         {
43762                             tag : 'div',
43763                             cls: 'roo-select2-container input-group',
43764                             cn: [
43765                                 {
43766                                     tag : 'input',
43767                                     cls : 'form-control roo-money-currency-input',
43768                                     autocomplete: 'new-password',
43769                                     readOnly : 1,
43770                                     name : this.currencyName
43771                                 },
43772                                 {
43773                                     tag :'span',
43774                                     cls : 'input-group-addon',
43775                                     cn : [
43776                                         {
43777                                             tag: 'span',
43778                                             cls: 'caret'
43779                                         }
43780                                     ]
43781                                 }
43782                             ]
43783                         }
43784                     ]
43785                 },
43786                 {
43787                     tag : 'div',
43788                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43789                     cn : [
43790                         {
43791                             tag: 'div',
43792                             cls: this.hasFeedback ? 'has-feedback' : '',
43793                             cn: [
43794                                 input
43795                             ]
43796                         }
43797                     ]
43798                 }
43799             ]
43800             
43801         };
43802         
43803         if (this.fieldLabel.length) {
43804             var indicator = {
43805                 tag: 'i',
43806                 tooltip: 'This field is required'
43807             };
43808
43809             var label = {
43810                 tag: 'label',
43811                 'for':  id,
43812                 cls: 'control-label',
43813                 cn: []
43814             };
43815
43816             var label_text = {
43817                 tag: 'span',
43818                 html: this.fieldLabel
43819             };
43820
43821             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43822             label.cn = [
43823                 indicator,
43824                 label_text
43825             ];
43826
43827             if(this.indicatorpos == 'right') {
43828                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43829                 label.cn = [
43830                     label_text,
43831                     indicator
43832                 ];
43833             }
43834
43835             if(align == 'left') {
43836                 container = {
43837                     tag: 'div',
43838                     cn: [
43839                         container
43840                     ]
43841                 };
43842
43843                 if(this.labelWidth > 12){
43844                     label.style = "width: " + this.labelWidth + 'px';
43845                 }
43846                 if(this.labelWidth < 13 && this.labelmd == 0){
43847                     this.labelmd = this.labelWidth;
43848                 }
43849                 if(this.labellg > 0){
43850                     label.cls += ' col-lg-' + this.labellg;
43851                     input.cls += ' col-lg-' + (12 - this.labellg);
43852                 }
43853                 if(this.labelmd > 0){
43854                     label.cls += ' col-md-' + this.labelmd;
43855                     container.cls += ' col-md-' + (12 - this.labelmd);
43856                 }
43857                 if(this.labelsm > 0){
43858                     label.cls += ' col-sm-' + this.labelsm;
43859                     container.cls += ' col-sm-' + (12 - this.labelsm);
43860                 }
43861                 if(this.labelxs > 0){
43862                     label.cls += ' col-xs-' + this.labelxs;
43863                     container.cls += ' col-xs-' + (12 - this.labelxs);
43864                 }
43865             }
43866         }
43867
43868         cfg.cn = [
43869             label,
43870             container,
43871             hiddenInput
43872         ];
43873         
43874         var settings = this;
43875
43876         ['xs','sm','md','lg'].map(function(size){
43877             if (settings[size]) {
43878                 cfg.cls += ' col-' + size + '-' + settings[size];
43879             }
43880         });
43881         
43882         return cfg;
43883     },
43884     
43885     initEvents : function()
43886     {
43887         this.indicator = this.indicatorEl();
43888         
43889         this.initCurrencyEvent();
43890         
43891         this.initNumberEvent();
43892     },
43893     
43894     initCurrencyEvent : function()
43895     {
43896         if (!this.store) {
43897             throw "can not find store for combo";
43898         }
43899         
43900         this.store = Roo.factory(this.store, Roo.data);
43901         this.store.parent = this;
43902         
43903         this.createList();
43904         
43905         this.triggerEl = this.el.select('.input-group-addon', true).first();
43906         
43907         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43908         
43909         var _this = this;
43910         
43911         (function(){
43912             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43913             _this.list.setWidth(lw);
43914         }).defer(100);
43915         
43916         this.list.on('mouseover', this.onViewOver, this);
43917         this.list.on('mousemove', this.onViewMove, this);
43918         this.list.on('scroll', this.onViewScroll, this);
43919         
43920         if(!this.tpl){
43921             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43922         }
43923         
43924         this.view = new Roo.View(this.list, this.tpl, {
43925             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43926         });
43927         
43928         this.view.on('click', this.onViewClick, this);
43929         
43930         this.store.on('beforeload', this.onBeforeLoad, this);
43931         this.store.on('load', this.onLoad, this);
43932         this.store.on('loadexception', this.onLoadException, this);
43933         
43934         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43935             "up" : function(e){
43936                 this.inKeyMode = true;
43937                 this.selectPrev();
43938             },
43939
43940             "down" : function(e){
43941                 if(!this.isExpanded()){
43942                     this.onTriggerClick();
43943                 }else{
43944                     this.inKeyMode = true;
43945                     this.selectNext();
43946                 }
43947             },
43948
43949             "enter" : function(e){
43950                 this.collapse();
43951                 
43952                 if(this.fireEvent("specialkey", this, e)){
43953                     this.onViewClick(false);
43954                 }
43955                 
43956                 return true;
43957             },
43958
43959             "esc" : function(e){
43960                 this.collapse();
43961             },
43962
43963             "tab" : function(e){
43964                 this.collapse();
43965                 
43966                 if(this.fireEvent("specialkey", this, e)){
43967                     this.onViewClick(false);
43968                 }
43969                 
43970                 return true;
43971             },
43972
43973             scope : this,
43974
43975             doRelay : function(foo, bar, hname){
43976                 if(hname == 'down' || this.scope.isExpanded()){
43977                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43978                 }
43979                 return true;
43980             },
43981
43982             forceKeyDown: true
43983         });
43984         
43985         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43986         
43987     },
43988     
43989     initNumberEvent : function(e)
43990     {
43991         this.inputEl().on("keydown" , this.fireKey,  this);
43992         this.inputEl().on("focus", this.onFocus,  this);
43993         this.inputEl().on("blur", this.onBlur,  this);
43994         
43995         this.inputEl().relayEvent('keyup', this);
43996         
43997         if(this.indicator){
43998             this.indicator.addClass('invisible');
43999         }
44000  
44001         this.originalValue = this.getValue();
44002         
44003         if(this.validationEvent == 'keyup'){
44004             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
44005             this.inputEl().on('keyup', this.filterValidation, this);
44006         }
44007         else if(this.validationEvent !== false){
44008             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
44009         }
44010         
44011         if(this.selectOnFocus){
44012             this.on("focus", this.preFocus, this);
44013             
44014         }
44015         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
44016             this.inputEl().on("keypress", this.filterKeys, this);
44017         } else {
44018             this.inputEl().relayEvent('keypress', this);
44019         }
44020         
44021         var allowed = "0123456789";
44022         
44023         if(this.allowDecimals){
44024             allowed += this.decimalSeparator;
44025         }
44026         
44027         if(this.allowNegative){
44028             allowed += "-";
44029         }
44030         
44031         if(this.thousandsDelimiter) {
44032             allowed += ",";
44033         }
44034         
44035         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
44036         
44037         var keyPress = function(e){
44038             
44039             var k = e.getKey();
44040             
44041             var c = e.getCharCode();
44042             
44043             if(
44044                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
44045                     allowed.indexOf(String.fromCharCode(c)) === -1
44046             ){
44047                 e.stopEvent();
44048                 return;
44049             }
44050             
44051             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
44052                 return;
44053             }
44054             
44055             if(allowed.indexOf(String.fromCharCode(c)) === -1){
44056                 e.stopEvent();
44057             }
44058         };
44059         
44060         this.inputEl().on("keypress", keyPress, this);
44061         
44062     },
44063     
44064     onTriggerClick : function(e)
44065     {   
44066         if(this.disabled){
44067             return;
44068         }
44069         
44070         this.page = 0;
44071         this.loadNext = false;
44072         
44073         if(this.isExpanded()){
44074             this.collapse();
44075             return;
44076         }
44077         
44078         this.hasFocus = true;
44079         
44080         if(this.triggerAction == 'all') {
44081             this.doQuery(this.allQuery, true);
44082             return;
44083         }
44084         
44085         this.doQuery(this.getRawValue());
44086     },
44087     
44088     getCurrency : function()
44089     {   
44090         var v = this.currencyEl().getValue();
44091         
44092         return v;
44093     },
44094     
44095     restrictHeight : function()
44096     {
44097         this.list.alignTo(this.currencyEl(), this.listAlign);
44098         this.list.alignTo(this.currencyEl(), this.listAlign);
44099     },
44100     
44101     onViewClick : function(view, doFocus, el, e)
44102     {
44103         var index = this.view.getSelectedIndexes()[0];
44104         
44105         var r = this.store.getAt(index);
44106         
44107         if(r){
44108             this.onSelect(r, index);
44109         }
44110     },
44111     
44112     onSelect : function(record, index){
44113         
44114         if(this.fireEvent('beforeselect', this, record, index) !== false){
44115         
44116             this.setFromCurrencyData(index > -1 ? record.data : false);
44117             
44118             this.collapse();
44119             
44120             this.fireEvent('select', this, record, index);
44121         }
44122     },
44123     
44124     setFromCurrencyData : function(o)
44125     {
44126         var currency = '';
44127         
44128         this.lastCurrency = o;
44129         
44130         if (this.currencyField) {
44131             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
44132         } else {
44133             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
44134         }
44135         
44136         this.lastSelectionText = currency;
44137         
44138         //setting default currency
44139         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
44140             this.setCurrency(this.defaultCurrency);
44141             return;
44142         }
44143         
44144         this.setCurrency(currency);
44145     },
44146     
44147     setFromData : function(o)
44148     {
44149         var c = {};
44150         
44151         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
44152         
44153         this.setFromCurrencyData(c);
44154         
44155         var value = '';
44156         
44157         if (this.name) {
44158             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
44159         } else {
44160             Roo.log('no value set for '+ (this.name ? this.name : this.id));
44161         }
44162         
44163         this.setValue(value);
44164         
44165     },
44166     
44167     setCurrency : function(v)
44168     {   
44169         this.currencyValue = v;
44170         
44171         if(this.rendered){
44172             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
44173             this.validate();
44174         }
44175     },
44176     
44177     setValue : function(v)
44178     {
44179         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
44180         
44181         this.value = v;
44182         
44183         if(this.rendered){
44184             
44185             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
44186             
44187             this.inputEl().dom.value = (v == '') ? '' :
44188                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
44189             
44190             if(!this.allowZero && v === '0') {
44191                 this.hiddenEl().dom.value = '';
44192                 this.inputEl().dom.value = '';
44193             }
44194             
44195             this.validate();
44196         }
44197     },
44198     
44199     getRawValue : function()
44200     {
44201         var v = this.inputEl().getValue();
44202         
44203         return v;
44204     },
44205     
44206     getValue : function()
44207     {
44208         return this.fixPrecision(this.parseValue(this.getRawValue()));
44209     },
44210     
44211     parseValue : function(value)
44212     {
44213         if(this.thousandsDelimiter) {
44214             value += "";
44215             r = new RegExp(",", "g");
44216             value = value.replace(r, "");
44217         }
44218         
44219         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
44220         return isNaN(value) ? '' : value;
44221         
44222     },
44223     
44224     fixPrecision : function(value)
44225     {
44226         if(this.thousandsDelimiter) {
44227             value += "";
44228             r = new RegExp(",", "g");
44229             value = value.replace(r, "");
44230         }
44231         
44232         var nan = isNaN(value);
44233         
44234         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
44235             return nan ? '' : value;
44236         }
44237         return parseFloat(value).toFixed(this.decimalPrecision);
44238     },
44239     
44240     decimalPrecisionFcn : function(v)
44241     {
44242         return Math.floor(v);
44243     },
44244     
44245     validateValue : function(value)
44246     {
44247         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
44248             return false;
44249         }
44250         
44251         var num = this.parseValue(value);
44252         
44253         if(isNaN(num)){
44254             this.markInvalid(String.format(this.nanText, value));
44255             return false;
44256         }
44257         
44258         if(num < this.minValue){
44259             this.markInvalid(String.format(this.minText, this.minValue));
44260             return false;
44261         }
44262         
44263         if(num > this.maxValue){
44264             this.markInvalid(String.format(this.maxText, this.maxValue));
44265             return false;
44266         }
44267         
44268         return true;
44269     },
44270     
44271     validate : function()
44272     {
44273         if(this.disabled || this.allowBlank){
44274             this.markValid();
44275             return true;
44276         }
44277         
44278         var currency = this.getCurrency();
44279         
44280         if(this.validateValue(this.getRawValue()) && currency.length){
44281             this.markValid();
44282             return true;
44283         }
44284         
44285         this.markInvalid();
44286         return false;
44287     },
44288     
44289     getName: function()
44290     {
44291         return this.name;
44292     },
44293     
44294     beforeBlur : function()
44295     {
44296         if(!this.castInt){
44297             return;
44298         }
44299         
44300         var v = this.parseValue(this.getRawValue());
44301         
44302         if(v || v == 0){
44303             this.setValue(v);
44304         }
44305     },
44306     
44307     onBlur : function()
44308     {
44309         this.beforeBlur();
44310         
44311         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
44312             //this.el.removeClass(this.focusClass);
44313         }
44314         
44315         this.hasFocus = false;
44316         
44317         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
44318             this.validate();
44319         }
44320         
44321         var v = this.getValue();
44322         
44323         if(String(v) !== String(this.startValue)){
44324             this.fireEvent('change', this, v, this.startValue);
44325         }
44326         
44327         this.fireEvent("blur", this);
44328     },
44329     
44330     inputEl : function()
44331     {
44332         return this.el.select('.roo-money-amount-input', true).first();
44333     },
44334     
44335     currencyEl : function()
44336     {
44337         return this.el.select('.roo-money-currency-input', true).first();
44338     },
44339     
44340     hiddenEl : function()
44341     {
44342         return this.el.select('input.hidden-number-input',true).first();
44343     }
44344     
44345 });/**
44346  * @class Roo.bootstrap.BezierSignature
44347  * @extends Roo.bootstrap.Component
44348  * Bootstrap BezierSignature class
44349  * This script refer to:
44350  *    Title: Signature Pad
44351  *    Author: szimek
44352  *    Availability: https://github.com/szimek/signature_pad
44353  *
44354  * @constructor
44355  * Create a new BezierSignature
44356  * @param {Object} config The config object
44357  */
44358
44359 Roo.bootstrap.BezierSignature = function(config){
44360     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
44361     this.addEvents({
44362         "resize" : true
44363     });
44364 };
44365
44366 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44367 {
44368      
44369     curve_data: [],
44370     
44371     is_empty: true,
44372     
44373     mouse_btn_down: true,
44374     
44375     /**
44376      * @cfg {int} canvas height
44377      */
44378     canvas_height: '200px',
44379     
44380     /**
44381      * @cfg {float|function} Radius of a single dot.
44382      */ 
44383     dot_size: false,
44384     
44385     /**
44386      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44387      */
44388     min_width: 0.5,
44389     
44390     /**
44391      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44392      */
44393     max_width: 2.5,
44394     
44395     /**
44396      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44397      */
44398     throttle: 16,
44399     
44400     /**
44401      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44402      */
44403     min_distance: 5,
44404     
44405     /**
44406      * @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.
44407      */
44408     bg_color: 'rgba(0, 0, 0, 0)',
44409     
44410     /**
44411      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44412      */
44413     dot_color: 'black',
44414     
44415     /**
44416      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44417      */ 
44418     velocity_filter_weight: 0.7,
44419     
44420     /**
44421      * @cfg {function} Callback when stroke begin. 
44422      */
44423     onBegin: false,
44424     
44425     /**
44426      * @cfg {function} Callback when stroke end.
44427      */
44428     onEnd: false,
44429     
44430     getAutoCreate : function()
44431     {
44432         var cls = 'roo-signature column';
44433         
44434         if(this.cls){
44435             cls += ' ' + this.cls;
44436         }
44437         
44438         var col_sizes = [
44439             'lg',
44440             'md',
44441             'sm',
44442             'xs'
44443         ];
44444         
44445         for(var i = 0; i < col_sizes.length; i++) {
44446             if(this[col_sizes[i]]) {
44447                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44448             }
44449         }
44450         
44451         var cfg = {
44452             tag: 'div',
44453             cls: cls,
44454             cn: [
44455                 {
44456                     tag: 'div',
44457                     cls: 'roo-signature-body',
44458                     cn: [
44459                         {
44460                             tag: 'canvas',
44461                             cls: 'roo-signature-body-canvas',
44462                             height: this.canvas_height,
44463                             width: this.canvas_width
44464                         }
44465                     ]
44466                 },
44467                 {
44468                     tag: 'input',
44469                     type: 'file',
44470                     style: 'display: none'
44471                 }
44472             ]
44473         };
44474         
44475         return cfg;
44476     },
44477     
44478     initEvents: function() 
44479     {
44480         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44481         
44482         var canvas = this.canvasEl();
44483         
44484         // mouse && touch event swapping...
44485         canvas.dom.style.touchAction = 'none';
44486         canvas.dom.style.msTouchAction = 'none';
44487         
44488         this.mouse_btn_down = false;
44489         canvas.on('mousedown', this._handleMouseDown, this);
44490         canvas.on('mousemove', this._handleMouseMove, this);
44491         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44492         
44493         if (window.PointerEvent) {
44494             canvas.on('pointerdown', this._handleMouseDown, this);
44495             canvas.on('pointermove', this._handleMouseMove, this);
44496             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44497         }
44498         
44499         if ('ontouchstart' in window) {
44500             canvas.on('touchstart', this._handleTouchStart, this);
44501             canvas.on('touchmove', this._handleTouchMove, this);
44502             canvas.on('touchend', this._handleTouchEnd, this);
44503         }
44504         
44505         Roo.EventManager.onWindowResize(this.resize, this, true);
44506         
44507         // file input event
44508         this.fileEl().on('change', this.uploadImage, this);
44509         
44510         this.clear();
44511         
44512         this.resize();
44513     },
44514     
44515     resize: function(){
44516         
44517         var canvas = this.canvasEl().dom;
44518         var ctx = this.canvasElCtx();
44519         var img_data = false;
44520         
44521         if(canvas.width > 0) {
44522             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44523         }
44524         // setting canvas width will clean img data
44525         canvas.width = 0;
44526         
44527         var style = window.getComputedStyle ? 
44528             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44529             
44530         var padding_left = parseInt(style.paddingLeft) || 0;
44531         var padding_right = parseInt(style.paddingRight) || 0;
44532         
44533         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44534         
44535         if(img_data) {
44536             ctx.putImageData(img_data, 0, 0);
44537         }
44538     },
44539     
44540     _handleMouseDown: function(e)
44541     {
44542         if (e.browserEvent.which === 1) {
44543             this.mouse_btn_down = true;
44544             this.strokeBegin(e);
44545         }
44546     },
44547     
44548     _handleMouseMove: function (e)
44549     {
44550         if (this.mouse_btn_down) {
44551             this.strokeMoveUpdate(e);
44552         }
44553     },
44554     
44555     _handleMouseUp: function (e)
44556     {
44557         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44558             this.mouse_btn_down = false;
44559             this.strokeEnd(e);
44560         }
44561     },
44562     
44563     _handleTouchStart: function (e) {
44564         
44565         e.preventDefault();
44566         if (e.browserEvent.targetTouches.length === 1) {
44567             // var touch = e.browserEvent.changedTouches[0];
44568             // this.strokeBegin(touch);
44569             
44570              this.strokeBegin(e); // assume e catching the correct xy...
44571         }
44572     },
44573     
44574     _handleTouchMove: function (e) {
44575         e.preventDefault();
44576         // var touch = event.targetTouches[0];
44577         // _this._strokeMoveUpdate(touch);
44578         this.strokeMoveUpdate(e);
44579     },
44580     
44581     _handleTouchEnd: function (e) {
44582         var wasCanvasTouched = e.target === this.canvasEl().dom;
44583         if (wasCanvasTouched) {
44584             e.preventDefault();
44585             // var touch = event.changedTouches[0];
44586             // _this._strokeEnd(touch);
44587             this.strokeEnd(e);
44588         }
44589     },
44590     
44591     reset: function () {
44592         this._lastPoints = [];
44593         this._lastVelocity = 0;
44594         this._lastWidth = (this.min_width + this.max_width) / 2;
44595         this.canvasElCtx().fillStyle = this.dot_color;
44596     },
44597     
44598     strokeMoveUpdate: function(e)
44599     {
44600         this.strokeUpdate(e);
44601         
44602         if (this.throttle) {
44603             this.throttleStroke(this.strokeUpdate, this.throttle);
44604         }
44605         else {
44606             this.strokeUpdate(e);
44607         }
44608     },
44609     
44610     strokeBegin: function(e)
44611     {
44612         var newPointGroup = {
44613             color: this.dot_color,
44614             points: []
44615         };
44616         
44617         if (typeof this.onBegin === 'function') {
44618             this.onBegin(e);
44619         }
44620         
44621         this.curve_data.push(newPointGroup);
44622         this.reset();
44623         this.strokeUpdate(e);
44624     },
44625     
44626     strokeUpdate: function(e)
44627     {
44628         var rect = this.canvasEl().dom.getBoundingClientRect();
44629         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44630         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44631         var lastPoints = lastPointGroup.points;
44632         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44633         var isLastPointTooClose = lastPoint
44634             ? point.distanceTo(lastPoint) <= this.min_distance
44635             : false;
44636         var color = lastPointGroup.color;
44637         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44638             var curve = this.addPoint(point);
44639             if (!lastPoint) {
44640                 this.drawDot({color: color, point: point});
44641             }
44642             else if (curve) {
44643                 this.drawCurve({color: color, curve: curve});
44644             }
44645             lastPoints.push({
44646                 time: point.time,
44647                 x: point.x,
44648                 y: point.y
44649             });
44650         }
44651     },
44652     
44653     strokeEnd: function(e)
44654     {
44655         this.strokeUpdate(e);
44656         if (typeof this.onEnd === 'function') {
44657             this.onEnd(e);
44658         }
44659     },
44660     
44661     addPoint:  function (point) {
44662         var _lastPoints = this._lastPoints;
44663         _lastPoints.push(point);
44664         if (_lastPoints.length > 2) {
44665             if (_lastPoints.length === 3) {
44666                 _lastPoints.unshift(_lastPoints[0]);
44667             }
44668             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44669             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44670             _lastPoints.shift();
44671             return curve;
44672         }
44673         return null;
44674     },
44675     
44676     calculateCurveWidths: function (startPoint, endPoint) {
44677         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44678             (1 - this.velocity_filter_weight) * this._lastVelocity;
44679
44680         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44681         var widths = {
44682             end: newWidth,
44683             start: this._lastWidth
44684         };
44685         
44686         this._lastVelocity = velocity;
44687         this._lastWidth = newWidth;
44688         return widths;
44689     },
44690     
44691     drawDot: function (_a) {
44692         var color = _a.color, point = _a.point;
44693         var ctx = this.canvasElCtx();
44694         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44695         ctx.beginPath();
44696         this.drawCurveSegment(point.x, point.y, width);
44697         ctx.closePath();
44698         ctx.fillStyle = color;
44699         ctx.fill();
44700     },
44701     
44702     drawCurve: function (_a) {
44703         var color = _a.color, curve = _a.curve;
44704         var ctx = this.canvasElCtx();
44705         var widthDelta = curve.endWidth - curve.startWidth;
44706         var drawSteps = Math.floor(curve.length()) * 2;
44707         ctx.beginPath();
44708         ctx.fillStyle = color;
44709         for (var i = 0; i < drawSteps; i += 1) {
44710         var t = i / drawSteps;
44711         var tt = t * t;
44712         var ttt = tt * t;
44713         var u = 1 - t;
44714         var uu = u * u;
44715         var uuu = uu * u;
44716         var x = uuu * curve.startPoint.x;
44717         x += 3 * uu * t * curve.control1.x;
44718         x += 3 * u * tt * curve.control2.x;
44719         x += ttt * curve.endPoint.x;
44720         var y = uuu * curve.startPoint.y;
44721         y += 3 * uu * t * curve.control1.y;
44722         y += 3 * u * tt * curve.control2.y;
44723         y += ttt * curve.endPoint.y;
44724         var width = curve.startWidth + ttt * widthDelta;
44725         this.drawCurveSegment(x, y, width);
44726         }
44727         ctx.closePath();
44728         ctx.fill();
44729     },
44730     
44731     drawCurveSegment: function (x, y, width) {
44732         var ctx = this.canvasElCtx();
44733         ctx.moveTo(x, y);
44734         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44735         this.is_empty = false;
44736     },
44737     
44738     clear: function()
44739     {
44740         var ctx = this.canvasElCtx();
44741         var canvas = this.canvasEl().dom;
44742         ctx.fillStyle = this.bg_color;
44743         ctx.clearRect(0, 0, canvas.width, canvas.height);
44744         ctx.fillRect(0, 0, canvas.width, canvas.height);
44745         this.curve_data = [];
44746         this.reset();
44747         this.is_empty = true;
44748     },
44749     
44750     fileEl: function()
44751     {
44752         return  this.el.select('input',true).first();
44753     },
44754     
44755     canvasEl: function()
44756     {
44757         return this.el.select('canvas',true).first();
44758     },
44759     
44760     canvasElCtx: function()
44761     {
44762         return this.el.select('canvas',true).first().dom.getContext('2d');
44763     },
44764     
44765     getImage: function(type)
44766     {
44767         if(this.is_empty) {
44768             return false;
44769         }
44770         
44771         // encryption ?
44772         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44773     },
44774     
44775     drawFromImage: function(img_src)
44776     {
44777         var img = new Image();
44778         
44779         img.onload = function(){
44780             this.canvasElCtx().drawImage(img, 0, 0);
44781         }.bind(this);
44782         
44783         img.src = img_src;
44784         
44785         this.is_empty = false;
44786     },
44787     
44788     selectImage: function()
44789     {
44790         this.fileEl().dom.click();
44791     },
44792     
44793     uploadImage: function(e)
44794     {
44795         var reader = new FileReader();
44796         
44797         reader.onload = function(e){
44798             var img = new Image();
44799             img.onload = function(){
44800                 this.reset();
44801                 this.canvasElCtx().drawImage(img, 0, 0);
44802             }.bind(this);
44803             img.src = e.target.result;
44804         }.bind(this);
44805         
44806         reader.readAsDataURL(e.target.files[0]);
44807     },
44808     
44809     // Bezier Point Constructor
44810     Point: (function () {
44811         function Point(x, y, time) {
44812             this.x = x;
44813             this.y = y;
44814             this.time = time || Date.now();
44815         }
44816         Point.prototype.distanceTo = function (start) {
44817             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44818         };
44819         Point.prototype.equals = function (other) {
44820             return this.x === other.x && this.y === other.y && this.time === other.time;
44821         };
44822         Point.prototype.velocityFrom = function (start) {
44823             return this.time !== start.time
44824             ? this.distanceTo(start) / (this.time - start.time)
44825             : 0;
44826         };
44827         return Point;
44828     }()),
44829     
44830     
44831     // Bezier Constructor
44832     Bezier: (function () {
44833         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44834             this.startPoint = startPoint;
44835             this.control2 = control2;
44836             this.control1 = control1;
44837             this.endPoint = endPoint;
44838             this.startWidth = startWidth;
44839             this.endWidth = endWidth;
44840         }
44841         Bezier.fromPoints = function (points, widths, scope) {
44842             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44843             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44844             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44845         };
44846         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44847             var dx1 = s1.x - s2.x;
44848             var dy1 = s1.y - s2.y;
44849             var dx2 = s2.x - s3.x;
44850             var dy2 = s2.y - s3.y;
44851             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44852             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44853             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44854             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44855             var dxm = m1.x - m2.x;
44856             var dym = m1.y - m2.y;
44857             var k = l2 / (l1 + l2);
44858             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44859             var tx = s2.x - cm.x;
44860             var ty = s2.y - cm.y;
44861             return {
44862                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44863                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44864             };
44865         };
44866         Bezier.prototype.length = function () {
44867             var steps = 10;
44868             var length = 0;
44869             var px;
44870             var py;
44871             for (var i = 0; i <= steps; i += 1) {
44872                 var t = i / steps;
44873                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44874                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44875                 if (i > 0) {
44876                     var xdiff = cx - px;
44877                     var ydiff = cy - py;
44878                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44879                 }
44880                 px = cx;
44881                 py = cy;
44882             }
44883             return length;
44884         };
44885         Bezier.prototype.point = function (t, start, c1, c2, end) {
44886             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44887             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44888             + (3.0 * c2 * (1.0 - t) * t * t)
44889             + (end * t * t * t);
44890         };
44891         return Bezier;
44892     }()),
44893     
44894     throttleStroke: function(fn, wait) {
44895       if (wait === void 0) { wait = 250; }
44896       var previous = 0;
44897       var timeout = null;
44898       var result;
44899       var storedContext;
44900       var storedArgs;
44901       var later = function () {
44902           previous = Date.now();
44903           timeout = null;
44904           result = fn.apply(storedContext, storedArgs);
44905           if (!timeout) {
44906               storedContext = null;
44907               storedArgs = [];
44908           }
44909       };
44910       return function wrapper() {
44911           var args = [];
44912           for (var _i = 0; _i < arguments.length; _i++) {
44913               args[_i] = arguments[_i];
44914           }
44915           var now = Date.now();
44916           var remaining = wait - (now - previous);
44917           storedContext = this;
44918           storedArgs = args;
44919           if (remaining <= 0 || remaining > wait) {
44920               if (timeout) {
44921                   clearTimeout(timeout);
44922                   timeout = null;
44923               }
44924               previous = now;
44925               result = fn.apply(storedContext, storedArgs);
44926               if (!timeout) {
44927                   storedContext = null;
44928                   storedArgs = [];
44929               }
44930           }
44931           else if (!timeout) {
44932               timeout = window.setTimeout(later, remaining);
44933           }
44934           return result;
44935       };
44936   }
44937   
44938 });
44939
44940  
44941
44942